new file: .DS_Store
new file: CTF/.ipynb_checkpoints/fixed_map_example-checkpoint.png new file: CTF/.ipynb_checkpoints/random_map_example-checkpoint.png new file: CTF/CTF/README.md new file: CTF/CTF/backend/example_init.json new file: CTF/CTF/backend/example_plan_next_actions.json new file: CTF/CTF/backend/lib/game_engine.py new file: CTF/CTF/backend/main.ipynb new file: CTF/CTF/backend/pick_closest_flag.py new file: CTF/CTF/backend/pick_flag_ai.py new file: CTF/CTF/backend/python_tutorial.ipynb new file: CTF/CTF/backend/start.sh new file: CTF/CTF/backend/test_lib.ipynb new file: CTF/CTF/fixed_map_example.png new file: CTF/CTF/frontend/assets/characters.png new file: CTF/CTF/frontend/assets/characters_red_flag.png new file: CTF/CTF/frontend/assets/characters_yellow_flag.png new file: CTF/CTF/frontend/assets/red_flag_16_16.png new file: CTF/CTF/frontend/assets/red_flag_32_32.png new file: CTF/CTF/frontend/assets/tilemap.json new file: CTF/CTF/frontend/assets/tilemap.tmx new file: CTF/CTF/frontend/assets/tiles.png new file: CTF/CTF/frontend/assets/yellow_flag_16_16.png new file: CTF/CTF/frontend/assets/yellow_flag_32_32.png new file: CTF/CTF/frontend/game_config.json new file: CTF/CTF/frontend/index.html new file: CTF/CTF/frontend/phaser.js new file: CTF/CTF/frontend/project.config new file: CTF/CTF/frontend/src/assets.js new file: CTF/CTF/frontend/src/gameObjects/Flag.js new file: CTF/CTF/frontend/src/gameObjects/Player.js new file: CTF/CTF/frontend/src/main.js new file: CTF/CTF/frontend/src/scenes/Boot.js new file: CTF/CTF/frontend/src/scenes/Game.js new file: CTF/CTF/frontend/src/scenes/GameOver.js new file: CTF/CTF/frontend/src/scenes/Preloader.js new file: CTF/CTF/frontend/start.sh new file: CTF/CTF/random_map_example.png new file: CTF/README.md new file: CTF/backend/.ipynb_checkpoints/main-checkpoint.ipynb new file: CTF/backend/.ipynb_checkpoints/pick_closest_flag-checkpoint.py new file: CTF/backend/.ipynb_checkpoints/pick_flag_ai-checkpoint.py new file: CTF/backend/.ipynb_checkpoints/python_tutorial-checkpoint.ipynb new file: CTF/backend/.ipynb_checkpoints/start-checkpoint.sh new file: CTF/backend/.ipynb_checkpoints/test_lib-checkpoint.ipynb new file: CTF/backend/example_init.json new file: CTF/backend/example_plan_next_actions.json new file: CTF/backend/lib/__pycache__/game_engine.cpython-313.pyc new file: CTF/backend/lib/game_engine.py new file: CTF/backend/main.ipynb new file: CTF/backend/pick_closest_flag.py new file: CTF/backend/pick_flag_ai.py new file: CTF/backend/python_tutorial.ipynb new file: CTF/backend/start.sh new file: CTF/backend/test_lib.ipynb new file: CTF/fixed_map_example.png new file: CTF/frontend/.ipynb_checkpoints/game_config-checkpoint.json new file: CTF/frontend/assets/.ipynb_checkpoints/tilemap-checkpoint.json new file: CTF/frontend/assets/characters.png new file: CTF/frontend/assets/characters_red_flag.png new file: CTF/frontend/assets/characters_yellow_flag.png new file: CTF/frontend/assets/red_flag_16_16.png new file: CTF/frontend/assets/red_flag_32_32.png new file: CTF/frontend/assets/tilemap.json new file: CTF/frontend/assets/tilemap.tmx new file: CTF/frontend/assets/tiles.png new file: CTF/frontend/assets/yellow_flag_16_16.png new file: CTF/frontend/assets/yellow_flag_32_32.png new file: CTF/frontend/game_config.json new file: CTF/frontend/index.html new file: CTF/frontend/phaser.js new file: CTF/frontend/project.config new file: CTF/frontend/src/assets.js new file: CTF/frontend/src/gameObjects/Flag.js new file: CTF/frontend/src/gameObjects/Player.js new file: CTF/frontend/src/main.js new file: CTF/frontend/src/scenes/Boot.js new file: CTF/frontend/src/scenes/Game.js new file: CTF/frontend/src/scenes/GameOver.js new file: CTF/frontend/src/scenes/Preloader.js new file: CTF/frontend/start.sh new file: CTF/random_map_example.png
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "21c8be3f-c57b-439c-8ca8-5991dd0da465",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 夺旗赛 Capture The Flag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0dfeabca-44d9-4906-abc6-cba1e4fc8a35",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 初始化 (使用上排目录栏的的 ▶️ 运行,▪️停止, ⟳ 重启)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "098c5853-135d-4eb8-ab51-bbf660fc09e9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import importlib\n",
|
||||
"import lib.game_engine\n",
|
||||
"\n",
|
||||
"# Force the reload manually\n",
|
||||
"importlib.reload(lib.game_engine)\n",
|
||||
"\n",
|
||||
"# Re-import the specific classes/functions\n",
|
||||
"from lib.game_engine import GameMap, run_game_server\n",
|
||||
"\n",
|
||||
"# Now initialize your objects\n",
|
||||
"world = GameMap()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e9437ef2-777a-4694-b1b2-428e654be4ab",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython.display import clear_output\n",
|
||||
"import os\n",
|
||||
"import json\n",
|
||||
"import random"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a1d9d9f6-3644-41bf-a60d-0148a74140c4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 以下是你要编写的代码。 \n",
|
||||
"- start_game:初始化游戏。\n",
|
||||
"- game_over:游戏结束。\n",
|
||||
"- plan_next_actions:每一时刻,告诉你目前小人们的位置。\n",
|
||||
" \n",
|
||||
"每次代码更新,一定要使用上排目录栏的的 ▶️ 运行"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "021ea24a-9a8f-4a4d-a086-f3b80b882c95",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是你要编写的策略\n",
|
||||
"def start_game(req):\n",
|
||||
" \"\"\"Called when the game begins.\"\"\"\n",
|
||||
" world.init(req)\n",
|
||||
" print(f\"Map initialized: {world.width}x{world.height}\")\n",
|
||||
"\n",
|
||||
"def game_over(req):\n",
|
||||
" \"\"\"Called when the game ends.\"\"\"\n",
|
||||
" print(\"Game Over!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "22e04cb6-87cb-486d-a580-a1ae5fbe3a98",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是你要编写的策略。以下always_move_right和walk_to_first_flag_and_return是两个例子\n",
|
||||
"def plan_next_actions(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" world.update(req) \n",
|
||||
" world.show() \n",
|
||||
" actions = dict()\n",
|
||||
" # TODO:请在这里写下你的代码来控制小人\n",
|
||||
" \n",
|
||||
" return actions\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a8561aec-4246-40dc-b833-1d2b8991edd3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是一个超简单策略:即把所有的小人都向右移动\n",
|
||||
"## Python小测试 - 将以下程序改成:如果是L team,就往右走;如果是R team,就往左走。\n",
|
||||
"def always_move_right(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" if not world.update(req):\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" world.show() \n",
|
||||
"\n",
|
||||
" # List all players that can move freely (set `hasFlag=True`)\n",
|
||||
" my_players = world.list_players(mine=True, inPrison=False, hasFlag=None)\n",
|
||||
"\n",
|
||||
" actions = dict()\n",
|
||||
" for p in my_players:\n",
|
||||
" actions[p[\"name\"]] = \"right\"\n",
|
||||
"\n",
|
||||
" return actions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "e4b51cda-514b-4264-b8af-53acc4a76120",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是一个超简单策略:所有小人都向着第一个小旗子走\n",
|
||||
"## Python小测试 - 将以下程序改成:如果在对方的领地里,将对方的player位置设为extra_obstacles\n",
|
||||
"\n",
|
||||
"def walk_to_first_flag_and_return(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" if not world.update(req):\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # world.show() always show targets and prisons, regardless whether flags and players are not there or not\n",
|
||||
" world.show(flag_over_target=True, player_over_prison=True) \n",
|
||||
"\n",
|
||||
" # List all players that can move freely (set `hasFlag=True`)\n",
|
||||
" my_players_go = world.list_players(mine=True, inPrison=False, hasFlag=False)\n",
|
||||
" my_players_return = world.list_players(mine=True, inPrison=False, hasFlag=True)\n",
|
||||
" # List a\n",
|
||||
" opponents = world.list_players(mine=False, inPrison=False, hasFlag=None)\n",
|
||||
" enemy_flags = world.list_flags(mine=False, canPickup=None)\n",
|
||||
" \n",
|
||||
" actions = {}\n",
|
||||
" \n",
|
||||
" # Everyone wo/ a flag rushes the first flag\n",
|
||||
" if enemy_flags:\n",
|
||||
" target_flag = enemy_flags[0]\n",
|
||||
" dest = (target_flag[\"posX\"], target_flag[\"posY\"])\n",
|
||||
" for p in my_players_go:\n",
|
||||
" start = (p[\"posX\"], p[\"posY\"])\n",
|
||||
" path = world.route_to(start, dest, extra_obstacles=[])\n",
|
||||
" \n",
|
||||
" if len(path) > 1:\n",
|
||||
" # Convert the next coordinate in path to a direction string\n",
|
||||
" next_step = path[1]\n",
|
||||
" actions[p[\"name\"]] = GameMap.get_direction(start, next_step)\n",
|
||||
" # Everyone w/ a flag returns the target zone\n",
|
||||
" target_zone = list(world.list_targets(mine=True))[0]\n",
|
||||
" for p in my_players_return:\n",
|
||||
" start = (p[\"posX\"], p[\"posY\"])\n",
|
||||
" path = world.route_to(start, target_zone, extra_obstacles=[])\n",
|
||||
" if len(path) > 1:\n",
|
||||
" # Convert the next coordinate in path to a direction string\n",
|
||||
" next_step = path[1]\n",
|
||||
" actions[p[\"name\"]] = GameMap.get_direction(start, next_step)\n",
|
||||
" return actions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "322772ab-a0b8-4da3-906f-212926ad24fb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 运行以下cell启动你的server (使用上排目录栏的的 ▶️ 运行,使用▪️停止)\n",
|
||||
"[*] 表示cell正在运行中"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e0898ab6-bd6e-4bf1-9a29-d802933dd91a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Change the port to match your game settings\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"PORT_ID = 2 # 或者 2\n",
|
||||
"PORT = \"CTF_PORT_BACKEND\" + str(PORT_ID)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" PORT_VALUE = os.environ[PORT]\n",
|
||||
" print(f\"PORT: {PORT_VALUE}\")\n",
|
||||
"except KeyError:\n",
|
||||
" print(\"Error: PORT environment variable not set.\")\n",
|
||||
"try:\n",
|
||||
" # 将`plan_fn=`改成你的plan_next_actions,如always_move_right, walk_to_first_flag_and_return 等等\n",
|
||||
" await run_game_server(PORT_VALUE, start_fn=start_game, plan_fn=walk_to_first_flag_and_return, end_fn=game_over)\n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"Server stopped: {e}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b869dde3-cf94-4d25-8fe8-25096bab1f37",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8e8d02fc-7dac-4ad1-985b-c2aae132c920",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import asyncio
|
||||
import json
|
||||
import random
|
||||
import traceback
|
||||
import websockets
|
||||
|
||||
ACTIONS = ["up", "down", "left", "right", ""]
|
||||
|
||||
class GameMap:
|
||||
|
||||
EMPTY = 0
|
||||
OBSTACLE = -1
|
||||
FLAG = 2
|
||||
|
||||
ACTIONS = ["up", "down", "left", "right", ""]
|
||||
ACTIONS_IN_MOVE = [(0, -1), (0, 1), (-1, 0), (1, 0)]
|
||||
|
||||
def __init__(self, map_json):
|
||||
self.w = map_json["width"]
|
||||
self.h = map_json["height"]
|
||||
self.grids = [[GameMap.EMPTY for _ in range(0, self.h)] for _ in range(0, self.w)]
|
||||
self.obstacles = map_json["walls"] + map_json["obstacles"]
|
||||
for o in self.obstacles:
|
||||
self.grids[o["x"]][o["y"]] = GameMap.OBSTACLE
|
||||
|
||||
def show_map(self):
|
||||
for y in range(0, self.h):
|
||||
for x in range(0, self.w):
|
||||
print(self.grids[x][y], end=" ")
|
||||
print("")
|
||||
|
||||
|
||||
def find_closest_goal_from_pos(self, pos_x, pos_y, goals, blockers):
|
||||
"""
|
||||
Find the closest goal(@goal_x, @goal_y) in @goals from (@pos_x, @pos_y).
|
||||
Return the moving direction for (@pos_x, @pos_y).
|
||||
If none is reachable, return "".
|
||||
"""
|
||||
goals_pos = {(g["x"], g["y"]) for g in goals}
|
||||
blocker_pos = set()
|
||||
for (bx, by) in blockers:
|
||||
for (dx, dy) in GameMap.ACTIONS_IN_MOVE:
|
||||
x = bx + dx
|
||||
y = by + dy
|
||||
if (x, y) not in goals_pos:
|
||||
blocker_pos.add((bx, by))
|
||||
|
||||
# visited != -1 means the grid is reached from (pos_x, pos_y)
|
||||
# it stores the previous grid's direction to reach the current grid
|
||||
visited = [[-1 for _ in range(0, self.h)] for _ in range(0, self.w)]
|
||||
bfs = [(pos_x, pos_y)]
|
||||
st = 0
|
||||
goal_x = -1
|
||||
goal_y = -1
|
||||
while st < len(bfs):
|
||||
for d, (dx, dy) in enumerate(GameMap.ACTIONS_IN_MOVE):
|
||||
x = bfs[st][0] + dx
|
||||
y = bfs[st][1] + dy
|
||||
if (self.grids[x][y] != GameMap.OBSTACLE and
|
||||
((x, y) not in blocker_pos) and
|
||||
visited[x][y] < 0):
|
||||
visited[x][y] = d
|
||||
bfs.append((x, y))
|
||||
if (x, y) in goals_pos:
|
||||
goal_x = x
|
||||
goal_y = y
|
||||
break
|
||||
st = st + 1
|
||||
|
||||
# we need to find the very first direction taken by (pos_x, pos_y) to reach (goal_x, goal_y)
|
||||
if goal_x < 0 or goal_y < 0:
|
||||
return GameMap.ACTIONS[-1]
|
||||
cur_x = goal_x
|
||||
cur_y = goal_y
|
||||
first_direction = -1
|
||||
while cur_x != pos_x or cur_y != pos_y:
|
||||
first_direction = visited[cur_x][cur_y]
|
||||
cur_x = cur_x - GameMap.ACTIONS_IN_MOVE[first_direction][0]
|
||||
cur_y = cur_y - GameMap.ACTIONS_IN_MOVE[first_direction][1]
|
||||
|
||||
return GameMap.ACTIONS[first_direction]
|
||||
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
self.map = None
|
||||
self.team_name = None
|
||||
self.team_target = None
|
||||
self.num_flags = 0
|
||||
self.num_players = 0
|
||||
self.game_started = False
|
||||
self.player_to_flag_assignments = None
|
||||
|
||||
def startGame(self, game_json):
|
||||
self.map = GameMap(game_json["map"])
|
||||
self.team_name = game_json["myteamName"]
|
||||
self.team_target = game_json["myteamTarget"]
|
||||
self.num_flags = game_json["numFlags"]
|
||||
self.num_players = game_json["numPlayers"]
|
||||
self.game_started = True
|
||||
self.middle_line = self.map.w / 2;
|
||||
self.my_team_on_the_left = self.team_target[0]['x'] < self.middle_line;
|
||||
# playerName -> (flagX, flagY)
|
||||
self.player_to_flag_assignments = dict()
|
||||
|
||||
def endGame(self, game_json):
|
||||
self.map = None
|
||||
self.team_name = None
|
||||
self.team_target = None
|
||||
self.num_flags = 0
|
||||
self.num_players = 0
|
||||
self.game_started = False
|
||||
self.player_to_flag_assignments = None
|
||||
|
||||
def is_player_safe(self, player):
|
||||
return (player["posX"] < self.middle_line) == self.my_team_on_the_left
|
||||
|
||||
|
||||
def assign_flags_to_players(self, players, flags):
|
||||
"""
|
||||
assign all pickable flags to all eligible players (i.e., !hasFlag and !inPrison)
|
||||
"""
|
||||
# remove the prison player and player with flags
|
||||
for p in players:
|
||||
if (p["hasFlag"] or p["inPrison"]) and p["name"] in self.player_to_flag_assignments:
|
||||
del self.player_to_flag_assignments[p["name"]]
|
||||
pickable_flags = {
|
||||
(f["posX"], f["posY"]): False for f in flags if f["canPickup"]
|
||||
}
|
||||
players_wo_flag = set([p["name"] for p in players if (not p["hasFlag"]) and (not p["inPrison"])])
|
||||
for p, f in self.player_to_flag_assignments.items():
|
||||
if f in pickable_flags:
|
||||
pickable_flags[f] = True
|
||||
players_wo_flag.remove(p)
|
||||
|
||||
# randomly match unassigned flags and players
|
||||
flags_wo_player = [f for (f, m) in pickable_flags.items() if not m]
|
||||
if len(flags_wo_player) > 0:
|
||||
for i, p in enumerate(players_wo_flag):
|
||||
self.player_to_flag_assignments[p] = flags_wo_player[i % len(flags_wo_player)]
|
||||
elif len(pickable_flags) > 0:
|
||||
pickable_flags_list = list(pickable_flags)
|
||||
for i, p in enumerate(players_wo_flag):
|
||||
self.player_to_flag_assignments[p] = random.choice(pickable_flags_list)
|
||||
|
||||
|
||||
def find_next_move(self, player, opponents):
|
||||
if player["inPrison"]:
|
||||
return ""
|
||||
|
||||
blockers = [] if self.is_player_safe(player) else [(o["posX"], o["posY"]) for o in opponents]
|
||||
if player["hasFlag"]:
|
||||
return self.map.find_closest_goal_from_pos(
|
||||
player["posX"], player["posY"],
|
||||
[GAME.team_target[0]],
|
||||
blockers)
|
||||
|
||||
if player["name"] not in self.player_to_flag_assignments:
|
||||
return ""
|
||||
|
||||
flag = self.player_to_flag_assignments[player["name"]]
|
||||
return self.map.find_closest_goal_from_pos(
|
||||
player["posX"], player["posY"],
|
||||
[{"x": flag[0], "y": flag[1]}],
|
||||
blockers
|
||||
)
|
||||
|
||||
|
||||
# SINGLETON
|
||||
GAME = Game()
|
||||
|
||||
async def startGame(req):
|
||||
print("Start Game")
|
||||
|
||||
global GAME
|
||||
GAME.startGame(req)
|
||||
|
||||
|
||||
async def planNextActions(req, websocket):
|
||||
global GAME
|
||||
|
||||
player_moves = dict()
|
||||
players = req.get("myteamPlayer", [])
|
||||
opponents = req.get("opponentPlayer", [])
|
||||
flags = req.get("opponentFlag", [])
|
||||
GAME.assign_flags_to_players(players, flags)
|
||||
|
||||
for p in players:
|
||||
action = GAME.find_next_move(p, opponents)
|
||||
if action != "":
|
||||
player_moves[p["name"]] = action
|
||||
|
||||
result = {"players": player_moves}
|
||||
await websocket.send(json.dumps(result))
|
||||
|
||||
|
||||
async def gameOver(req):
|
||||
global GAME
|
||||
GAME.endGame(req)
|
||||
|
||||
|
||||
async def handle_client(websocket):
|
||||
print("New session started")
|
||||
|
||||
try:
|
||||
async for msg in websocket:
|
||||
try:
|
||||
req = json.loads(msg)
|
||||
|
||||
if req.get("action") == "status":
|
||||
await planNextActions(req, websocket)
|
||||
|
||||
elif req.get("action") == "init":
|
||||
await startGame(req)
|
||||
|
||||
elif req.get("action") == "finished":
|
||||
await gameOver(req)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("JSON parse error")
|
||||
await websocket.send(json.dumps({"error": "invalid json"}))
|
||||
|
||||
except websockets.exceptions.ConnectionClosedOK:
|
||||
print("Client closed connection normally")
|
||||
except websockets.exceptions.ConnectionClosedError as e:
|
||||
print(f"Connection error: {e}")
|
||||
except Exception as e:
|
||||
print(f"Exception: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
async def main():
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <port>")
|
||||
print(f"Example: python3 {sys.argv[0]} 8080")
|
||||
return
|
||||
|
||||
port = int(sys.argv[1])
|
||||
print(f"AI backend running on port {port} ...")
|
||||
|
||||
async with websockets.serve(handle_client, "0.0.0.0", port):
|
||||
await asyncio.Future() # run forever
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,103 @@
|
||||
import asyncio
|
||||
import random
|
||||
from lib.game_engine import GameMap, run_game_server
|
||||
import threading
|
||||
|
||||
|
||||
# 1. Initialize the global world model
|
||||
world = GameMap(show_gap_in_msec=1000.0)
|
||||
lock = threading.Lock()
|
||||
last_updated_time = -1
|
||||
update_threshold = 100
|
||||
player_to_flag_assignments = {}
|
||||
|
||||
def start_game(req):
|
||||
global player_to_flag_assignments
|
||||
world.init(req)
|
||||
print("Start Game!!")
|
||||
player_to_flag_assignments = {}
|
||||
print(f"Game Started! Side: {'Left' if world.is_on_left(list(world.my_team_target)[0]) else 'Right'}")
|
||||
|
||||
def plan_next_actions(req):
|
||||
if not world.update(req):
|
||||
return
|
||||
|
||||
global player_to_flag_assignments
|
||||
|
||||
# Render the map
|
||||
# world.show(do_not_clear=False)
|
||||
|
||||
my_players = world.list_players(mine=True, inPrison=False, hasFlag=None)
|
||||
opponents = world.list_players(mine=False, inPrison=False, hasFlag=None)
|
||||
enemy_flags = world.list_flags(mine=False, canPickup=True)
|
||||
my_targets = list(world.list_targets(mine=True))
|
||||
|
||||
# 2. Logic: Assign flags to players without flags
|
||||
# We maintain the original logic of matching players to specific flag coordinates
|
||||
active_player_names = {p["name"] for p in my_players if not p["hasFlag"]}
|
||||
|
||||
# Cleanup assignments for players captured or flags already taken
|
||||
player_to_flag_assignments = {
|
||||
name: pos for name, pos in player_to_flag_assignments.items()
|
||||
if name in active_player_names
|
||||
}
|
||||
|
||||
if enemy_flags:
|
||||
for p in my_players:
|
||||
if not p["hasFlag"] and p["name"] not in player_to_flag_assignments:
|
||||
# Randomly assign one of the available enemy flags
|
||||
f = random.choice(enemy_flags)
|
||||
player_to_flag_assignments[p["name"]] = (f["posX"], f["posY"])
|
||||
|
||||
# 3. Plan moves for each player
|
||||
player_moves = {}
|
||||
my_side_is_left = world.is_on_left(my_targets[0])
|
||||
|
||||
for p in my_players:
|
||||
curr_pos = (p["posX"], p["posY"])
|
||||
|
||||
# Determine Target: Either the assigned flag or the home target
|
||||
if p["hasFlag"]:
|
||||
dest = my_targets[0]
|
||||
elif p["name"] in player_to_flag_assignments:
|
||||
dest = player_to_flag_assignments[p["name"]]
|
||||
else:
|
||||
continue
|
||||
|
||||
# Determine Obstacles: Avoid opponents if we are in enemy territory
|
||||
is_safe = world.is_on_left(curr_pos) == my_side_is_left
|
||||
blockers = [] if is_safe else [(o["posX"], o["posY"]) for o in opponents]
|
||||
|
||||
# Calculate Path
|
||||
path = world.route_to(curr_pos, dest, extra_obstacles=blockers)
|
||||
|
||||
if len(path) > 1:
|
||||
move = world.get_direction(curr_pos, path[1])
|
||||
player_moves[p["name"]] = move
|
||||
|
||||
return player_moves
|
||||
|
||||
def game_over(req):
|
||||
print("Game Over!")
|
||||
world.show(force=True)
|
||||
|
||||
|
||||
async def main():
|
||||
import sys
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <port>")
|
||||
print(f"Example: python3 {sys.argv[0]} 8080")
|
||||
sys.exit(1)
|
||||
|
||||
port = int(sys.argv[1])
|
||||
print(f"AI backend running on port {port} ...")
|
||||
|
||||
try:
|
||||
await run_game_server(port, start_game, plan_next_actions, game_over)
|
||||
except Exception as e:
|
||||
print(f"Server Stopped: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
python3 pick_closets_flag.py ${CTF_PORT_BACKEND1}
|
||||
@@ -0,0 +1,177 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "21c8be3f-c57b-439c-8ca8-5991dd0da465",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 夺旗赛 Capture The Flag Test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0dfeabca-44d9-4906-abc6-cba1e4fc8a35",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 初始化 (使用上排目录栏的的 ▶️ 运行,▪️停止,⟳ 重启)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "dfde1bf2-03b7-4f8c-8eff-07f486bfe520",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import importlib\n",
|
||||
"import lib.game_engine\n",
|
||||
"\n",
|
||||
"# Force the reload manually\n",
|
||||
"importlib.reload(lib.game_engine)\n",
|
||||
"\n",
|
||||
"# Re-import the specific classes/functions\n",
|
||||
"from lib.game_engine import GameMap, run_game_server\n",
|
||||
"\n",
|
||||
"# Now initialize your objects\n",
|
||||
"world = GameMap()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "a3f08c96-ce25-43c5-97e6-955e6fc67746",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"def load_example_json():\n",
|
||||
" with open(\"example_init.json\", \"r\") as fin:\n",
|
||||
" init_data = json.load(fin)\n",
|
||||
" world.init(init_data)\n",
|
||||
"\n",
|
||||
" with open(\"example_plan_next_actions.json\", \"r\") as fin:\n",
|
||||
" status_data = json.load(fin)\n",
|
||||
" \n",
|
||||
" world.init(init_data)\n",
|
||||
" world.update(status_data)\n",
|
||||
" world.show()\n",
|
||||
" # world.show(flag_over_target=True, player_over_prison=True) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "b0eeca91-8a45-4c1f-8418-d5593968dd99",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n",
|
||||
" 0 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ \n",
|
||||
" 1 ██ LF . . . . . . . . ██ . . . . . R0 . RF ██ \n",
|
||||
" 2 ██ LF L0 . . . . . . . . . . . . . R1 . RF ██ \n",
|
||||
" 3 ██ LF L1 . . . . . . . . . . . . . R2 . RF ██ \n",
|
||||
" 4 ██ LF L2 . . . . . . . . ██ . . . . . . RF ██ \n",
|
||||
" 5 ██ LF . . . . . . . ██ . ██ . . . . . . RF ██ \n",
|
||||
" 6 ██ LF . . . . . . . . ██ . . . . ██ . . RF ██ \n",
|
||||
" 7 ██ . . . . . . . . . . . . . . . . . . ██ \n",
|
||||
" 8 ██ . . . . . . . . . . . . . . . . . . ██ \n",
|
||||
" 9 ██ TT TT TT . . . . . . . . . . . ██ TT TT TT ██ \n",
|
||||
"10 ██ TT TT TT . . . . . . . . . . . . TT TT TT ██ \n",
|
||||
"11 ██ TT TT TT . . . . . ██ . . . . ██ . TT TT TT ██ \n",
|
||||
"12 ██ . . . . . . . . ██ . . . ██ . . . . . ██ \n",
|
||||
"13 ██ . . . . . . . . . . . . . ██ . . . . ██ \n",
|
||||
"14 ██ . . . . . . . . . . ██ . . ██ . . . . ██ \n",
|
||||
"15 ██ . . . . . . . . . . ██ . . . . . . . ██ \n",
|
||||
"16 ██ PP PP PP . . . . . . . . . . . . PP PP PP ██ \n",
|
||||
"17 ██ PP PP PP . . . . . . . . . . . . PP PP PP ██ \n",
|
||||
"18 ██ PP PP PP . . . . . . ██ . . . . . PP PP PP ██ \n",
|
||||
"19 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"load_example_json()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "854f32c5-e6cd-420e-ac44-6bf3dfbee646",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"11067.9\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(world.current_time)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "e6bca36b-bb07-44c6-8b0a-d740987e5811",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1000.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(world.show_gap_in_msec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "a425410f-c733-4240-a032-0f852f7b8d39",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'width': 20, 'height': 20, 'middle_line': 10.0, 'walls': {(4, 0), (19, 0), (8, 0), (14, 13), (19, 9), (10, 6), (0, 5), (5, 19), (19, 18), (11, 5), (0, 14), (11, 14), (7, 19), (18, 19), (19, 2), (19, 11), (0, 7), (9, 19), (0, 16), (13, 19), (19, 4), (0, 0), (11, 0), (9, 12), (0, 9), (15, 0), (13, 12), (15, 9), (1, 19), (17, 0), (19, 6), (9, 5), (3, 19), (14, 19), (0, 2), (16, 19), (5, 0), (11, 4), (7, 0), (18, 0), (9, 0), (14, 14), (10, 1), (13, 0), (19, 13), (15, 6), (10, 19), (0, 18), (12, 19), (1, 0), (19, 15), (0, 11), (3, 0), (14, 0), (19, 8), (0, 4), (16, 0), (19, 17), (0, 13), (2, 19), (6, 19), (14, 11), (4, 19), (19, 1), (19, 10), (0, 6), (19, 19), (8, 19), (0, 15), (11, 15), (19, 3), (10, 0), (19, 12), (9, 11), (0, 8), (10, 18), (0, 17), (12, 0), (19, 5), (0, 1), (19, 14), (0, 10), (0, 19), (11, 19), (15, 19), (19, 7), (0, 3), (2, 0), (19, 16), (17, 19), (0, 12), (6, 0)}, 'players': [{'name': 'L0', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 2, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'L1', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 3, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'L2', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 4, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'R0', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 1, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}, {'name': 'R1', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 2, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}, {'name': 'R2', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 3, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}], 'flags': [{'canPickup': True, 'posX': 1, 'posY': 1, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 2, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 3, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 4, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 5, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 6, 'mine': True}, {'canPickup': True, 'posX': 18, 'posY': 1, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 2, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 3, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 4, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 5, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 6, 'mine': False}], 'current_time': 11067.9, 'next_show_time': 12067.9, 'my_team_name': 'L', 'show_gap_in_msec': 1000.0, 'my_team_prison': {(1, 18), (2, 17), (3, 17), (2, 16), (3, 16), (1, 17), (2, 18), (1, 16), (3, 18)}, 'opponent_team_prison': {(17, 17), (18, 17), (16, 16), (17, 16), (18, 16), (18, 18), (16, 18), (17, 18), (16, 17)}, 'my_team_target': {(1, 11), (2, 10), (3, 10), (2, 9), (3, 9), (1, 10), (2, 11), (1, 9), (3, 11)}, 'opponent_team_target': {(16, 10), (17, 10), (18, 10), (16, 9), (17, 9), (18, 9), (17, 11), (18, 11), (16, 11)}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# print all the variables in `world`\n",
|
||||
"print(world.__dict__)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
{
|
||||
"action": "init",
|
||||
"map": {
|
||||
"width": 20,
|
||||
"height": 20,
|
||||
"walls": [
|
||||
{
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 4,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 5,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 6,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 7,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 8,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 9,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 10,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 12,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 13,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 14,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 15,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 0
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 4,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 5,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 6,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 7,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 8,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 9,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 10,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 12,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 13,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 14,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 15,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 19
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 2
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 4
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 6
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 7
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 8
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 12
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 13
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 14
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 15
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 0,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 2
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 3
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 4
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 6
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 7
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 8
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 12
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 13
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 14
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 15
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 19,
|
||||
"y": 18
|
||||
}
|
||||
],
|
||||
"obstacles": [
|
||||
{
|
||||
"x": 10,
|
||||
"y": 6
|
||||
},
|
||||
{
|
||||
"x": 15,
|
||||
"y": 6
|
||||
},
|
||||
{
|
||||
"x": 9,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"x": 13,
|
||||
"y": 12
|
||||
},
|
||||
{
|
||||
"x": 15,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 10,
|
||||
"y": 1
|
||||
},
|
||||
{
|
||||
"x": 14,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 10,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 14,
|
||||
"y": 13
|
||||
},
|
||||
{
|
||||
"x": 9,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 4
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 14
|
||||
},
|
||||
{
|
||||
"x": 14,
|
||||
"y": 14
|
||||
},
|
||||
{
|
||||
"x": 9,
|
||||
"y": 12
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 5
|
||||
},
|
||||
{
|
||||
"x": 11,
|
||||
"y": 15
|
||||
}
|
||||
]
|
||||
},
|
||||
"numPlayers": 3,
|
||||
"numFlags": 6,
|
||||
"myteamName": "L",
|
||||
"myteamPrison": [
|
||||
{
|
||||
"x": 1,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 18
|
||||
}
|
||||
],
|
||||
"myteamTarget": [
|
||||
{
|
||||
"x": 1,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 1,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 2,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 3,
|
||||
"y": 11
|
||||
}
|
||||
],
|
||||
"opponentPrison": [
|
||||
{
|
||||
"x": 16,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 16
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 17
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 18
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 18
|
||||
}
|
||||
],
|
||||
"opponentTarget": [
|
||||
{
|
||||
"x": 16,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 9
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 10
|
||||
},
|
||||
{
|
||||
"x": 16,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 17,
|
||||
"y": 11
|
||||
},
|
||||
{
|
||||
"x": 18,
|
||||
"y": 11
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"action": "status",
|
||||
"time": 11067.9,
|
||||
"myteamPlayer": [
|
||||
{
|
||||
"name": "L0",
|
||||
"team": "L",
|
||||
"hasFlag": false,
|
||||
"posX": 2,
|
||||
"posY": 2,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
},
|
||||
{
|
||||
"name": "L1",
|
||||
"team": "L",
|
||||
"hasFlag": false,
|
||||
"posX": 2,
|
||||
"posY": 3,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
},
|
||||
{
|
||||
"name": "L2",
|
||||
"team": "L",
|
||||
"hasFlag": false,
|
||||
"posX": 2,
|
||||
"posY": 4,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
}
|
||||
],
|
||||
"myteamFlag": [
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 1
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 2
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 3
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 4
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 5
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 1,
|
||||
"posY": 6
|
||||
}
|
||||
],
|
||||
"myteamScore": 0,
|
||||
"opponentPlayer": [
|
||||
{
|
||||
"name": "R0",
|
||||
"team": "R",
|
||||
"hasFlag": false,
|
||||
"posX": 16,
|
||||
"posY": 1,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
},
|
||||
{
|
||||
"name": "R1",
|
||||
"team": "R",
|
||||
"hasFlag": false,
|
||||
"posX": 16,
|
||||
"posY": 2,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
},
|
||||
{
|
||||
"name": "R2",
|
||||
"team": "R",
|
||||
"hasFlag": false,
|
||||
"posX": 16,
|
||||
"posY": 3,
|
||||
"inPrison": false,
|
||||
"inPrisonTimeLeft": 0,
|
||||
"inPrisonDuration": 20000
|
||||
}
|
||||
],
|
||||
"opponentFlag": [
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 1
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 2
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 3
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 4
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 5
|
||||
},
|
||||
{
|
||||
"canPickup": true,
|
||||
"posX": 18,
|
||||
"posY": 6
|
||||
}
|
||||
],
|
||||
"opponentScore": 0
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,202 @@
|
||||
import asyncio
|
||||
from abc import ABC
|
||||
import collections
|
||||
import json
|
||||
import threading
|
||||
import random
|
||||
import websockets
|
||||
|
||||
from IPython.display import clear_output
|
||||
|
||||
class GameMap(ABC):
|
||||
def __init__(self, show_gap_in_msec = 1000.0):
|
||||
"""
|
||||
@show_gap: how many milliseconds when show can be invoked
|
||||
"""
|
||||
self.width = 0
|
||||
self.height = 0
|
||||
self.middle_line = self.width / 2
|
||||
self.walls = set()
|
||||
self.players = []
|
||||
self.flags = []
|
||||
self.current_time = 0.0
|
||||
self.next_show_time = -1.0
|
||||
self.my_team_name = ""
|
||||
self.show_gap_in_msec = show_gap_in_msec
|
||||
|
||||
def init(self, req):
|
||||
map_data = req["map"]
|
||||
self.current_time = 0.0
|
||||
self.next_show_time = -1.0
|
||||
self.width = map_data["width"]
|
||||
self.height = map_data["height"]
|
||||
self.middle_line = self.width / 2
|
||||
self.my_team_name = req.get("myteamName", "")
|
||||
|
||||
self.walls = {(w["x"], w["y"]) for w in (map_data.get("walls", []) + map_data.get("obstacles", []))}
|
||||
self.my_team_prison = {(w["x"], w["y"]) for w in (req.get("myteamPrison", []))}
|
||||
self.opponent_team_prison = {(w["x"], w["y"]) for w in (req.get("opponentPrison", []))}
|
||||
self.my_team_target = {(w["x"], w["y"]) for w in (req.get("myteamTarget", []))}
|
||||
self.opponent_team_target = {(w["x"], w["y"]) for w in (req.get("opponentTarget", []))}
|
||||
|
||||
|
||||
def update(self, req):
|
||||
if req["time"] < self.current_time:
|
||||
return False
|
||||
self.current_time = req["time"]
|
||||
self.players = []
|
||||
# Combine and tag players
|
||||
for p in req.get("myteamPlayer", []):
|
||||
p['mine'] = True
|
||||
self.players.append(p)
|
||||
for p in req.get("opponentPlayer", []):
|
||||
p['mine'] = False
|
||||
self.players.append(p)
|
||||
|
||||
self.flags = []
|
||||
for f in req.get("myteamFlag", []):
|
||||
f['mine'] = True
|
||||
self.flags.append(f)
|
||||
for f in req.get("opponentFlag", []):
|
||||
f['mine'] = False
|
||||
self.flags.append(f)
|
||||
return True
|
||||
|
||||
def list_players(self, mine, inPrison, hasFlag):
|
||||
"""
|
||||
mine: True or False. If True, return players on my side, otherwise return opponent players;
|
||||
inPrison: True or False or None. If True, return players that are in prison; if false, return players that can move around freely; if none, return all of them.
|
||||
hasFlag: True or False or None. If True, return players that have flags; if false, return players that do not have flags; if none, return all of them.
|
||||
"""
|
||||
return [p for p in self.players if p['mine'] == mine and (inPrison == None or p["inPrison"] == inPrison) and (hasFlag == None or p["hasFlag"] == hasFlag)]
|
||||
|
||||
def list_flags(self, mine, canPickup):
|
||||
"""
|
||||
mine: True or False. If True, return flags on my side (i.e., flags I should protect), otherwise return opponent's flags (i.e., flags I should pick up);
|
||||
canPickup: True or False or None. If True, return flags that can be picked up; if false, return flags that are already placed in my camp; if none, return all of them.
|
||||
"""
|
||||
|
||||
return [f for f in self.flags if f['mine'] == mine and (canPickup == None or f["canPickup"] == canPickup)]
|
||||
|
||||
def list_targets(self, mine):
|
||||
if mine:
|
||||
return self.my_team_target
|
||||
else:
|
||||
return self.opponent_team_target
|
||||
|
||||
def list_prisons(self, mine):
|
||||
if mine:
|
||||
return self.my_team_prison
|
||||
else:
|
||||
return self.opponent_team_prison
|
||||
|
||||
def get_object_at_XY(self, x, y, flag_over_target=False, player_over_prison=False):
|
||||
"""
|
||||
flags could overlap with targets, and players could overlap with prisons.
|
||||
These are controlled by @flag_over_target and @player_over_prison.
|
||||
"""
|
||||
if (x, y) in self.walls: return "██ "
|
||||
if not player_over_prison:
|
||||
if (x, y) in self.my_team_prison: return "PP "
|
||||
if (x, y) in self.opponent_team_prison: return "PP "
|
||||
if not flag_over_target:
|
||||
if (x, y) in self.my_team_target: return "TT "
|
||||
if (x, y) in self.opponent_team_target: return "TT "
|
||||
|
||||
# Check Players
|
||||
for p in self.players:
|
||||
if p["posX"] == x and p["posY"] == y:
|
||||
return p["name"] + " "
|
||||
|
||||
# Check Flags
|
||||
for f in self.flags:
|
||||
if f["posX"] == x and f["posY"] == y:
|
||||
if f['mine']:
|
||||
team = self.my_team_name
|
||||
else:
|
||||
team = "R" if self.my_team_name == "L" else "L"
|
||||
return f"{team}F "
|
||||
|
||||
if player_over_prison:
|
||||
if (x, y) in self.my_team_prison: return "PP "
|
||||
if (x, y) in self.opponent_team_prison: return "PP "
|
||||
if flag_over_target:
|
||||
if (x, y) in self.my_team_target: return "TT "
|
||||
if (x, y) in self.opponent_team_target: return "TT "
|
||||
|
||||
return " . "
|
||||
|
||||
def show(self, force=False, do_not_clear=False, flag_over_target=False, player_over_prison=False):
|
||||
"""
|
||||
Prints the grid with L1, R2, LF, RF labels.
|
||||
flags could overlap with targets, and players could overlap with prisons.
|
||||
These are controlled by @flag_over_target and @player_over_prison.
|
||||
"""
|
||||
if self.current_time < self.next_show_time and (not force):
|
||||
return
|
||||
if not do_not_clear:
|
||||
clear_output()
|
||||
header = " " + " ".join([f"{x:2}" for x in range(self.width)])
|
||||
print(header)
|
||||
for y in range(self.height):
|
||||
row = f"{y:2} "
|
||||
for x in range(self.width):
|
||||
row += self.get_object_at_XY(x, y, flag_over_target, player_over_prison)
|
||||
print(row)
|
||||
self.next_show_time = self.current_time + self.show_gap_in_msec
|
||||
|
||||
def route_to(self, srcXY, dstXY, extra_obstacles=None):
|
||||
extras = set(extra_obstacles) if extra_obstacles else set()
|
||||
queue = collections.deque([[srcXY]])
|
||||
seen = {srcXY}
|
||||
|
||||
while queue:
|
||||
path = queue.popleft()
|
||||
curr = path[-1]
|
||||
if curr == dstXY:
|
||||
return path
|
||||
|
||||
for dx, dy in [(0, -1), (0, 1), (-1, 0), (1, 0)]: # Up, Down, Left, Right
|
||||
nxt = (curr[0] + dx, curr[1] + dy)
|
||||
if (0 <= nxt[0] < self.width and 0 <= nxt[1] < self.height and
|
||||
nxt not in self.walls and nxt not in extras and nxt not in seen):
|
||||
queue.append(path + [nxt])
|
||||
seen.add(nxt)
|
||||
return []
|
||||
|
||||
def is_on_left(self, srcXY):
|
||||
return srcXY[0] < self.middle_line
|
||||
|
||||
@staticmethod
|
||||
def get_direction(currentXY, nextXY):
|
||||
"""Helper to convert two points into a direction string."""
|
||||
dx = nextXY[0] - currentXY[0]
|
||||
dy = nextXY[1] - currentXY[1]
|
||||
if dx == 1: return "right"
|
||||
if dx == -1: return "left"
|
||||
if dy == 1: return "down"
|
||||
if dy == -1: return "up"
|
||||
return ""
|
||||
|
||||
|
||||
async def run_game_server(port, start_fn, plan_fn, end_fn):
|
||||
lock = threading.Lock()
|
||||
async def handler(websocket):
|
||||
print(f"Connected on port {port}")
|
||||
async for msg in websocket:
|
||||
req = json.loads(msg)
|
||||
action = req.get("action")
|
||||
if action == "init":
|
||||
with lock:
|
||||
start_fn(req)
|
||||
elif action == "status":
|
||||
with lock:
|
||||
moves = plan_fn(req)
|
||||
await websocket.send(json.dumps({"players": moves}))
|
||||
elif action == "finished":
|
||||
with lock:
|
||||
end_fn(req)
|
||||
|
||||
print(f"Starting server on port {port}...")
|
||||
async with websockets.serve(handler, "0.0.0.0", port):
|
||||
await asyncio.Future()
|
||||
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "21c8be3f-c57b-439c-8ca8-5991dd0da465",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 夺旗赛 Capture The Flag"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0dfeabca-44d9-4906-abc6-cba1e4fc8a35",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 初始化 (使用上排目录栏的的 ▶️ 运行,▪️停止, ⟳ 重启)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "098c5853-135d-4eb8-ab51-bbf660fc09e9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import importlib\n",
|
||||
"import lib.game_engine\n",
|
||||
"\n",
|
||||
"# Force the reload manually\n",
|
||||
"importlib.reload(lib.game_engine)\n",
|
||||
"\n",
|
||||
"# Re-import the specific classes/functions\n",
|
||||
"from lib.game_engine import GameMap, run_game_server\n",
|
||||
"\n",
|
||||
"# Now initialize your objects\n",
|
||||
"world = GameMap()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e9437ef2-777a-4694-b1b2-428e654be4ab",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython.display import clear_output\n",
|
||||
"import os\n",
|
||||
"import json\n",
|
||||
"import random"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a1d9d9f6-3644-41bf-a60d-0148a74140c4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 以下是你要编写的代码。 \n",
|
||||
"- start_game:初始化游戏。\n",
|
||||
"- game_over:游戏结束。\n",
|
||||
"- plan_next_actions:每一时刻,告诉你目前小人们的位置。\n",
|
||||
" \n",
|
||||
"每次代码更新,一定要使用上排目录栏的的 ▶️ 运行"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "021ea24a-9a8f-4a4d-a086-f3b80b882c95",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是你要编写的策略\n",
|
||||
"def start_game(req):\n",
|
||||
" \"\"\"Called when the game begins.\"\"\"\n",
|
||||
" world.init(req)\n",
|
||||
" print(f\"Map initialized: {world.width}x{world.height}\")\n",
|
||||
"\n",
|
||||
"def game_over(req):\n",
|
||||
" \"\"\"Called when the game ends.\"\"\"\n",
|
||||
" print(\"Game Over!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "22e04cb6-87cb-486d-a580-a1ae5fbe3a98",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是你要编写的策略。以下always_move_right和walk_to_first_flag_and_return是两个例子\n",
|
||||
"def plan_next_actions(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" world.update(req) \n",
|
||||
" world.show() \n",
|
||||
" actions = dict()\n",
|
||||
" # TODO:请在这里写下你的代码来控制小人\n",
|
||||
" \n",
|
||||
" return actions\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "a8561aec-4246-40dc-b833-1d2b8991edd3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是一个超简单策略:即把所有的小人都向右移动\n",
|
||||
"## Python小测试 - 将以下程序改成:如果是L team,就往右走;如果是R team,就往左走。\n",
|
||||
"def always_move_right(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" if not world.update(req):\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" world.show() \n",
|
||||
"\n",
|
||||
" # List all players that can move freely (set `hasFlag=True`)\n",
|
||||
" my_players = world.list_players(mine=True, inPrison=False, hasFlag=None)\n",
|
||||
"\n",
|
||||
" actions = dict()\n",
|
||||
" for p in my_players:\n",
|
||||
" actions[p[\"name\"]] = \"right\"\n",
|
||||
"\n",
|
||||
" return actions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "e4b51cda-514b-4264-b8af-53acc4a76120",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## 这是一个超简单策略:所有小人都向着第一个小旗子走\n",
|
||||
"## Python小测试 - 将以下程序改成:如果在对方的领地里,将对方的player位置设为extra_obstacles\n",
|
||||
"\n",
|
||||
"def walk_to_first_flag_and_return(req):\n",
|
||||
" \"\"\"\n",
|
||||
" Called every tick. \n",
|
||||
" Return a dictionary: {\"playerName\": \"direction\"}\n",
|
||||
" direction is \"up\", \"down\", \"right\", \"left, \"\" . \"\" means the player should stand still.\n",
|
||||
" \"\"\"\n",
|
||||
" if not world.update(req):\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # world.show() always show targets and prisons, regardless whether flags and players are not there or not\n",
|
||||
" world.show(flag_over_target=True, player_over_prison=True) \n",
|
||||
"\n",
|
||||
" # List all players that can move freely (set `hasFlag=True`)\n",
|
||||
" my_players_go = world.list_players(mine=True, inPrison=False, hasFlag=False)\n",
|
||||
" my_players_return = world.list_players(mine=True, inPrison=False, hasFlag=True)\n",
|
||||
" # List a\n",
|
||||
" opponents = world.list_players(mine=False, inPrison=False, hasFlag=None)\n",
|
||||
" enemy_flags = world.list_flags(mine=False, canPickup=None)\n",
|
||||
" \n",
|
||||
" actions = {}\n",
|
||||
" \n",
|
||||
" # Everyone wo/ a flag rushes the first flag\n",
|
||||
" if enemy_flags:\n",
|
||||
" target_flag = enemy_flags[0]\n",
|
||||
" dest = (target_flag[\"posX\"], target_flag[\"posY\"])\n",
|
||||
" for p in my_players_go:\n",
|
||||
" start = (p[\"posX\"], p[\"posY\"])\n",
|
||||
" path = world.route_to(start, dest, extra_obstacles=[])\n",
|
||||
" \n",
|
||||
" if len(path) > 1:\n",
|
||||
" # Convert the next coordinate in path to a direction string\n",
|
||||
" next_step = path[1]\n",
|
||||
" actions[p[\"name\"]] = GameMap.get_direction(start, next_step)\n",
|
||||
" # Everyone w/ a flag returns the target zone\n",
|
||||
" target_zone = list(world.list_targets(mine=True))[0]\n",
|
||||
" for p in my_players_return:\n",
|
||||
" start = (p[\"posX\"], p[\"posY\"])\n",
|
||||
" path = world.route_to(start, target_zone, extra_obstacles=[])\n",
|
||||
" if len(path) > 1:\n",
|
||||
" # Convert the next coordinate in path to a direction string\n",
|
||||
" next_step = path[1]\n",
|
||||
" actions[p[\"name\"]] = GameMap.get_direction(start, next_step)\n",
|
||||
" return actions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "322772ab-a0b8-4da3-906f-212926ad24fb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 运行以下cell启动你的server (使用上排目录栏的的 ▶️ 运行,使用▪️停止)\n",
|
||||
"[*] 表示cell正在运行中"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e0898ab6-bd6e-4bf1-9a29-d802933dd91a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Change the port to match your game settings\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"PORT_ID = 2 # 或者 2\n",
|
||||
"PORT = \"CTF_PORT_BACKEND\" + str(PORT_ID)\n",
|
||||
"\n",
|
||||
"try:\n",
|
||||
" PORT_VALUE = os.environ[PORT]\n",
|
||||
" print(f\"PORT: {PORT_VALUE}\")\n",
|
||||
"except KeyError:\n",
|
||||
" print(\"Error: PORT environment variable not set.\")\n",
|
||||
"try:\n",
|
||||
" # 将`plan_fn=`改成你的plan_next_actions,如always_move_right, walk_to_first_flag_and_return 等等\n",
|
||||
" await run_game_server(PORT_VALUE, start_fn=start_game, plan_fn=walk_to_first_flag_and_return, end_fn=game_over)\n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"Server stopped: {e}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b869dde3-cf94-4d25-8fe8-25096bab1f37",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import asyncio
|
||||
import json
|
||||
import random
|
||||
import traceback
|
||||
import websockets
|
||||
|
||||
ACTIONS = ["up", "down", "left", "right", ""]
|
||||
|
||||
class GameMap:
|
||||
|
||||
EMPTY = 0
|
||||
OBSTACLE = -1
|
||||
FLAG = 2
|
||||
|
||||
ACTIONS = ["up", "down", "left", "right", ""]
|
||||
ACTIONS_IN_MOVE = [(0, -1), (0, 1), (-1, 0), (1, 0)]
|
||||
|
||||
def __init__(self, map_json):
|
||||
self.w = map_json["width"]
|
||||
self.h = map_json["height"]
|
||||
self.grids = [[GameMap.EMPTY for _ in range(0, self.h)] for _ in range(0, self.w)]
|
||||
self.obstacles = map_json["walls"] + map_json["obstacles"]
|
||||
for o in self.obstacles:
|
||||
self.grids[o["x"]][o["y"]] = GameMap.OBSTACLE
|
||||
|
||||
def show_map(self):
|
||||
for y in range(0, self.h):
|
||||
for x in range(0, self.w):
|
||||
print(self.grids[x][y], end=" ")
|
||||
print("")
|
||||
|
||||
|
||||
def find_closest_goal_from_pos(self, pos_x, pos_y, goals, blockers):
|
||||
"""
|
||||
Find the closest goal(@goal_x, @goal_y) in @goals from (@pos_x, @pos_y).
|
||||
Return the moving direction for (@pos_x, @pos_y).
|
||||
If none is reachable, return "".
|
||||
"""
|
||||
goals_pos = {(g["x"], g["y"]) for g in goals}
|
||||
blocker_pos = set()
|
||||
for (bx, by) in blockers:
|
||||
for (dx, dy) in GameMap.ACTIONS_IN_MOVE:
|
||||
x = bx + dx
|
||||
y = by + dy
|
||||
if (x, y) not in goals_pos:
|
||||
blocker_pos.add((bx, by))
|
||||
|
||||
# visited != -1 means the grid is reached from (pos_x, pos_y)
|
||||
# it stores the previous grid's direction to reach the current grid
|
||||
visited = [[-1 for _ in range(0, self.h)] for _ in range(0, self.w)]
|
||||
bfs = [(pos_x, pos_y)]
|
||||
st = 0
|
||||
goal_x = -1
|
||||
goal_y = -1
|
||||
while st < len(bfs):
|
||||
for d, (dx, dy) in enumerate(GameMap.ACTIONS_IN_MOVE):
|
||||
x = bfs[st][0] + dx
|
||||
y = bfs[st][1] + dy
|
||||
if (self.grids[x][y] != GameMap.OBSTACLE and
|
||||
((x, y) not in blocker_pos) and
|
||||
visited[x][y] < 0):
|
||||
visited[x][y] = d
|
||||
bfs.append((x, y))
|
||||
if (x, y) in goals_pos:
|
||||
goal_x = x
|
||||
goal_y = y
|
||||
break
|
||||
st = st + 1
|
||||
|
||||
# we need to find the very first direction taken by (pos_x, pos_y) to reach (goal_x, goal_y)
|
||||
if goal_x < 0 or goal_y < 0:
|
||||
return GameMap.ACTIONS[-1]
|
||||
cur_x = goal_x
|
||||
cur_y = goal_y
|
||||
first_direction = -1
|
||||
while cur_x != pos_x or cur_y != pos_y:
|
||||
first_direction = visited[cur_x][cur_y]
|
||||
cur_x = cur_x - GameMap.ACTIONS_IN_MOVE[first_direction][0]
|
||||
cur_y = cur_y - GameMap.ACTIONS_IN_MOVE[first_direction][1]
|
||||
|
||||
return GameMap.ACTIONS[first_direction]
|
||||
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
self.map = None
|
||||
self.team_name = None
|
||||
self.team_target = None
|
||||
self.num_flags = 0
|
||||
self.num_players = 0
|
||||
self.game_started = False
|
||||
self.player_to_flag_assignments = None
|
||||
|
||||
def startGame(self, game_json):
|
||||
self.map = GameMap(game_json["map"])
|
||||
self.team_name = game_json["myteamName"]
|
||||
self.team_target = game_json["myteamTarget"]
|
||||
self.num_flags = game_json["numFlags"]
|
||||
self.num_players = game_json["numPlayers"]
|
||||
self.game_started = True
|
||||
self.middle_line = self.map.w / 2;
|
||||
self.my_team_on_the_left = self.team_target[0]['x'] < self.middle_line;
|
||||
# playerName -> (flagX, flagY)
|
||||
self.player_to_flag_assignments = dict()
|
||||
|
||||
def endGame(self, game_json):
|
||||
self.map = None
|
||||
self.team_name = None
|
||||
self.team_target = None
|
||||
self.num_flags = 0
|
||||
self.num_players = 0
|
||||
self.game_started = False
|
||||
self.player_to_flag_assignments = None
|
||||
|
||||
def is_player_safe(self, player):
|
||||
return (player["posX"] < self.middle_line) == self.my_team_on_the_left
|
||||
|
||||
|
||||
def assign_flags_to_players(self, players, flags):
|
||||
"""
|
||||
assign all pickable flags to all eligible players (i.e., !hasFlag and !inPrison)
|
||||
"""
|
||||
# remove the prison player and player with flags
|
||||
for p in players:
|
||||
if (p["hasFlag"] or p["inPrison"]) and p["name"] in self.player_to_flag_assignments:
|
||||
del self.player_to_flag_assignments[p["name"]]
|
||||
pickable_flags = {
|
||||
(f["posX"], f["posY"]): False for f in flags if f["canPickup"]
|
||||
}
|
||||
players_wo_flag = set([p["name"] for p in players if (not p["hasFlag"]) and (not p["inPrison"])])
|
||||
for p, f in self.player_to_flag_assignments.items():
|
||||
if f in pickable_flags:
|
||||
pickable_flags[f] = True
|
||||
players_wo_flag.remove(p)
|
||||
|
||||
# randomly match unassigned flags and players
|
||||
flags_wo_player = [f for (f, m) in pickable_flags.items() if not m]
|
||||
if len(flags_wo_player) > 0:
|
||||
for i, p in enumerate(players_wo_flag):
|
||||
self.player_to_flag_assignments[p] = flags_wo_player[i % len(flags_wo_player)]
|
||||
elif len(pickable_flags) > 0:
|
||||
pickable_flags_list = list(pickable_flags)
|
||||
for i, p in enumerate(players_wo_flag):
|
||||
self.player_to_flag_assignments[p] = random.choice(pickable_flags_list)
|
||||
|
||||
|
||||
def find_next_move(self, player, opponents):
|
||||
if player["inPrison"]:
|
||||
return ""
|
||||
|
||||
blockers = [] if self.is_player_safe(player) else [(o["posX"], o["posY"]) for o in opponents]
|
||||
if player["hasFlag"]:
|
||||
return self.map.find_closest_goal_from_pos(
|
||||
player["posX"], player["posY"],
|
||||
[GAME.team_target[0]],
|
||||
blockers)
|
||||
|
||||
if player["name"] not in self.player_to_flag_assignments:
|
||||
return ""
|
||||
|
||||
flag = self.player_to_flag_assignments[player["name"]]
|
||||
return self.map.find_closest_goal_from_pos(
|
||||
player["posX"], player["posY"],
|
||||
[{"x": flag[0], "y": flag[1]}],
|
||||
blockers
|
||||
)
|
||||
|
||||
|
||||
# SINGLETON
|
||||
GAME = Game()
|
||||
|
||||
async def startGame(req):
|
||||
print("Start Game")
|
||||
|
||||
global GAME
|
||||
GAME.startGame(req)
|
||||
|
||||
|
||||
async def planNextActions(req, websocket):
|
||||
global GAME
|
||||
|
||||
player_moves = dict()
|
||||
players = req.get("myteamPlayer", [])
|
||||
opponents = req.get("opponentPlayer", [])
|
||||
flags = req.get("opponentFlag", [])
|
||||
GAME.assign_flags_to_players(players, flags)
|
||||
|
||||
for p in players:
|
||||
action = GAME.find_next_move(p, opponents)
|
||||
if action != "":
|
||||
player_moves[p["name"]] = action
|
||||
|
||||
result = {"players": player_moves}
|
||||
await websocket.send(json.dumps(result))
|
||||
|
||||
|
||||
async def gameOver(req):
|
||||
global GAME
|
||||
GAME.endGame(req)
|
||||
|
||||
|
||||
async def handle_client(websocket):
|
||||
print("New session started")
|
||||
|
||||
try:
|
||||
async for msg in websocket:
|
||||
try:
|
||||
req = json.loads(msg)
|
||||
|
||||
if req.get("action") == "status":
|
||||
await planNextActions(req, websocket)
|
||||
|
||||
elif req.get("action") == "init":
|
||||
await startGame(req)
|
||||
|
||||
elif req.get("action") == "finished":
|
||||
await gameOver(req)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
print("JSON parse error")
|
||||
await websocket.send(json.dumps({"error": "invalid json"}))
|
||||
|
||||
except websockets.exceptions.ConnectionClosedOK:
|
||||
print("Client closed connection normally")
|
||||
except websockets.exceptions.ConnectionClosedError as e:
|
||||
print(f"Connection error: {e}")
|
||||
except Exception as e:
|
||||
print(f"Exception: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
async def main():
|
||||
import sys
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <port>")
|
||||
print(f"Example: python3 {sys.argv[0]} 8080")
|
||||
return
|
||||
|
||||
port = int(sys.argv[1])
|
||||
print(f"AI backend running on port {port} ...")
|
||||
|
||||
async with websockets.serve(handle_client, "0.0.0.0", port):
|
||||
await asyncio.Future() # run forever
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,103 @@
|
||||
import asyncio
|
||||
import random
|
||||
from lib.game_engine import GameMap, run_game_server
|
||||
import threading
|
||||
|
||||
|
||||
# 1. Initialize the global world model
|
||||
world = GameMap(show_gap_in_msec=1000.0)
|
||||
lock = threading.Lock()
|
||||
last_updated_time = -1
|
||||
update_threshold = 100
|
||||
player_to_flag_assignments = {}
|
||||
|
||||
def start_game(req):
|
||||
global player_to_flag_assignments
|
||||
world.init(req)
|
||||
print("Start Game!!")
|
||||
player_to_flag_assignments = {}
|
||||
print(f"Game Started! Side: {'Left' if world.is_on_left(list(world.my_team_target)[0]) else 'Right'}")
|
||||
|
||||
def plan_next_actions(req):
|
||||
if not world.update(req):
|
||||
return
|
||||
|
||||
global player_to_flag_assignments
|
||||
|
||||
# Render the map
|
||||
# world.show(do_not_clear=False)
|
||||
|
||||
my_players = world.list_players(mine=True, inPrison=False, hasFlag=None)
|
||||
opponents = world.list_players(mine=False, inPrison=False, hasFlag=None)
|
||||
enemy_flags = world.list_flags(mine=False, canPickup=True)
|
||||
my_targets = list(world.list_targets(mine=True))
|
||||
|
||||
# 2. Logic: Assign flags to players without flags
|
||||
# We maintain the original logic of matching players to specific flag coordinates
|
||||
active_player_names = {p["name"] for p in my_players if not p["hasFlag"]}
|
||||
|
||||
# Cleanup assignments for players captured or flags already taken
|
||||
player_to_flag_assignments = {
|
||||
name: pos for name, pos in player_to_flag_assignments.items()
|
||||
if name in active_player_names
|
||||
}
|
||||
|
||||
if enemy_flags:
|
||||
for p in my_players:
|
||||
if not p["hasFlag"] and p["name"] not in player_to_flag_assignments:
|
||||
# Randomly assign one of the available enemy flags
|
||||
f = random.choice(enemy_flags)
|
||||
player_to_flag_assignments[p["name"]] = (f["posX"], f["posY"])
|
||||
|
||||
# 3. Plan moves for each player
|
||||
player_moves = {}
|
||||
my_side_is_left = world.is_on_left(my_targets[0])
|
||||
|
||||
for p in my_players:
|
||||
curr_pos = (p["posX"], p["posY"])
|
||||
|
||||
# Determine Target: Either the assigned flag or the home target
|
||||
if p["hasFlag"]:
|
||||
dest = my_targets[0]
|
||||
elif p["name"] in player_to_flag_assignments:
|
||||
dest = player_to_flag_assignments[p["name"]]
|
||||
else:
|
||||
continue
|
||||
|
||||
# Determine Obstacles: Avoid opponents if we are in enemy territory
|
||||
is_safe = world.is_on_left(curr_pos) == my_side_is_left
|
||||
blockers = [] if is_safe else [(o["posX"], o["posY"]) for o in opponents]
|
||||
|
||||
# Calculate Path
|
||||
path = world.route_to(curr_pos, dest, extra_obstacles=blockers)
|
||||
|
||||
if len(path) > 1:
|
||||
move = world.get_direction(curr_pos, path[1])
|
||||
player_moves[p["name"]] = move
|
||||
|
||||
return player_moves
|
||||
|
||||
def game_over(req):
|
||||
print("Game Over!")
|
||||
world.show(force=True)
|
||||
|
||||
|
||||
async def main():
|
||||
import sys
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <port>")
|
||||
print(f"Example: python3 {sys.argv[0]} 8080")
|
||||
sys.exit(1)
|
||||
|
||||
port = int(sys.argv[1])
|
||||
print(f"AI backend running on port {port} ...")
|
||||
|
||||
try:
|
||||
await run_game_server(port, start_game, plan_next_actions, game_over)
|
||||
except Exception as e:
|
||||
print(f"Server Stopped: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
python3 pick_closets_flag.py ${CTF_PORT_BACKEND1}
|
||||
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "21c8be3f-c57b-439c-8ca8-5991dd0da465",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 夺旗赛 Capture The Flag Test"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0dfeabca-44d9-4906-abc6-cba1e4fc8a35",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## 初始化 (使用上排目录栏的的 ▶️ 运行,▪️停止,⟳ 重启)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "dfde1bf2-03b7-4f8c-8eff-07f486bfe520",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import importlib\n",
|
||||
"import lib.game_engine\n",
|
||||
"\n",
|
||||
"# Force the reload manually\n",
|
||||
"importlib.reload(lib.game_engine)\n",
|
||||
"\n",
|
||||
"# Re-import the specific classes/functions\n",
|
||||
"from lib.game_engine import GameMap, run_game_server\n",
|
||||
"\n",
|
||||
"# Now initialize your objects\n",
|
||||
"world = GameMap()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "a3f08c96-ce25-43c5-97e6-955e6fc67746",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"def load_example_json():\n",
|
||||
" with open(\"example_init.json\", \"r\") as fin:\n",
|
||||
" init_data = json.load(fin)\n",
|
||||
" world.init(init_data)\n",
|
||||
"\n",
|
||||
" with open(\"example_plan_next_actions.json\", \"r\") as fin:\n",
|
||||
" status_data = json.load(fin)\n",
|
||||
" \n",
|
||||
" world.init(init_data)\n",
|
||||
" world.update(status_data)\n",
|
||||
" world.show()\n",
|
||||
" # world.show(flag_over_target=True, player_over_prison=True) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "b0eeca91-8a45-4c1f-8418-d5593968dd99",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19\n",
|
||||
" 0 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ \n",
|
||||
" 1 ██ LF . . . . . . . . ██ . . . . . R0 . RF ██ \n",
|
||||
" 2 ██ LF L0 . . . . . . . . . . . . . R1 . RF ██ \n",
|
||||
" 3 ██ LF L1 . . . . . . . . . . . . . R2 . RF ██ \n",
|
||||
" 4 ██ LF L2 . . . . . . . . ██ . . . . . . RF ██ \n",
|
||||
" 5 ██ LF . . . . . . . ██ . ██ . . . . . . RF ██ \n",
|
||||
" 6 ██ LF . . . . . . . . ██ . . . . ██ . . RF ██ \n",
|
||||
" 7 ██ . . . . . . . . . . . . . . . . . . ██ \n",
|
||||
" 8 ██ . . . . . . . . . . . . . . . . . . ██ \n",
|
||||
" 9 ██ TT TT TT . . . . . . . . . . . ██ TT TT TT ██ \n",
|
||||
"10 ██ TT TT TT . . . . . . . . . . . . TT TT TT ██ \n",
|
||||
"11 ██ TT TT TT . . . . . ██ . . . . ██ . TT TT TT ██ \n",
|
||||
"12 ██ . . . . . . . . ██ . . . ██ . . . . . ██ \n",
|
||||
"13 ██ . . . . . . . . . . . . . ██ . . . . ██ \n",
|
||||
"14 ██ . . . . . . . . . . ██ . . ██ . . . . ██ \n",
|
||||
"15 ██ . . . . . . . . . . ██ . . . . . . . ██ \n",
|
||||
"16 ██ PP PP PP . . . . . . . . . . . . PP PP PP ██ \n",
|
||||
"17 ██ PP PP PP . . . . . . . . . . . . PP PP PP ██ \n",
|
||||
"18 ██ PP PP PP . . . . . . ██ . . . . . PP PP PP ██ \n",
|
||||
"19 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ \n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"load_example_json()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "854f32c5-e6cd-420e-ac44-6bf3dfbee646",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"11067.9\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(world.current_time)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "e6bca36b-bb07-44c6-8b0a-d740987e5811",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"1000.0\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(world.show_gap_in_msec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "a425410f-c733-4240-a032-0f852f7b8d39",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"{'width': 20, 'height': 20, 'middle_line': 10.0, 'walls': {(4, 0), (19, 0), (8, 0), (14, 13), (19, 9), (10, 6), (0, 5), (5, 19), (19, 18), (11, 5), (0, 14), (11, 14), (7, 19), (18, 19), (19, 2), (19, 11), (0, 7), (9, 19), (0, 16), (13, 19), (19, 4), (0, 0), (11, 0), (9, 12), (0, 9), (15, 0), (13, 12), (15, 9), (1, 19), (17, 0), (19, 6), (9, 5), (3, 19), (14, 19), (0, 2), (16, 19), (5, 0), (11, 4), (7, 0), (18, 0), (9, 0), (14, 14), (10, 1), (13, 0), (19, 13), (15, 6), (10, 19), (0, 18), (12, 19), (1, 0), (19, 15), (0, 11), (3, 0), (14, 0), (19, 8), (0, 4), (16, 0), (19, 17), (0, 13), (2, 19), (6, 19), (14, 11), (4, 19), (19, 1), (19, 10), (0, 6), (19, 19), (8, 19), (0, 15), (11, 15), (19, 3), (10, 0), (19, 12), (9, 11), (0, 8), (10, 18), (0, 17), (12, 0), (19, 5), (0, 1), (19, 14), (0, 10), (0, 19), (11, 19), (15, 19), (19, 7), (0, 3), (2, 0), (19, 16), (17, 19), (0, 12), (6, 0)}, 'players': [{'name': 'L0', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 2, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'L1', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 3, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'L2', 'team': 'L', 'hasFlag': False, 'posX': 2, 'posY': 4, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': True}, {'name': 'R0', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 1, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}, {'name': 'R1', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 2, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}, {'name': 'R2', 'team': 'R', 'hasFlag': False, 'posX': 16, 'posY': 3, 'inPrison': False, 'inPrisonTimeLeft': 0, 'inPrisonDuration': 20000, 'mine': False}], 'flags': [{'canPickup': True, 'posX': 1, 'posY': 1, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 2, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 3, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 4, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 5, 'mine': True}, {'canPickup': True, 'posX': 1, 'posY': 6, 'mine': True}, {'canPickup': True, 'posX': 18, 'posY': 1, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 2, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 3, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 4, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 5, 'mine': False}, {'canPickup': True, 'posX': 18, 'posY': 6, 'mine': False}], 'current_time': 11067.9, 'next_show_time': 12067.9, 'my_team_name': 'L', 'show_gap_in_msec': 1000.0, 'my_team_prison': {(1, 18), (2, 17), (3, 17), (2, 16), (3, 16), (1, 17), (2, 18), (1, 16), (3, 18)}, 'opponent_team_prison': {(17, 17), (18, 17), (16, 16), (17, 16), (18, 16), (18, 18), (16, 18), (17, 18), (16, 17)}, 'my_team_target': {(1, 11), (2, 10), (3, 10), (2, 9), (3, 9), (1, 10), (2, 11), (1, 9), (3, 11)}, 'opponent_team_target': {(16, 10), (17, 10), (18, 10), (16, 9), (17, 9), (18, 9), (17, 11), (18, 11), (16, 11)}}\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# print all the variables in `world`\n",
|
||||
"print(world.__dict__)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "010b21df-067a-4576-aedc-a3b6b8045da1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.13.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user