From 7e3bd95c33efa4f2c5f1abb598ff958a14585b2b Mon Sep 17 00:00:00 2001 From: chenweize1998 Date: Wed, 24 May 2023 17:52:54 +0800 Subject: [PATCH 1/2] implement npc path finding --- ui/dist/assets/may.png | Bin 0 -> 62961 bytes ui/dist/assets/tilemaps/json/town.json | 72 ++++++++++++------------- ui/src/classes/npc.ts | 37 ++++++++++++- ui/src/index.ts | 6 +++ ui/src/scenes/town/town.ts | 67 +++++++++++++++++++---- 5 files changed, 136 insertions(+), 46 deletions(-) create mode 100644 ui/dist/assets/may.png diff --git a/ui/dist/assets/may.png b/ui/dist/assets/may.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8bc1b7c267d22a75c807a4973110b6ea7da3e8 GIT binary patch literal 62961 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 JEKo2B{4f8C8~OkM literal 0 HcmV?d00001 diff --git a/ui/dist/assets/tilemaps/json/town.json b/ui/dist/assets/tilemaps/json/town.json index 714476b5c..75bcc7282 100644 --- a/ui/dist/assets/tilemaps/json/town.json +++ b/ui/dist/assets/tilemaps/json/town.json @@ -103,7 +103,7 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 5, 6, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 15, 16, 13, 14, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 5, 6, 5, 6, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 13, 14, 13, 14, 13, 14, @@ -252,8 +252,8 @@ "type":"npc", "visible":true, "width":0, - "x":215.333333333333, - "y":251.333333333333 + "x":223.75, + "y":272 }, { "height":0, @@ -270,8 +270,8 @@ "type":"npc", "visible":true, "width":0, - "x":422.666666666667, - "y":300 + "x":416, + "y":304 }, { "height":0, @@ -288,8 +288,8 @@ "type":"npc", "visible":true, "width":0, - "x":343.333333333333, - "y":426.666666666667 + "x":344.060272727273, + "y":426 }, { "height":0, @@ -306,7 +306,7 @@ "type":"npc", "visible":true, "width":0, - "x":151.333333333333, + "x":144, "y":154 }, { @@ -324,8 +324,8 @@ "type":"npc", "visible":true, "width":0, - "x":407.333333333333, - "y":138.666666666667 + "x":399.818181818182, + "y":154 }, { "height":0, @@ -342,8 +342,8 @@ "type":"npc", "visible":true, "width":0, - "x":455.333333333333, - "y":155.333333333333 + "x":448, + "y":154 }], "opacity":1, "type":"objectgroup", @@ -3551,7 +3551,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3560,7 +3560,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3569,7 +3569,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3578,7 +3578,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3587,7 +3587,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3596,7 +3596,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3912,7 +3912,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3921,7 +3921,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3930,7 +3930,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3939,7 +3939,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3948,7 +3948,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3966,7 +3966,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -3975,7 +3975,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12184,7 +12184,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12193,7 +12193,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12202,7 +12202,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12256,7 +12256,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12265,7 +12265,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12274,7 +12274,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12328,7 +12328,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12337,7 +12337,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12400,7 +12400,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12409,7 +12409,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { @@ -12418,7 +12418,7 @@ { "name":"collides", "type":"bool", - "value":false + "value":true }] }, { diff --git a/ui/src/classes/npc.ts b/ui/src/classes/npc.ts index 05aec1a8c..fea9e2a9a 100644 --- a/ui/src/classes/npc.ts +++ b/ui/src/classes/npc.ts @@ -1,10 +1,21 @@ import { Actor } from "./actor"; import { DIRECTION } from "../utils"; +import { + MoveTo, + PathFinder, + Board, +} from "../phaser3-rex-plugins/plugins/board-components"; export class NPC extends Actor { + private moveTo: MoveTo; + private board: Board; + private path: PathFinder.NodeType[] = []; + public id: number; public direction: number = DIRECTION.DOWN; + constructor( scene: Phaser.Scene, + board: Board, x: number, y: number, name: string, @@ -13,16 +24,33 @@ export class NPC extends Actor { super(scene, x, y, name); this.setName(name); + this.board = board; this.id = id; // PHYSICS this.getBody().setSize(14, 10); this.getBody().setOffset(0, 0); this.getBody().setImmovable(true); + // this.setOrigin(0, 1); this.initAnimations(); + this.moveTo = this.scene.rexBoard.add.moveTo(this, { + speed: 110, + }); } update(): void { + if (this.path.length > 0 && !this.moveTo.isRunning) { + var tileXY = this.board.worldXYToTileXY(this.x, this.y); + if (tileXY.x == this.path[0].x) { + if (tileXY.y < this.path[0].y) this.changeDirection(DIRECTION.DOWN); + else if (tileXY.y > this.path[0].y) this.changeDirection(DIRECTION.UP); + } else if (tileXY.y == this.path[0].y) { + if (tileXY.x < this.path[0].x) this.changeDirection(DIRECTION.RIGHT); + else if (tileXY.x > this.path[0].x) + this.changeDirection(DIRECTION.LEFT); + } + this.moveTo.moveTo(this.path.shift()); + } var text = ""; switch (this.direction) { case DIRECTION.UP: @@ -39,11 +67,18 @@ export class NPC extends Actor { break; } this.anims.play(this.name + "-walk-" + text, true); - if (this.anims.isPlaying) + if (this.anims.isPlaying && !this.moveTo.isRunning) this.anims.setCurrentFrame(this.anims.currentAnim!.frames[0]); } public changeDirection(direction: number): void { this.direction = direction; } + + public moveAlongPath(path: PathFinder.NodeType[]): void { + if (path.length == 0) return; + if (this.moveTo.isRunning) return; + if (this.path.length > 0) return; + this.path = path; + } } diff --git a/ui/src/index.ts b/ui/src/index.ts index 957e46157..abae0ffb0 100644 --- a/ui/src/index.ts +++ b/ui/src/index.ts @@ -2,6 +2,7 @@ import { Game, Scale, Types, WEBGL } from "phaser"; import { TownScene, LoadingScene } from "./scenes"; import UIPlugin from "./phaser3-rex-plugins/templates/ui/ui-plugin"; +import BoardPlugin from "./phaser3-rex-plugins/plugins/board-plugin"; declare global { interface Window { @@ -51,6 +52,11 @@ export const gameConfig: Types.Core.GameConfig = { plugin: UIPlugin, mapping: "rexUI", }, + { + key: "rexBoard", + plugin: BoardPlugin, + mapping: "rexBoard", + }, ], }, }; diff --git a/ui/src/scenes/town/town.ts b/ui/src/scenes/town/town.ts index 6d609ccf2..cf59895ae 100644 --- a/ui/src/scenes/town/town.ts +++ b/ui/src/scenes/town/town.ts @@ -15,6 +15,9 @@ import { Click, } from "../../phaser3-rex-plugins/templates/ui/ui-components"; import UIPlugin from "../../phaser3-rex-plugins/templates/ui/ui-plugin"; +import BoardPlugin from "../../phaser3-rex-plugins/plugins/board-plugin"; +import { PathFinder } from "../../phaser3-rex-plugins/plugins/board-components"; +import { TileXYType } from "../../phaser3-rex-plugins/plugins/board/types/Position"; const COLOR_PRIMARY = 0x4e342e; const COLOR_LIGHT = 0x7b5e57; @@ -32,14 +35,25 @@ export class TownScene extends Scene { private player: Player; private npcGroup: GameObjects.Group; private keySpace: Phaser.Input.Keyboard.Key; + private keyEnter: Phaser.Input.Keyboard.Key; private rexUI: UIPlugin; + private rexBoard: BoardPlugin; + private board: BoardPlugin.Board; + private pathFinder: PathFinder; constructor() { super("town-scene"); } create(): void { - // Background + this.keySpace = this.input.keyboard!.addKey("SPACE"); + this.keyEnter = this.input.keyboard!.addKey("ENTER"); + this.initMap(); + this.initSprite(); + this.initCamera(); + } + + initMap(): void { this.map = this.make.tilemap({ key: "town", tileWidth: 16, @@ -55,21 +69,42 @@ export class TownScene extends Scene { this.wallLayer.setCollisionByProperty({ collides: true }); this.treeLayer.setCollisionByProperty({ collides: true }); this.houseLayer.setCollisionByProperty({ collides: true }); + this.board = this.rexBoard.createBoardFromTilemap(this.map); + this.board.getAllChess().forEach((chess) => { + var collide = ["wall", "tree", "house"].includes(chess.layer.name); + if (collide && chess.index != -1) { + chess.rexChess.setBlocker(); + } + }); + this.pathFinder = this.rexBoard.add.pathFinder({ + occupiedTest: true, + blockerTest: true, + pathMode: "straight", + cacheCost: true, + }); + } - this.keySpace = this.input.keyboard!.addKey("SPACE"); - + initSprite(): void { // NPC this.npcGroup = this.add.group(); var npcPoints = this.map.filterObjects("npcs", (npc) => { return npc.type === "npc"; }); - var npcs = npcPoints.map((npc) => { - var id_property = npc.properties.filter((property) => { + var npcs = npcPoints.map((npcPoint) => { + var id_property = npcPoint.properties.filter((property) => { return property.name === "id"; }); - this.npcGroup.add( - new NPC(this, npc.x, npc.y, npc.name, id_property[0].value) + var tileXY = this.board.worldXYToTileXY(npcPoint.x, npcPoint.y); + var npc = new NPC( + this, + this.board, + npcPoint.x, + npcPoint.y, + npcPoint.name, + id_property[0].value ); + this.board.addChess(npc, tileXY.x, tileXY.y, 0, true); + this.npcGroup.add(npc); }); this.physics.add.collider(this.npcGroup, this.wallLayer); this.physics.add.collider(this.npcGroup, this.treeLayer); @@ -77,6 +112,8 @@ export class TownScene extends Scene { // Player this.player = new Player(this, 256, 250); + var tileXY = this.board.worldXYToTileXY(this.player.x, this.player.y); + // this.board.addChess(this.player, tileXY.x, tileXY.y, 0, true); this.physics.add.collider(this.player, this.wallLayer); this.physics.add.collider(this.player, this.treeLayer); this.physics.add.collider(this.player, this.houseLayer); @@ -90,6 +127,18 @@ export class TownScene extends Scene { this.createInputBox(npc); } }); + var scene = this; + this.keyEnter.on("up", () => { + var npc = this.npcGroup.getChildren()[0] as NPC; + var npc_chess = this.board.worldXYToChess(npc.x, npc.y); + this.pathFinder.setChess(npc_chess); + var tmp = this.board.chessToTileXYZ(npc_chess); + var path = this.pathFinder.findPath({ + x: tmp.x + 3, + y: tmp.y - 6, + } as TileXYType); + npc.moveAlongPath(path); + }); this.physics.world.setBounds( 0, @@ -97,8 +146,9 @@ export class TownScene extends Scene { this.groundLayer.width + this.player.width, this.groundLayer.height ); + } - // Camera; + initCamera(): void { this.cameras.main.setSize(this.game.scale.width, this.game.scale.height); this.cameras.main.setBounds( 0, @@ -236,7 +286,6 @@ export class TownScene extends Scene { }, loop: true, }); - debugger; fetch("http://127.0.0.1:10002/chat", { method: "POST", headers: { From e375b9dd385311fd09d8120155c6b4e2a84ee94e Mon Sep 17 00:00:00 2001 From: chenweize1998 Date: Mon, 29 May 2023 14:47:29 +0800 Subject: [PATCH 2/2] npcs move themselves --- agentverse/agentverse.py | 4 + agentverse/environments/pokemon.py | 130 ++++++++- .../environments/rules/describer/pokemon.py | 41 ++- .../environments/rules/selector/__init__.py | 1 + .../environments/rules/selector/pokemon.py | 98 +++++++ .../environments/rules/updater/__init__.py | 1 + .../environments/rules/updater/pokemon.py | 42 +++ .../environments/rules/visibility/__init__.py | 1 + .../environments/rules/visibility/pokemon.py | 25 ++ agentverse/tasks/pokemon/config.yaml | 73 ++++- agentverse/tasks/pokemon/output_parser.py | 19 +- pokemon_server.py | 42 ++- test_pokemon_env.py | 4 + ui/dist/assets/config/npcs.yaml | 50 ---- ui/dist/assets/message/message.png | Bin 41634 -> 0 bytes ui/dist/assets/tilemaps/json/town.json | 92 +++++- ui/src/classes/npc.ts | 176 +++++++++++- ui/src/classes/player.ts | 5 +- ui/src/constants.ts | 3 + ui/src/scenes/town/town.ts | 264 +++++++++++++++--- ui/src/utils.ts | 20 ++ ui/tsconfig.json | 3 +- 22 files changed, 945 insertions(+), 149 deletions(-) create mode 100644 agentverse/environments/rules/selector/pokemon.py create mode 100644 agentverse/environments/rules/updater/pokemon.py create mode 100644 agentverse/environments/rules/visibility/pokemon.py create mode 100644 test_pokemon_env.py delete mode 100644 ui/dist/assets/config/npcs.yaml delete mode 100644 ui/dist/assets/message/message.png create mode 100644 ui/src/constants.ts diff --git a/agentverse/agentverse.py b/agentverse/agentverse.py index 6307314dc..a716b810c 100644 --- a/agentverse/agentverse.py +++ b/agentverse/agentverse.py @@ -59,3 +59,7 @@ def next(self, *args, **kwargs): """Run the environment for one step and return the return message.""" return_message = asyncio.run(self.environment.step(*args, **kwargs)) return return_message + + def update_state(self, *args, **kwargs): + """Run the environment for one step and return the return message.""" + self.environment.update_state(*args, **kwargs) diff --git a/agentverse/environments/pokemon.py b/agentverse/environments/pokemon.py index 6e9af5fd5..b4bc42bf5 100644 --- a/agentverse/environments/pokemon.py +++ b/agentverse/environments/pokemon.py @@ -1,7 +1,7 @@ import asyncio -import time +import datetime import logging -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Set # from agentverse.agents.agent import Agent from agentverse.agents.conversation_agent import BaseAgent @@ -19,6 +19,7 @@ class PokemonEnvironment(BaseEnvironment): Args: agents: List of agents + locations: A dict of locations to agents within them rule: Rule for the environment max_turns: Maximum number of turns cnt_turn: Current turn number @@ -27,13 +28,16 @@ class PokemonEnvironment(BaseEnvironment): """ agents: List[BaseAgent] + locations_to_agents: Dict[str, Set[str]] + # locations_descriptions: Dict[str, str] + time: datetime.datetime = datetime.datetime(2021, 1, 1, 8, 0, 0) rule: Rule max_turns: int = 10 cnt_turn: int = 0 last_messages: List[Message] = [] rule_params: Dict = {} - def __init__(self, rule, **kwargs): + def __init__(self, rule, locations, **kwargs): rule_config = rule order_config = rule_config.get("order", {"type": "sequential"}) visibility_config = rule_config.get("visibility", {"type": "all"}) @@ -47,16 +51,70 @@ def __init__(self, rule, **kwargs): updater_config, describer_config, ) - super().__init__(rule=rule, **kwargs) + locations_to_agents = {} + # locations_descriptions = {} + locations_config = locations + for loc in locations_config: + locations_to_agents[loc["name"]] = set(loc["init_agents"]) + # locations_descriptions[loc["name"]] = loc["description"] + super().__init__( + rule=rule, + locations_to_agents=locations_to_agents, + # locations_descriptions=locations_descriptions, + **kwargs, + ) async def step( - self, player_content: str, receiver: str, receiver_id: Optional[int] = None + self, + is_player: bool = False, + player_content: str = None, + receiver: str = None, + receiver_id: Optional[int] = None, + agent_ids: Optional[List[int]] = None, ) -> List[Message]: """Run one step of the environment""" # Get the next agent index # time.sleep(8) # return [Message(content="Test", sender="May", receiver=["May"])] + if is_player: + return await self._respond_to_player(player_content, receiver, receiver_id) + else: + return await self._routine_step(agent_ids) + + async def _routine_step(self, agent_ids) -> List[Message]: + self.rule.update_visible_agents(self) + + # agent_ids = self.rule.get_next_agent_idx(self) + + # Generate current environment description + env_descriptions = self.rule.get_env_description(self) + + # Generate the next message + messages = await asyncio.gather( + *[self.agents[i].astep(env_descriptions[i]) for i in agent_ids] + ) + # messages = self.get_test_messages() + + # Some rules will select certain messages from all the messages + selected_messages = self.rule.select_message(self, messages) + + # Update the memory of the agents + self.last_messages = selected_messages + self.rule.update_memory(self) + self.print_messages(selected_messages) + + self.cnt_turn += 1 + self.time += datetime.timedelta(minutes=5) + + return selected_messages + + async def _respond_to_player( + self, + player_content: str = None, + receiver: str = None, + receiver_id: Optional[int] = None, + ) -> List[Message]: if receiver_id is None: for agent in self.agents: if agent.name == receiver: @@ -80,16 +138,29 @@ async def step( ) # Some rules will select certain messages from all the messages - selected_messages = self.rule.select_message(self, messages) + # selected_messages = self.rule.select_message(self, messages) # Update the memory of the agents - self.last_messages = [player_message, *selected_messages] + self.last_messages = [player_message, *messages] self.rule.update_memory(self) - self.print_messages(selected_messages) + self.print_messages(messages) self.cnt_turn += 1 - return selected_messages + return messages + + def update_state(self, agent_location: Dict[str, str]): + for agent_name, location in agent_location.items(): + # original_location = self.get_agent_to_location()[agent_name] + # self.locations_to_agents[original_location].remove(agent_name) + self.locations_to_agents[location].add(agent_name) + + def get_agent_to_location(self) -> Dict[str, str]: + ret = {} + for location, agent_names in self.locations_to_agents.items(): + for agent in agent_names: + ret[agent] = location + return ret def print_messages(self, messages: List[Message]) -> None: for message in messages: @@ -106,3 +177,44 @@ def reset(self) -> None: def is_done(self) -> bool: """Check if the environment is done""" return self.cnt_turn >= self.max_turns + + def get_test_messages(self) -> List[Message]: + messages = [ + Message( + content='{"to": "Birch", "action": "Speak", "text": "Hi!!!"}', + sender="May", + receiver={"May", "Birch"}, + tool_response=[], + ), + Message( + content='{"to": "May", "text": "Good morning, May! How is your research going?", "action": "Speak"}', + sender="Birch", + receiver={"May", "Birch"}, + tool_response=[], + ), + Message( + content='{"to": "Pokémon Center", "action": "MoveTo"}', + sender="Steven", + receiver={"Steven"}, + tool_response=[], + ), + Message( + content='{"to": "Shop", "last_time": "10 minutes", "action": "MoveTo"}', + sender="Maxie", + receiver={"Maxie"}, + tool_response=[], + ), + Message( + content='{"to": "Pok\\u00e9mon Center", "action": "MoveTo"}', + sender="Archie", + receiver={"Archie"}, + tool_response=[], + ), + Message( + content='{"to": "Shop", "action": "MoveTo"}', + sender="Joseph", + receiver={"Joseph"}, + tool_response=[], + ), + ] + return messages diff --git a/agentverse/environments/rules/describer/pokemon.py b/agentverse/environments/rules/describer/pokemon.py index abbfbfbd3..44d5dbecb 100644 --- a/agentverse/environments/rules/describer/pokemon.py +++ b/agentverse/environments/rules/describer/pokemon.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, List, Optional +from typing import TYPE_CHECKING, Any, List, Optional, Dict +from copy import deepcopy from . import describer_registry as DescriberRegistry from .base import BaseDescriber @@ -14,11 +15,37 @@ class PokemonDescriber(BaseDescriber): def get_env_description( self, environment: PokemonEnvironment, - player_content: str, - time: Optional[str] = None, + player_content: str = "", ) -> List[str]: - description = "" - if time is not None: + time = environment.time + if player_content == "": + agent_to_location = environment.get_agent_to_location() + descriptions = [] + for agent in environment.agents: + description = "" + if agent.name not in agent_to_location: + # Agent is on the way to a location + descriptions.append("") + continue + location = agent_to_location[agent.name] + agents_in_same_loc = deepcopy(environment.locations_to_agents[location]) + agents_in_same_loc.remove(agent.name) + agents_in_same_loc = list(agents_in_same_loc) + description += f"It is now {time}. You are at {location}." + if len(agents_in_same_loc) == 0: + description += " There is no one else here." + elif len(agents_in_same_loc) == 1: + description += f" {agents_in_same_loc[0]} is also here." + else: + other_agents = ", ".join(agents_in_same_loc) + description += f" {other_agents} are also here." + # description += " The locations you can go to include: \n" + # for loc, dsec in environment.locations_descriptions.items(): + # description += f"{loc}: {dsec}\n" + descriptions.append(description) + return descriptions + else: + description = "" description += f"It is now {time}. Brendan is talking to you.\n" - description += f"[Brendan]: {player_content}\n" - return [description for _ in range(len(environment.agents))] + description += f"[Brendan]: {player_content}\n" + return [description for _ in range(len(environment.agents))] diff --git a/agentverse/environments/rules/selector/__init__.py b/agentverse/environments/rules/selector/__init__.py index da11bda59..724338d7a 100644 --- a/agentverse/environments/rules/selector/__init__.py +++ b/agentverse/environments/rules/selector/__init__.py @@ -5,3 +5,4 @@ from .base import BaseSelector from .basic import BasicSelector from .classroom import ClassroomSelector +from .pokemon import PokemonSelector diff --git a/agentverse/environments/rules/selector/pokemon.py b/agentverse/environments/rules/selector/pokemon.py new file mode 100644 index 000000000..b631aa850 --- /dev/null +++ b/agentverse/environments/rules/selector/pokemon.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List +import numpy as np +import json + +from agentverse.message import Message + +from . import selector_registry as SelectorRegistry +from .base import BaseSelector + +if TYPE_CHECKING: + from agentverse.environments import PokemonEnvironment + + +@SelectorRegistry.register("pokemon") +class PokemonSelector(BaseSelector): + """ + Selector for Pokemon environment + """ + + def select_message( + self, environment: PokemonEnvironment, messages: List[Message] + ) -> List[Message]: + valid = [] + talk_matrix = np.zeros((len(environment.agents), len(environment.agents))) + agent_to_idx = {agent.name: i for i, agent in enumerate(environment.agents)} + for i, message in enumerate(messages): + try: + content = json.loads(message.content) + except json.decoder.JSONDecodeError: + valid.append(0) + continue + if content["action"] == "Speak": + try: + if "to" not in content: + # If the model does not generate receiver, then we discard the message + valid.append(0) + elif content["to"] in agent_to_idx: + # TODO: allow talk to a list of agents + valid.append(1) + # talk_matrix[i][j] = 1 ==> i talk to j + talk_matrix[agent_to_idx[message.sender]][ + agent_to_idx[content["to"]] + ] = 1 + else: + # If the receiver is not in the environment, then we discard the message + valid.append(0) + except: + valid.append(0) + continue + elif content["action"] == "MoveTo": + # If the agent move to a location that does not exist, then we discard the message + valid.append( + "to" in content and content["to"] in environment.locations_to_agents + ) + else: + valid.append(1) + selected_messages = [] + for i, message in enumerate(messages): + content = json.loads(message.content) + sender_idx = agent_to_idx[message.sender] + if valid[i] == 0: + selected_messages.append(Message()) + continue + if content["action"] == "MoveTo": + if np.sum(talk_matrix[:, sender_idx]) > 0: + # If someone talk to this agent, then we discard the move action + selected_messages.append(Message()) + else: + selected_messages.append(message) + elif content["action"] == "Speak": + receiver_idx = agent_to_idx[content["to"]] + if talk_matrix[sender_idx][receiver_idx] == 0: + # If this agent talk to someone who also talk to this agent, and we + # select the message from this agent, then we discard the message + selected_messages.append(Message()) + continue + if np.sum(talk_matrix[receiver_idx, :]) > 0: + if talk_matrix[receiver_idx][sender_idx] == 1: + # If the receiver talk to this agent, then we randomly select one message + if sender_idx < receiver_idx: + if np.random.random() < 0.5: + selected_messages.append(message) + talk_matrix[receiver_idx][sender_idx] = 0 + else: + selected_messages.append(Message()) + talk_matrix[sender_idx][receiver_idx] = 0 + else: + print("Shouldn't happen") + else: + # If the receiver talk to other agent, we still talk to the receiver (?) + selected_messages.append(message) + else: + selected_messages.append(message) + else: + selected_messages.append(message) + return selected_messages diff --git a/agentverse/environments/rules/updater/__init__.py b/agentverse/environments/rules/updater/__init__.py index 81c30a37f..6175f1007 100644 --- a/agentverse/environments/rules/updater/__init__.py +++ b/agentverse/environments/rules/updater/__init__.py @@ -5,3 +5,4 @@ from .base import BaseUpdater from .basic import BasicUpdater from .classroom import ClassroomUpdater +from .pokemon import PokemonUpdater diff --git a/agentverse/environments/rules/updater/pokemon.py b/agentverse/environments/rules/updater/pokemon.py new file mode 100644 index 000000000..c292f2a9b --- /dev/null +++ b/agentverse/environments/rules/updater/pokemon.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Tuple +import json +from copy import deepcopy + +from . import updater_registry as UpdaterRegistry +from .basic import BasicUpdater +from agentverse.message import Message + +if TYPE_CHECKING: + from agentverse.environments import PokemonEnvironment + + +@UpdaterRegistry.register("pokemon") +class PokemonUpdater(BasicUpdater): + def update_memory(self, environment: PokemonEnvironment): + for message in environment.last_messages: + if message.content == "": + continue + message = deepcopy(message) + try: + message.content = json.loads(message.content) + except json.decoder.JSONDecodeError: + continue + if message.content["action"] == "Speak": + message.content = message.content["text"] + elif message.content["action"] == "MoveTo": + if message.content["to"] in environment.locations_to_agents: + try: + orig_location = environment.get_agent_to_location()[ + message.sender + ] + environment.locations_to_agents[orig_location].remove( + message.sender + ) + except: + continue + message.content = f"[MoveTo] {message.content['to']}" + else: + message.content = f"[{message.content['action']}]" + self.add_message_to_all_agents(environment.agents, message) diff --git a/agentverse/environments/rules/visibility/__init__.py b/agentverse/environments/rules/visibility/__init__.py index c5fba1455..950518d79 100644 --- a/agentverse/environments/rules/visibility/__init__.py +++ b/agentverse/environments/rules/visibility/__init__.py @@ -9,3 +9,4 @@ from .classroom import ClassroomVisibility from .oneself import OneselfVisibility from .prisoner import PrisonerVisibility +from .pokemon import PokemonVisibility diff --git a/agentverse/environments/rules/visibility/pokemon.py b/agentverse/environments/rules/visibility/pokemon.py new file mode 100644 index 000000000..355f103b2 --- /dev/null +++ b/agentverse/environments/rules/visibility/pokemon.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from . import visibility_registry as VisibilityRegistry +from .base import BaseVisibility + +if TYPE_CHECKING: + from agentverse.environments import PokemonEnvironment + + +@VisibilityRegistry.register("pokemon") +class PokemonVisibility(BaseVisibility): + """Visibility module for Pokemon environment""" + + def update_visible_agents(self, environment: PokemonEnvironment): + for agent in environment.agents: + agent_to_location = environment.get_agent_to_location() + try: + location = agent_to_location[agent.name] + except KeyError: + # Agent is on the way to a location + continue + agents_in_same_loc = environment.locations_to_agents[location] + agent.set_receiver(agents_in_same_loc) diff --git a/agentverse/tasks/pokemon/config.yaml b/agentverse/tasks/pokemon/config.yaml index 94a867fcd..b032a501e 100644 --- a/agentverse/tasks/pokemon/config.yaml +++ b/agentverse/tasks/pokemon/config.yaml @@ -1,12 +1,34 @@ prompts: prompt: &prompt |- Now you are in the world of Pokémon Emerald, living as one of the characters. Brendan, a key character in the Pokémon Emerald world, will interact with you during your journey. Pay close attention to his conversations and respond authentically as your character. Your choices and dialogue will shape the course of your adventure. When you give your response, you should always output in the following format: - Thought: (your thought here) - Speak: (your words here) + Action: (an action name, can be Speak, MoveTo, or other actions) + Action Input: (the arguments for the action in json format, and NOTHING else) + + For example, when you would like to talk to person XX, you can output in the following format: + Thought: (your thought here) + Action: Speak + Action Input: {"to": "XX", "text": "..."} + + When you would like to do something in the current place, you can output in the following format: + Thought: (your thought here) + Action: (action_name) + Action Input: {"last_time": "xx minutes"} + + When you would like to move to another place, you can output in the following format: + Thought: (your thought here) + Action: MoveTo + Action Input: {"to": "name_of_the_place"} + + The places you can go include: + - Pokémon Center: The Pokémon Center is a place where you can get your Pokémon healed. A Pokémon Center is completely free of charge and is found in most major cities. + - Shop: The Shop is a place where you can buy the daily necessities. + - Bike Store: The Bike Store is a place where you can buy a bike. + - Park: The Park is a place where you can relax yourself. Many residents in the town like to go there to chat with others. + - Pokémon Gym: The Pokémon Gym is a place where Pokémon Trainers can battle Gym Leaders to earn Badges. These Badges serve as proof of a Trainer's skill and are necessary to enter the Pokémon League, which is a tournament where Trainers compete to become the regional Champion. ${role_description} - Now, immerse yourself in this vibrant world and let your character's personality shine through your responses to Brendan. Good luck! + Now, immerse yourself in this vibrant world and let your character's personality shine. Good luck! Here is the conversation history so far: ${chat_history} @@ -17,15 +39,37 @@ prompts: environment: env_type: pokemon max_turns: 10000000 + locations: + - name: Pokémon Center + # description: The Pokémon Center is a place where you can get your Pokémon healed. A Pokémon Center is completely free of charge and is found in most major cities. + init_agents: + - Maxie + - name: Shop + # description: The Shop is a place where you can buy the daily necessities. + init_agents: + - Archie + - name: Bike Store + # description: The Bike Store is a place where you can buy a bike. + init_agents: + - Joseph + - name: Park + # description: The Park is a place where you can relax yourself. Many residents in the town like to go there to chat with others. + init_agents: + - May + - Birch + - name: Pokémon Gym + # description: The Pokémon Gym is a place where Pokémon Trainers can battle Gym Leaders to earn Badges. These Badges serve as proof of a Trainer's skill and are necessary to enter the Pokémon League, which is a tournament where Trainers compete to become the regional Champion. + init_agents: + - Steven rule: order: type: sequential visibility: - type: oneself + type: pokemon selector: - type: basic + type: pokemon updater: - type: basic + type: pokemon describer: type: pokemon @@ -45,6 +89,9 @@ agents: model: 'gpt-3.5-turbo' temperature: 0.7 max_tokens: 1024 + stop: |+ + + - agent_type: conversation name: Birch role_description: |- @@ -60,6 +107,9 @@ agents: model: gpt-3.5-turbo temperature: 0.7 max_tokens: 1024 + stop: |+ + + - agent_type: conversation name: Steven role_description: |- @@ -75,6 +125,9 @@ agents: model: gpt-3.5-turbo temperature: 0.7 max_tokens: 1024 + stop: |+ + + - agent_type: conversation name: Maxie role_description: |- @@ -90,6 +143,9 @@ agents: model: gpt-3.5-turbo temperature: 0.7 max_tokens: 1024 + stop: |+ + + - agent_type: conversation name: Archie role_description: |- @@ -105,6 +161,9 @@ agents: model: gpt-3.5-turbo temperature: 0.7 max_tokens: 1024 + stop: |+ + + - agent_type: conversation name: Joseph role_description: |- @@ -120,5 +179,7 @@ agents: model: gpt-3.5-turbo temperature: 0.7 max_tokens: 1024 + stop: |+ + tools: diff --git a/agentverse/tasks/pokemon/output_parser.py b/agentverse/tasks/pokemon/output_parser.py index 15e7d8c34..e96f7e3cf 100644 --- a/agentverse/tasks/pokemon/output_parser.py +++ b/agentverse/tasks/pokemon/output_parser.py @@ -1,6 +1,7 @@ from __future__ import annotations import re +import json from typing import Union from agentverse.parser import OutputParser, LLMResult @@ -19,11 +20,17 @@ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]: cleaned_output = re.sub(r"\n+", "\n", cleaned_output) cleaned_output = cleaned_output.split("\n") if not ( - len(cleaned_output) == 2 + len(cleaned_output) == 3 and cleaned_output[0].startswith("Thought:") - and cleaned_output[1].startswith("Speak:") + and cleaned_output[1].startswith("Action:") + and cleaned_output[2].startswith("Action Input:") ): - raise OutputParserError("Output Format Error") - action = cleaned_output[0][len("Thought:") :].strip() - action_input = cleaned_output[1][len("Speak:") :].strip() - return AgentFinish({"output": action_input}, text) + raise OutputParserError(text) + action = cleaned_output[1][len("Action:") :].strip() + action_input = cleaned_output[2][len("Action Input:") :].strip() + try: + action_input = json.loads(action_input) + except json.JSONDecodeError: + raise OutputParserError(text) + action_input["action"] = action + return AgentFinish({"output": json.dumps(action_input)}, text) diff --git a/pokemon_server.py b/pokemon_server.py index f57bc22d7..7dbb2b2c5 100644 --- a/pokemon_server.py +++ b/pokemon_server.py @@ -1,7 +1,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from pydantic import BaseModel, Field -from typing import Set +from typing import Set, List, Dict from agentverse.agentverse import AgentVerse from agentverse.message import Message @@ -13,6 +13,14 @@ class UserRequest(BaseModel): receiver_id: int +class RoutineRequest(BaseModel): + agent_ids: List[int] + + +class UpdateRequest(BaseModel): + agent_locations: Dict[str, str] + + app = FastAPI() app.add_middleware( @@ -36,5 +44,35 @@ def chat(message: UserRequest): content = message.content receiver = message.receiver receiver_id = message.receiver_id - response = agent_verse.next(content, receiver, receiver_id) + response = agent_verse.next( + is_player=True, + player_content=content, + receiver=receiver, + receiver_id=receiver_id, + ) return response[0].dict() + + +@app.post("/make_decision") +def update(message: RoutineRequest): + response = agent_verse.next(is_player=False, agent_ids=message.agent_ids) + return [r.dict() for r in response] + # import json + + # return [ + # # { + # # "content": json.dumps( + # # { + # # "to": "Maxie", + # # "action": "Speak", + # # "text": "Hello Hello Hello Hello Hello Hello", + # # } + # # ) + # # } + # {"content": json.dumps({"to": "Pokémon Center", "action": "MoveTo"})} + # ] + + +@app.post("/update_location") +def update_location(message: UpdateRequest): + agent_verse.update_state(message.agent_locations) diff --git a/test_pokemon_env.py b/test_pokemon_env.py new file mode 100644 index 000000000..b10e764e7 --- /dev/null +++ b/test_pokemon_env.py @@ -0,0 +1,4 @@ +import requests + +requests.post('http://127.0.0.1:10002/make_decision', headers={'Content-Type': 'application/json'}, json={'agent_ids': [0, 1, 2, 3, 4, 5]}) +# requests.post('http://127.0.0.1:10002/chat', headers={'Content-Type': 'application/json'}, json={'content': 'Hi!', 'receiver': 'May', 'receiver_id': 0, 'sender': 'Brendan'}) diff --git a/ui/dist/assets/config/npcs.yaml b/ui/dist/assets/config/npcs.yaml deleted file mode 100644 index 425faacfe..000000000 --- a/ui/dist/assets/config/npcs.yaml +++ /dev/null @@ -1,50 +0,0 @@ -player_description: - -agents: - - agent_type: conversation - name: May - role_description: |- - You are May, a character in Pokémon Emerald. You are helping your dad, Professor Birch, finish the Hoenn Pokédex and becoming a Pokémon Professor. You are also Brendan's rival and friend. For a reference, here are some quotes from you - "There isn't a single Trainer left in Hoenn who doesn't know who you are, Brendan! When I tell people that I'm friends with you, Brendan, they're all surprised!" - "I wonder where I should go catch some Pokémon next? Wouldn't it be funny if we ran into each other, Brendan?"npcs.yaml - "I'm thinking of going back to Littleroot soon. I've caught a decent group of Pokémon, and my Pokédex is coming along, so I'm going home to show my dad. Brendan, what are you going to do? Collect all the Gym Badges and take the Pokémon League challenge? Well, while you're collecting Badges, Brendan, I'm going to work on my Pokédex. I'll complete it before you! See you!" - memory: - memory_type: chat_history - prompt_template: *prompt - llm: - llm_type: gpt-4 - model: 'gpt-4' - temperature: 0.7 - max_tokens: 250 - - agent_type: conversation - name: Professor Birch - role_description: |- - You are Professor Birch, a character in Pokémon Emerald. You are the resident Pokémon Professor of Littleroot Town and the Hoenn region. You specializes in Pokémon habitats and distribution. You are the father of May. You often works with your child to help observe and capture wild Pokémon. Your wife worries about you, because you are always busy and rarely has time to come home. You are known to be more outgoing than the other Pokémon Professors, and oftentimes your research takes you outdoors. Your field of study is primarily how Pokémon behave in the wild. For a reference, here are some quotes from you - "Oh, hi, Brendan! I heard you beat May on your first try. That's excellent! May's been helping with my research for a long time. May has an extensive history as a Trainer already. Here, Brendan, I ordered this for my research, but I think you should have this Pokédex." - "See? What did I tell you, May? Didn't I tell you that you don't need to worry about Brendan? ... Brendan, you've finally done it. When I heard that you defeated your own father at the Petalburg Gym, I thought perhaps you had a chance... But to think you've actually become the Champion! Ah, yes! What become of your Pokédex? Here, let me see." - "Well, well, Brendan! That was good work out there! I knew there was something special about you when I first saw you, but I never expected this. Oh, yes. Do you still have the Pokédex I gave you? I have something to show you. Let's go to my Lab." - memory: - memory_type: chat_history - prompt_template: *prompt - llm: - llm_type: gpt-4 - model: 'gpt-4' - temperature: 0.7 - max_tokens: 100 - - agent_type: conversation - name: Steven Stone - role_description: |- - You are Steven Stone, a character in Pokémon Emerald. You are a skilled Trainer who specializes in Steel-type Pokémon. You are the Champion of the Hoenn region's Pokémon League. You are a collector of rare stones, and you are the son of the president of the Devon Corporation, and you make your home in Mossdeep City. You wanders the region, aiding the player on their journey. You are just defeated by Brendan. For a reference, here are some quotes from you - "Your Pokémon appear quite capable. If you keep training, you could even become the Champion of the Pokémon League one day. That's what I think. I know, since we've gotten to know each other, let's register one another in our PokéNavs. ... Now, I've got to hurry along." - "I see... Your battle style is intriguing. Your Pokémon have obviously grown since I first met you in Dewford. I'd like you to have this Devon Scope. Who knows, there may be other concealed Pokémon. Brendon, I enjoy seeing Pokémon and Trainers who strive together. I think you're doing great. Well, let's meet again somewhere." - "Hi, Brendon! When you're on an adventure with your Pokémon, what do you think? Do you consider them to be strong partners? Do you think of them as fun companions? Depending on how you think, your adventure's significance changes." - memory: - memory_type: chat_history - prompt_template: *prompt - llm: - llm_type: gpt-4 - model: gpt-4 - temperature: 0.7 - max_tokens: 100 - -tools: diff --git a/ui/dist/assets/message/message.png b/ui/dist/assets/message/message.png deleted file mode 100644 index 7d22a4a070403eb7cbeace49dcb199e7cf849091..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41634 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 e0pz40`Tqf;f}dtJA4Wp}0000 0 && !this.moveTo.isRunning) { + if (this.path.length > 0 && !this.moveTo.isRunning && this.canMove) { var tileXY = this.board.worldXYToTileXY(this.x, this.y); if (tileXY.x == this.path[0].x) { if (tileXY.y < this.path[0].y) this.changeDirection(DIRECTION.DOWN); @@ -49,8 +62,30 @@ export class NPC extends Actor { else if (tileXY.x > this.path[0].x) this.changeDirection(DIRECTION.LEFT); } - this.moveTo.moveTo(this.path.shift()); + var move = this.moveTo.moveTo(this.path.shift()); + move.removeAllListeners("complete"); + move.on("complete", () => { + if (this.path.length == 0) { + this.changeDirection(this.finalDirection); + this.emitTurnEvent(); + if (this.targetLocation != undefined) { + fetch("http://127.0.0.1:10002/update_location", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "same-origin", + body: JSON.stringify({ + agent_locations: { + [this.name]: this.targetLocation, + }, + }), + }); + } + } + }); } + var text = ""; switch (this.direction) { case DIRECTION.UP: @@ -69,16 +104,143 @@ export class NPC extends Actor { this.anims.play(this.name + "-walk-" + text, true); if (this.anims.isPlaying && !this.moveTo.isRunning) this.anims.setCurrentFrame(this.anims.currentAnim!.frames[0]); + this.updateTextBox(); + this.depth = this.y + this.height * 0.8; + } + + listenToDirectionEvent(): void { + eventsCenter.on(this.name + "-up", () => { + this.changeDirection(DIRECTION.UP); + }); + eventsCenter.on(this.name + "-down", () => { + this.changeDirection(DIRECTION.DOWN); + }); + eventsCenter.on(this.name + "-left", () => { + this.changeDirection(DIRECTION.LEFT); + }); + eventsCenter.on(this.name + "-right", () => { + this.changeDirection(DIRECTION.RIGHT); + }); + } + + emitTurnEvent(): void { + // Make the listener NPC turn to the speaker NPC. + if (this.targetNPC == undefined) return; + var direction = ""; + switch (this.finalDirection) { + case DIRECTION.UP: + direction = "down"; + break; + case DIRECTION.DOWN: + direction = "up"; + break; + case DIRECTION.LEFT: + direction = "right"; + break; + case DIRECTION.RIGHT: + direction = "left"; + break; + } + eventsCenter.emit(this.targetNPC.name + "-" + direction); + this.setTargetNPC(); + } + + updateTextBox(): void { + if (this.textBox == undefined) return; + this.textBox.setOrigin(0.5, 1.0); + var scale = this.scene.cameras.main.zoom; + this.textBox.setX(this.x + this.width / 2); + this.textBox.setY(this.y - this.height * 0.2); + this.textBox.depth = this.y + this.height * 0.8; + this.textBox.getChildren().forEach((child) => { + child.setDepth(this.y + this.height * 0.8); + }); + } + + public setTextBox(text: string): void { + this.destroyTextBox(); + var scale = this.scene.cameras.main.zoom; + var scene = this.scene as TownScene; + this.textBox = scene.rexUI.add + .label({ + x: this.x + this.width / 2, + y: this.y - this.height * 0.2, + width: 24 * scale, + orientation: "x", + background: scene.rexUI.add.roundRectangle( + 0, + 0, + 2, + 2, + 20, + COLOR_PRIMARY, + 0.5 + ), + text: scene.rexUI.wrapExpandText( + scene.add.text(0, 0, text, { + fontSize: 10, + }) + ), + expandTextWidth: true, + space: { + left: 10, + right: 10, + top: 10, + bottom: 10, + }, + }) + .setOrigin(0.5, 1.0) + .setScale(1 / scale, 1 / scale) + .setDepth(this.y + this.height * 0.8) + .layout(); + } + + public destroyTextBox(): void { + if (this.textBox != undefined) this.textBox.destroy(); + this.textBox = undefined; } public changeDirection(direction: number): void { + if (direction == undefined) return; this.direction = direction; } - public moveAlongPath(path: PathFinder.NodeType[]): void { + public moveAlongPath( + path: PathFinder.NodeType[], + finalDirection: number = undefined, + targetLocation: string = undefined + ): void { if (path.length == 0) return; if (this.moveTo.isRunning) return; if (this.path.length > 0) return; this.path = path; + this.finalDirection = finalDirection; + this.targetLocation = targetLocation; + } + + public pauseMoving(): void { + this.moveTo.stop(); + this.canMove = false; + } + + public resumeMoving(): void { + this.moveTo.resume(); + this.canMove = true; + } + + public isMoving(): boolean { + return this.moveTo.isRunning || this.path.length > 0; + } + + public isTalking(): boolean { + return this.talkWithPlayer; + } + + public setTalking(talking: boolean): void { + this.talkWithPlayer = talking; + } + + public setTargetNPC(targetNPC: NPC = undefined): void { + this.targetNPC = targetNPC; } } diff --git a/ui/src/classes/player.ts b/ui/src/classes/player.ts index 2a208d998..46be03186 100644 --- a/ui/src/classes/player.ts +++ b/ui/src/classes/player.ts @@ -14,8 +14,8 @@ export class Player extends Actor { this.initKeyboard(); // PHYSICS - this.getBody().setSize(14, 21); - this.getBody().setOffset(0, 0); + this.getBody().setSize(14, 16); + this.getBody().setOffset(0, 5); // ANIMATIONS this.initAnimations(); @@ -54,6 +54,7 @@ export class Player extends Actor { if (!pressed_flag && this.anims.isPlaying) { this.anims.setCurrentFrame(this.anims.currentAnim!.frames[0]); } + this.depth = this.y + 0.5 * this.height; } initKeyboard(): void { diff --git a/ui/src/constants.ts b/ui/src/constants.ts new file mode 100644 index 000000000..eb9cdfd03 --- /dev/null +++ b/ui/src/constants.ts @@ -0,0 +1,3 @@ +export const COLOR_PRIMARY = 0x4e342e; +export const COLOR_LIGHT = 0x7b5e57; +export const COLOR_DARK = 0x260e04; diff --git a/ui/src/scenes/town/town.ts b/ui/src/scenes/town/town.ts index cf59895ae..c35db5475 100644 --- a/ui/src/scenes/town/town.ts +++ b/ui/src/scenes/town/town.ts @@ -1,12 +1,5 @@ import * as Phaser from "phaser"; -import { - Scene, - Tilemaps, - GameObjects, - Physics, - Input, - Math as Mathph, -} from "phaser"; +import { Scene, Tilemaps, GameObjects, Physics, Math as Mathph } from "phaser"; import { Player } from "../../classes/player"; import { NPC } from "../../classes/npc"; import { DIRECTION } from "../../utils"; @@ -18,12 +11,13 @@ import UIPlugin from "../../phaser3-rex-plugins/templates/ui/ui-plugin"; import BoardPlugin from "../../phaser3-rex-plugins/plugins/board-plugin"; import { PathFinder } from "../../phaser3-rex-plugins/plugins/board-components"; import { TileXYType } from "../../phaser3-rex-plugins/plugins/board/types/Position"; - -const COLOR_PRIMARY = 0x4e342e; -const COLOR_LIGHT = 0x7b5e57; -const COLOR_DARK = 0x260e04; +import { shuffle } from "../../utils"; +import { COLOR_DARK, COLOR_LIGHT, COLOR_PRIMARY } from "../../constants"; export class TownScene extends Scene { + private timeFrame: number = 0; + private isQuerying: boolean = false; + private map: Tilemaps.Tilemap; private tileset: Tilemaps.Tileset; private groundLayer: Tilemaps.TilemapLayer; @@ -36,8 +30,8 @@ export class TownScene extends Scene { private npcGroup: GameObjects.Group; private keySpace: Phaser.Input.Keyboard.Key; private keyEnter: Phaser.Input.Keyboard.Key; - private rexUI: UIPlugin; - private rexBoard: BoardPlugin; + public rexUI: UIPlugin; + public rexBoard: BoardPlugin; private board: BoardPlugin.Board; private pathFinder: PathFinder; @@ -51,6 +45,83 @@ export class TownScene extends Scene { this.initMap(); this.initSprite(); this.initCamera(); + // this.add.grid(0, 0, 1024, 1024, 16, 16, 0x000000).setAlpha(0.1); + } + + update(time, delta): void { + this.timeFrame += delta; + this.player.update(); + + this.npcGroup.getChildren().forEach(function (npc) { + (npc as NPC).update(); + }); + if (this.timeFrame > 5000) { + if (!this.isQuerying) { + this.isQuerying = true; + var allNpcs = this.npcGroup.getChildren(); + var shouldUpdate = []; + + for (let i = 0; i < this.npcGroup.getLength(); i++) { + // for (let i = 0; i < 1; i++) { + if ( + !(allNpcs[i] as NPC).isMoving() && + !(allNpcs[i] as NPC).isTalking() + ) { + shouldUpdate.push(i); + } + } + + fetch("http://127.0.0.1:10002/make_decision", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + credentials: "same-origin", + body: JSON.stringify({ + agent_ids: shouldUpdate, + }), + }).then((response) => { + response.json().then((data) => { + this.npcGroup.getChildren().forEach(function (npc) { + (npc as NPC).destroyTextBox(); + }); + for (let i = 0; i < data.length; i++) { + var npc = allNpcs[shouldUpdate[i]] as NPC; + if (data[i].content == "") continue; + var content = JSON.parse(data[i].content); + switch (content.action) { + case "MoveTo": + var tile = this.getRandomTileAtLocation(content.to); + if (tile == undefined) break; + npc.destroyTextBox(); + this.moveNPC(shouldUpdate[i], tile, undefined, content.to); + break; + case "Speak": + var ret = this.getNPCNeighbor(content.to); + var tile = ret[0]; + var finalDirection = ret[1]; + var listener = ret[2]; + if (tile == undefined) break; + this.moveNPC( + shouldUpdate[i], + tile, + finalDirection, + undefined, + listener + ); + npc.setTextBox(content.text); + break; + default: + npc.setTextBox("[" + content.action + "]"); + break; + } + } + this.isQuerying = false; + }); + }); + } + this.timeFrame = 0; + } } initMap(): void { @@ -90,9 +161,13 @@ export class TownScene extends Scene { var npcPoints = this.map.filterObjects("npcs", (npc) => { return npc.type === "npc"; }); - var npcs = npcPoints.map((npcPoint) => { - var id_property = npcPoint.properties.filter((property) => { - return property.name === "id"; + for (let i = 0; i < npcPoints.length; i++) { + var npcPoint = this.map.findObject("npcs", (npc) => { + for (let j = 0; j < npc.properties.length; j++) { + if (npc.properties[j].name === "id") { + return npc.properties[j].value === i; + } + } }); var tileXY = this.board.worldXYToTileXY(npcPoint.x, npcPoint.y); var npc = new NPC( @@ -101,44 +176,58 @@ export class TownScene extends Scene { npcPoint.x, npcPoint.y, npcPoint.name, - id_property[0].value + npcPoint.properties[0].value ); this.board.addChess(npc, tileXY.x, tileXY.y, 0, true); + this.physics.add.collider(npc, this.npcGroup); this.npcGroup.add(npc); - }); + } + this.physics.add.collider(this.npcGroup, this.wallLayer); this.physics.add.collider(this.npcGroup, this.treeLayer); this.physics.add.collider(this.npcGroup, this.houseLayer); + // this.physics.add.collider(this.npcGroup, this.npcGroup); // Player - this.player = new Player(this, 256, 250); - var tileXY = this.board.worldXYToTileXY(this.player.x, this.player.y); - // this.board.addChess(this.player, tileXY.x, tileXY.y, 0, true); + this.player = new Player(this, 288, 240); this.physics.add.collider(this.player, this.wallLayer); this.physics.add.collider(this.player, this.treeLayer); this.physics.add.collider(this.player, this.houseLayer); - this.physics.add.collider(this.player, this.npcGroup); + this.physics.add.collider( + this.player, + this.npcGroup, + (player: Player, npc: NPC) => { + npc.pauseMoving(); + var checkResumeWalk = this.time.addEvent({ + delay: 1000, + callback: () => { + const nearbyDistance = 1.1 * Math.max(player.width, player.height); + var distance = Mathph.Distance.Between( + player.x, + player.y, + npc.x, + npc.y + ); + if (distance > nearbyDistance) { + npc.resumeMoving(); + checkResumeWalk.destroy(); + } + }, + }); + } + ); this.keySpace.on("up", () => { var ret = getNearbyNPC(this.player, this.npcGroup); var npc = ret[0]; if (npc) { + npc = npc as NPC; (npc as NPC).changeDirection(ret[1]); + (npc as NPC).setTalking(true); this.createInputBox(npc); } }); - var scene = this; - this.keyEnter.on("up", () => { - var npc = this.npcGroup.getChildren()[0] as NPC; - var npc_chess = this.board.worldXYToChess(npc.x, npc.y); - this.pathFinder.setChess(npc_chess); - var tmp = this.board.chessToTileXYZ(npc_chess); - var path = this.pathFinder.findPath({ - x: tmp.x + 3, - y: tmp.y - 6, - } as TileXYType); - npc.moveAlongPath(path); - }); + // this.keyEnter.on("up", () => {}); this.physics.world.setBounds( 0, @@ -160,13 +249,6 @@ export class TownScene extends Scene { this.cameras.main.setZoom(4); } - update(): void { - this.player.update(); - this.npcGroup.getChildren().forEach(function (npc) { - (npc as NPC).update(); - }); - } - disableKeyboard(): void { this.input.keyboard.manager.enabled = false; } @@ -300,16 +382,18 @@ export class TownScene extends Scene { }), }).then((response) => { response.json().then((data) => { - console.log(data); + // console.log(data); timer.destroy(); waitingBox.destroy(); + var content = JSON.parse(data.content); var responseBox = this.createTextBox() - .start(data.content, 50) + .start(content.text, 25) .on("complete", () => { this.enableKeyboard(); - this.input.keyboard.on("keyup", () => { + this.input.keyboard.on("keydown", () => { responseBox.destroy(); - this.input.keyboard.off("keyup"); + this.input.keyboard.off("keydown"); + (npc as NPC).setTalking(false); }); }); }); @@ -356,9 +440,97 @@ export class TownScene extends Scene { }) .setScale(0.25, 0.25) .setOrigin(0) + .setDepth(Number.MAX_SAFE_INTEGER) .layout(); return textBox; } + + getRandomTileAtLocation(location_name: string): TileXYType { + var location = this.map.findObject("location", function (object) { + return object.name == location_name; + }); + var x = location.x; + var y = location.y; + var width = location.width; + var height = location.height; + var cnt = 0; + debugger; + do { + if (cnt > 10) { + console.log("Failed to find a random tile"); + return null; + } + var worldX = Math.floor(Math.random() * width) + x; + var worldY = Math.floor(Math.random() * height) + y; + var tile = this.board.worldXYToTileXY(worldX, worldY); + cnt++; + } while ( + this.board.hasBlocker(tile.x, tile.y) || // has wall + this.board.tileXYToChessArray(tile.x, tile.y).length != + this.map.layers.length // has npc + ); + return tile; + } + + getNPCNeighbor(npc_name: string): [TileXYType, number, NPC] { + var npc = this.npcGroup.getChildren().find((npc) => { + return (npc as NPC).name == npc_name; + }) as NPC; + var npcTile = this.board.worldXYToTileXY(npc.x, npc.y); + var directions = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + var order = shuffle([0, 1, 2, 3]); + var tileX = undefined; + var tileY = undefined; + for (let i = 0; i < 4; i++) { + var direction = directions[order[i]]; + var tmpX = npcTile.x + direction[0]; + var tmpY = npcTile.y + direction[1]; + if ( + !this.board.hasBlocker(tmpX, tmpY) && // no wall + this.board.tileXYToChessArray(tmpX, tmpY).length == + this.map.layers.length // no npc) + ) { + tileX = tmpX; + tileY = tmpY; + break; + } + } + var finalDirection = DIRECTION.DOWN; + if (direction[0] == 0 && direction[1] == 1) { + finalDirection = DIRECTION.UP; + } else if (direction[0] == 0 && direction[1] == -1) { + finalDirection = DIRECTION.DOWN; + } else if (direction[0] == 1 && direction[1] == 0) { + finalDirection = DIRECTION.LEFT; + } else if (direction[0] == -1 && direction[1] == 0) { + finalDirection = DIRECTION.RIGHT; + } + return [{ x: tileX, y: tileY }, finalDirection, npc]; + } + + moveNPC( + npcId: number, + tile, + finalDirection: number = undefined, + targetLocation: string = undefined, + targetNPC: NPC = undefined + ): void { + var npc = this.npcGroup.getChildren()[npcId] as NPC; + var npc_chess = this.board.worldXYToChess(npc.x, npc.y); + this.pathFinder.setChess(npc_chess); + // var tmp = this.board.chessToTileXYZ(npc_chess); + var path = this.pathFinder.findPath({ + x: tile.x, + y: tile.y, + } as TileXYType); + npc.setTargetNPC(targetNPC); + npc.moveAlongPath(path, finalDirection, targetLocation); + } } function getNearbyNPC( diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 63f41b550..e059f5106 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -4,3 +4,23 @@ export enum DIRECTION { LEFT, RIGHT, } + +export function shuffle(array: any[]) { + let currentIndex = array.length, + randomIndex; + + // While there remain elements to shuffle. + while (currentIndex != 0) { + // Pick a remaining element. + randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex--; + + // And swap it with the current element. + [array[currentIndex], array[randomIndex]] = [ + array[randomIndex], + array[currentIndex], + ]; + } + + return array; +} diff --git a/ui/tsconfig.json b/ui/tsconfig.json index cecd357a9..8fece6d48 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { "target": "es5", - "moduleResolution": "node" + "moduleResolution": "node", + "lib": ["ESNext", "dom"], }, "include": [ "./src/**/*"