kaggle-environments 0.2.1__py3-none-any.whl → 1.20.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaggle-environments might be problematic. Click here for more details.
- kaggle_environments/__init__.py +49 -13
- kaggle_environments/agent.py +177 -124
- kaggle_environments/api.py +31 -0
- kaggle_environments/core.py +295 -170
- kaggle_environments/envs/cabt/cabt.js +164 -0
- kaggle_environments/envs/cabt/cabt.json +28 -0
- kaggle_environments/envs/cabt/cabt.py +186 -0
- kaggle_environments/envs/cabt/cg/__init__.py +0 -0
- kaggle_environments/envs/cabt/cg/cg.dll +0 -0
- kaggle_environments/envs/cabt/cg/game.py +75 -0
- kaggle_environments/envs/cabt/cg/libcg.so +0 -0
- kaggle_environments/envs/cabt/cg/sim.py +48 -0
- kaggle_environments/envs/cabt/test_cabt.py +120 -0
- kaggle_environments/envs/chess/chess.js +4289 -0
- kaggle_environments/envs/chess/chess.json +60 -0
- kaggle_environments/envs/chess/chess.py +4241 -0
- kaggle_environments/envs/chess/test_chess.py +60 -0
- kaggle_environments/envs/connectx/connectx.ipynb +3186 -0
- kaggle_environments/envs/connectx/connectx.js +1 -1
- kaggle_environments/envs/connectx/connectx.json +15 -1
- kaggle_environments/envs/connectx/connectx.py +6 -23
- kaggle_environments/envs/connectx/test_connectx.py +70 -24
- kaggle_environments/envs/football/football.ipynb +75 -0
- kaggle_environments/envs/football/football.json +91 -0
- kaggle_environments/envs/football/football.py +277 -0
- kaggle_environments/envs/football/helpers.py +95 -0
- kaggle_environments/envs/football/test_football.py +360 -0
- kaggle_environments/envs/halite/__init__.py +0 -0
- kaggle_environments/envs/halite/halite.ipynb +44741 -0
- kaggle_environments/envs/halite/halite.js +199 -83
- kaggle_environments/envs/halite/halite.json +31 -18
- kaggle_environments/envs/halite/halite.py +164 -303
- kaggle_environments/envs/halite/helpers.py +720 -0
- kaggle_environments/envs/halite/test_halite.py +190 -0
- kaggle_environments/envs/hungry_geese/__init__.py +0 -0
- kaggle_environments/envs/{battlegeese/battlegeese.js → hungry_geese/hungry_geese.js} +38 -22
- kaggle_environments/envs/{battlegeese/battlegeese.json → hungry_geese/hungry_geese.json} +21 -14
- kaggle_environments/envs/hungry_geese/hungry_geese.py +316 -0
- kaggle_environments/envs/hungry_geese/test_hungry_geese.py +0 -0
- kaggle_environments/envs/identity/identity.json +6 -5
- kaggle_environments/envs/identity/identity.py +15 -2
- kaggle_environments/envs/kore_fleets/__init__.py +0 -0
- kaggle_environments/envs/kore_fleets/helpers.py +1005 -0
- kaggle_environments/envs/kore_fleets/kore_fleets.ipynb +114 -0
- kaggle_environments/envs/kore_fleets/kore_fleets.js +658 -0
- kaggle_environments/envs/kore_fleets/kore_fleets.json +164 -0
- kaggle_environments/envs/kore_fleets/kore_fleets.py +555 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/Bot.java +54 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/README.md +26 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/jars/hamcrest-core-1.3.jar +0 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/jars/junit-4.13.2.jar +0 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Board.java +518 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Cell.java +61 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Configuration.java +24 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Direction.java +166 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Fleet.java +72 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/KoreJson.java +97 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Observation.java +72 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Pair.java +13 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Player.java +68 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Point.java +65 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Shipyard.java +70 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/kore/ShipyardAction.java +59 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/main.py +73 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/BoardTest.java +567 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/ConfigurationTest.java +25 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/KoreJsonTest.java +62 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/ObservationTest.java +46 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/PointTest.java +21 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/ShipyardTest.java +22 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/configuration.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/fullob.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/java/test/observation.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/python/__init__.py +0 -0
- kaggle_environments/envs/kore_fleets/starter_bots/python/main.py +27 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/Bot.ts +34 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/DoNothingBot.ts +12 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/MinerBot.ts +62 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/README.md +55 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/interpreter.ts +402 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Board.ts +514 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Cell.ts +63 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Configuration.ts +25 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Direction.ts +169 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Fleet.ts +76 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/KoreIO.ts +70 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Observation.ts +45 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Pair.ts +11 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Player.ts +68 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Point.ts +65 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Shipyard.ts +72 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/ShipyardAction.ts +58 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/main.py +73 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/miner.py +73 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/package.json +23 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/BoardTest.ts +551 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ConfigurationTest.ts +16 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ObservationTest.ts +33 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/PointTest.ts +17 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ShipyardTest.ts +18 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/configuration.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/fullob.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/test/observation.json +1 -0
- kaggle_environments/envs/kore_fleets/starter_bots/ts/tsconfig.json +22 -0
- kaggle_environments/envs/kore_fleets/test_kore_fleets.py +331 -0
- kaggle_environments/envs/lux_ai_2021/README.md +3 -0
- kaggle_environments/envs/lux_ai_2021/__init__.py +0 -0
- kaggle_environments/envs/lux_ai_2021/agents.py +11 -0
- kaggle_environments/envs/lux_ai_2021/dimensions/754.js +2 -0
- kaggle_environments/envs/lux_ai_2021/dimensions/754.js.LICENSE.txt +296 -0
- kaggle_environments/envs/lux_ai_2021/dimensions/main.js +1 -0
- kaggle_environments/envs/lux_ai_2021/index.html +43 -0
- kaggle_environments/envs/lux_ai_2021/lux_ai_2021.json +100 -0
- kaggle_environments/envs/lux_ai_2021/lux_ai_2021.py +231 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/__init__.py +0 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.js +6 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.json +59 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_objects.js +145 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/io.js +14 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/kit.js +209 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/map.js +107 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/parser.js +79 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.js +88 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.py +75 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/simple.tar.gz +0 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/__init__.py +0 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/annotate.py +20 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/constants.py +25 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game.py +86 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.json +59 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.py +7 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_map.py +106 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_objects.py +154 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/random_agent.py +38 -0
- kaggle_environments/envs/lux_ai_2021/test_agents/python/simple_agent.py +82 -0
- kaggle_environments/envs/lux_ai_2021/test_lux.py +19 -0
- kaggle_environments/envs/lux_ai_2021/testing.md +23 -0
- kaggle_environments/envs/lux_ai_2021/todo.md.og +18 -0
- kaggle_environments/envs/lux_ai_s3/README.md +21 -0
- kaggle_environments/envs/lux_ai_s3/agents.py +5 -0
- kaggle_environments/envs/lux_ai_s3/index.html +42 -0
- kaggle_environments/envs/lux_ai_s3/lux_ai_s3.json +47 -0
- kaggle_environments/envs/lux_ai_s3/lux_ai_s3.py +178 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/__init__.py +1 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/env.py +819 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/globals.py +9 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/params.py +101 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/profiler.py +141 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/pygame_render.py +222 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/spaces.py +27 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/state.py +464 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/utils.py +12 -0
- kaggle_environments/envs/lux_ai_s3/luxai_s3/wrappers.py +156 -0
- kaggle_environments/envs/lux_ai_s3/test_agents/python/agent.py +78 -0
- kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/__init__.py +0 -0
- kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/kit.py +31 -0
- kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/utils.py +17 -0
- kaggle_environments/envs/lux_ai_s3/test_agents/python/main.py +66 -0
- kaggle_environments/envs/lux_ai_s3/test_lux.py +9 -0
- kaggle_environments/envs/mab/__init__.py +0 -0
- kaggle_environments/envs/mab/agents.py +12 -0
- kaggle_environments/envs/mab/mab.js +100 -0
- kaggle_environments/envs/mab/mab.json +74 -0
- kaggle_environments/envs/mab/mab.py +146 -0
- kaggle_environments/envs/open_spiel/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/chess/chess.js +441 -0
- kaggle_environments/envs/open_spiel/games/chess/image_config.jsonl +20 -0
- kaggle_environments/envs/open_spiel/games/chess/openings.jsonl +20 -0
- kaggle_environments/envs/open_spiel/games/connect_four/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/connect_four/connect_four.js +284 -0
- kaggle_environments/envs/open_spiel/games/connect_four/connect_four_proxy.py +86 -0
- kaggle_environments/envs/open_spiel/games/go/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/go/go.js +481 -0
- kaggle_environments/envs/open_spiel/games/go/go_proxy.py +99 -0
- kaggle_environments/envs/open_spiel/games/tic_tac_toe/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/tic_tac_toe/tic_tac_toe.js +345 -0
- kaggle_environments/envs/open_spiel/games/tic_tac_toe/tic_tac_toe_proxy.py +98 -0
- kaggle_environments/envs/open_spiel/games/universal_poker/__init__.py +0 -0
- kaggle_environments/envs/open_spiel/games/universal_poker/universal_poker.js +431 -0
- kaggle_environments/envs/open_spiel/games/universal_poker/universal_poker_proxy.py +159 -0
- kaggle_environments/envs/open_spiel/html_playthrough_generator.py +31 -0
- kaggle_environments/envs/open_spiel/observation.py +128 -0
- kaggle_environments/envs/open_spiel/open_spiel.py +565 -0
- kaggle_environments/envs/open_spiel/proxy.py +138 -0
- kaggle_environments/envs/open_spiel/test_open_spiel.py +191 -0
- kaggle_environments/envs/rps/__init__.py +0 -0
- kaggle_environments/envs/rps/agents.py +84 -0
- kaggle_environments/envs/rps/helpers.py +25 -0
- kaggle_environments/envs/rps/rps.js +117 -0
- kaggle_environments/envs/rps/rps.json +63 -0
- kaggle_environments/envs/rps/rps.py +90 -0
- kaggle_environments/envs/rps/test_rps.py +110 -0
- kaggle_environments/envs/rps/utils.py +7 -0
- kaggle_environments/envs/tictactoe/test_tictactoe.py +43 -77
- kaggle_environments/envs/tictactoe/tictactoe.ipynb +1397 -0
- kaggle_environments/envs/tictactoe/tictactoe.json +10 -2
- kaggle_environments/envs/tictactoe/tictactoe.py +1 -1
- kaggle_environments/errors.py +2 -4
- kaggle_environments/helpers.py +377 -0
- kaggle_environments/main.py +340 -0
- kaggle_environments/schemas.json +23 -18
- kaggle_environments/static/player.html +206 -74
- kaggle_environments/utils.py +46 -73
- kaggle_environments-1.20.0.dist-info/METADATA +25 -0
- kaggle_environments-1.20.0.dist-info/RECORD +211 -0
- {kaggle_environments-0.2.1.dist-info → kaggle_environments-1.20.0.dist-info}/WHEEL +1 -2
- kaggle_environments-1.20.0.dist-info/entry_points.txt +3 -0
- kaggle_environments/envs/battlegeese/battlegeese.py +0 -223
- kaggle_environments/temp.py +0 -14
- kaggle_environments-0.2.1.dist-info/METADATA +0 -393
- kaggle_environments-0.2.1.dist-info/RECORD +0 -32
- kaggle_environments-0.2.1.dist-info/entry_points.txt +0 -3
- kaggle_environments-0.2.1.dist-info/top_level.txt +0 -1
- {kaggle_environments-0.2.1.dist-info → kaggle_environments-1.20.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from kaggle_environments import make
|
|
2
|
+
|
|
3
|
+
from .halite import random_agent
|
|
4
|
+
from .helpers import *
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_halite_no_repeated_steps():
|
|
8
|
+
step_count = 10
|
|
9
|
+
actual_steps = []
|
|
10
|
+
|
|
11
|
+
def step_appender_agent(obs, config):
|
|
12
|
+
actual_steps.append(obs.step)
|
|
13
|
+
return {}
|
|
14
|
+
|
|
15
|
+
env = make("halite", configuration={"episodeSteps": step_count}, debug=True)
|
|
16
|
+
env.run([step_appender_agent])
|
|
17
|
+
assert actual_steps == list(range(step_count - 1))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_halite_completes():
|
|
21
|
+
env = make("halite", configuration={"episodeSteps": 100})
|
|
22
|
+
env.run([random_agent, random_agent])
|
|
23
|
+
json = env.toJSON()
|
|
24
|
+
assert json["name"] == "halite"
|
|
25
|
+
assert json["statuses"] == ["DONE", "DONE"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_halite_exception_action_has_error_status():
|
|
29
|
+
env = make("halite")
|
|
30
|
+
|
|
31
|
+
def error_agent(obs, config):
|
|
32
|
+
raise Exception("An exception occurred!")
|
|
33
|
+
|
|
34
|
+
env.run([error_agent, random_agent])
|
|
35
|
+
json = env.toJSON()
|
|
36
|
+
assert json["name"] == "halite"
|
|
37
|
+
assert json["statuses"] == ["ERROR", "DONE"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_halite_helpers():
|
|
41
|
+
env = make("halite", configuration={"size": 3})
|
|
42
|
+
|
|
43
|
+
@board_agent
|
|
44
|
+
def helper_agent(board):
|
|
45
|
+
for ship in board.current_player.ships:
|
|
46
|
+
ship.next_action = ShipAction.NORTH
|
|
47
|
+
for shipyard in board.current_player.shipyards:
|
|
48
|
+
shipyard.next_action = ShipyardAction.SPAWN
|
|
49
|
+
|
|
50
|
+
env.run([helper_agent, helper_agent])
|
|
51
|
+
|
|
52
|
+
json = env.toJSON()
|
|
53
|
+
assert json["name"] == "halite"
|
|
54
|
+
assert json["statuses"] == ["DONE", "DONE"]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def create_board(size=3, starting_halite=0, agent_count=2, random_seed=0):
|
|
58
|
+
env = make("halite", configuration={"size": size, "startingHalite": starting_halite, "randomSeed": random_seed})
|
|
59
|
+
return Board(env.reset(agent_count)[0].observation, env.configuration)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_move_moves_ship():
|
|
63
|
+
size = 3
|
|
64
|
+
board = create_board(size, agent_count=1)
|
|
65
|
+
for ship in board.current_player.ships:
|
|
66
|
+
ship.next_action = ShipAction.SOUTH
|
|
67
|
+
next_board = board.next()
|
|
68
|
+
for ship in board.ships.values():
|
|
69
|
+
next_position = ship.position.translate(Point(0, -1), size)
|
|
70
|
+
next_ship = next_board.ships[ship.id]
|
|
71
|
+
assert next_ship.position == next_position
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def move_toward(ship, target: Point):
|
|
75
|
+
(x1, y1) = ship.position
|
|
76
|
+
(x2, y2) = target
|
|
77
|
+
if x2 > x1:
|
|
78
|
+
return ShipAction.EAST
|
|
79
|
+
elif x2 < x1:
|
|
80
|
+
return ShipAction.WEST
|
|
81
|
+
elif y2 > y1:
|
|
82
|
+
return ShipAction.NORTH
|
|
83
|
+
elif y2 < y1:
|
|
84
|
+
return ShipAction.SOUTH
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_equal_ship_collision_destroys_both_ships():
|
|
88
|
+
size = 3
|
|
89
|
+
board = create_board(size, agent_count=2)
|
|
90
|
+
for ship in board.ships.values():
|
|
91
|
+
ship.next_action = move_toward(ship, Point(1, 1))
|
|
92
|
+
next_board = board.next()
|
|
93
|
+
assert len(next_board.ships) == 0
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_unequal_ship_collision_destroys_weaker_ship():
|
|
97
|
+
board = create_board(agent_count=2)
|
|
98
|
+
for opponent in board.opponents:
|
|
99
|
+
for ship in opponent.ships:
|
|
100
|
+
# Make the opponents' ships have more halite so they'll be destroyed
|
|
101
|
+
ship._halite = 1000
|
|
102
|
+
for ship in board.ships.values():
|
|
103
|
+
ship.next_action = move_toward(ship, Point(1, 1))
|
|
104
|
+
next_board = board.next()
|
|
105
|
+
assert len(next_board.current_player.ships) == 1
|
|
106
|
+
assert len(next_board.ships) == 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def first(iterable):
|
|
110
|
+
return next(iter(iterable))
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_ship_shipyard_collision_destroys_both():
|
|
114
|
+
board = create_board(agent_count=2)
|
|
115
|
+
player_ship = first(board.current_player.ships)
|
|
116
|
+
opponent_ship = first(first(board.opponents).ships)
|
|
117
|
+
opponent_ship.next_action = ShipAction.CONVERT
|
|
118
|
+
board = board.next()
|
|
119
|
+
assert len(board.ships) == 1
|
|
120
|
+
assert len(board.shipyards) == 1
|
|
121
|
+
while player_ship.id in board.ships:
|
|
122
|
+
board.ships[player_ship.id].next_action = move_toward(player_ship, opponent_ship.position)
|
|
123
|
+
board = board.next()
|
|
124
|
+
assert len(board.ships) == 0
|
|
125
|
+
assert len(board.shipyards) == 0
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_cells_regen_halite():
|
|
129
|
+
board = create_board(starting_halite=1000, agent_count=1)
|
|
130
|
+
cell = first(board.cells.values())
|
|
131
|
+
next_board = board.next()
|
|
132
|
+
next_cell = next_board[cell.position]
|
|
133
|
+
expected_regen = round(cell.halite * board.configuration.regen_rate, 3)
|
|
134
|
+
# We compare to a floating point value here to handle float rounding errors
|
|
135
|
+
assert next_cell.halite - cell.halite - expected_regen < 0.000001
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_no_move_on_halite_gathers_halite():
|
|
139
|
+
board = create_board(starting_halite=1000, agent_count=1)
|
|
140
|
+
ship = first(board.ships.values())
|
|
141
|
+
expected_delta = int(ship.cell.halite * board.configuration.collect_rate)
|
|
142
|
+
next_board = board.next()
|
|
143
|
+
next_ship = next_board.ships[ship.id]
|
|
144
|
+
ship_delta = next_ship.halite - ship.halite
|
|
145
|
+
cell_delta = round(ship.cell.halite - next_ship.cell.halite, 3)
|
|
146
|
+
assert ship_delta == expected_delta
|
|
147
|
+
assert cell_delta == expected_delta
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_move_on_halite_gathers_no_halite():
|
|
151
|
+
board = create_board(starting_halite=1000, agent_count=1)
|
|
152
|
+
ship = first(board.ships.values())
|
|
153
|
+
ship.next_action = ShipAction.NORTH
|
|
154
|
+
next_board = board.next()
|
|
155
|
+
next_ship = next_board.ships[ship.id]
|
|
156
|
+
ship_delta = next_ship.halite - ship.halite
|
|
157
|
+
assert ship_delta == 0
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def test_failed_convert_gathers_halite():
|
|
161
|
+
board = create_board(starting_halite=1000, agent_count=1)
|
|
162
|
+
board.current_player._halite = board.configuration.convert_cost - 1
|
|
163
|
+
ship = first(board.ships.values())
|
|
164
|
+
ship.next_action = ShipAction.CONVERT
|
|
165
|
+
expected_delta = int(ship.cell.halite * board.configuration.collect_rate)
|
|
166
|
+
next_board = board.next()
|
|
167
|
+
next_ship = next_board.ships[ship.id]
|
|
168
|
+
ship_delta = next_ship.halite - ship.halite
|
|
169
|
+
cell_delta = round(ship.cell.halite - next_ship.cell.halite, 3)
|
|
170
|
+
assert ship_delta == expected_delta
|
|
171
|
+
assert cell_delta == expected_delta
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_shipyard_ids_not_reused():
|
|
175
|
+
board = create_board(starting_halite=1000, agent_count=1)
|
|
176
|
+
ship = first(board.ships.values())
|
|
177
|
+
ship.next_action = ShipAction.CONVERT
|
|
178
|
+
board = board.next()
|
|
179
|
+
shipyard = board.cells[ship.position].shipyard
|
|
180
|
+
assert ship.id != shipyard.id
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_seed_parameter():
|
|
184
|
+
seed = 9
|
|
185
|
+
|
|
186
|
+
def aggregate_halite_for_board(seed):
|
|
187
|
+
board = create_board(starting_halite=1000, agent_count=1, random_seed=seed)
|
|
188
|
+
return sum(map(lambda c: c.halite, board.cells.values()))
|
|
189
|
+
|
|
190
|
+
assert aggregate_halite_for_board(seed) == aggregate_halite_for_board(seed)
|
|
File without changes
|
|
@@ -151,36 +151,53 @@ function renderer({
|
|
|
151
151
|
],
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
// Javascript does not correctly handle mod of negatives
|
|
155
|
+
const mod = (number, divisor) => ((number % divisor) + divisor) % divisor;
|
|
156
|
+
|
|
157
|
+
const getRowCol = (cell) => [Math.floor(cell / columns), mod(cell, columns)];
|
|
158
|
+
|
|
159
|
+
const diff = (row, column, nextRow, nextColumn, rows, columns) => {
|
|
160
|
+
const north = mod(row - 1, rows) === nextRow;
|
|
161
|
+
const south = mod(row + 1, rows) === nextRow;
|
|
162
|
+
const east = mod(column + 1, columns) === nextColumn;
|
|
163
|
+
const west = mod(column - 1, columns) === nextColumn;
|
|
164
|
+
return [north, south, east, west];
|
|
165
|
+
}
|
|
155
166
|
|
|
156
|
-
// Observation.
|
|
157
167
|
const { geese, food } = environment.steps[step][0].observation;
|
|
158
168
|
|
|
159
169
|
// Organize the geese positions for rendering.
|
|
160
170
|
const geesePositions = {};
|
|
161
|
-
geese.forEach((goose,
|
|
171
|
+
geese.forEach((goose, gooseIndex) => {
|
|
162
172
|
const active = goose.length > 0;
|
|
163
173
|
let s = step;
|
|
164
174
|
if (!active) {
|
|
165
175
|
// Find the last step where the goose existed.
|
|
166
|
-
while (s
|
|
167
|
-
goose = environment.steps[--s][0].observation.geese[
|
|
176
|
+
while (s > 0 && !goose.length) {
|
|
177
|
+
goose = environment.steps[--s][0].observation.geese[gooseIndex];
|
|
168
178
|
}
|
|
169
179
|
}
|
|
170
|
-
goose.forEach((
|
|
171
|
-
if (!active &&
|
|
172
|
-
const [
|
|
173
|
-
const [
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
goose.forEach((position, segmentIndex) => {
|
|
181
|
+
if (!active && position in geesePositions) return;
|
|
182
|
+
const [row, column] = getRowCol(position);
|
|
183
|
+
const [previousRow, previousColumn] =
|
|
184
|
+
segmentIndex === 0
|
|
185
|
+
? [row, column]
|
|
186
|
+
: getRowCol(goose[segmentIndex - 1]);
|
|
187
|
+
const [nextRow, nextColumn] =
|
|
188
|
+
segmentIndex === goose.length - 1
|
|
189
|
+
? [row, column]
|
|
190
|
+
: getRowCol(goose[segmentIndex + 1]);
|
|
191
|
+
const [previousNorth, previousSouth, previousEast, previousWest] = diff(row, column, previousRow, previousColumn, rows, columns)
|
|
192
|
+
const [nextNorth, nextSouth, nextEast, nextWest] = diff(row, column, nextRow, nextColumn, rows, columns)
|
|
193
|
+
geesePositions[position] = {
|
|
194
|
+
index: gooseIndex,
|
|
195
|
+
head: segmentIndex === 0,
|
|
196
|
+
tail: segmentIndex === goose.length - 1,
|
|
197
|
+
east: previousEast || nextEast,
|
|
198
|
+
west: previousWest || nextWest,
|
|
199
|
+
south: previousSouth || nextSouth,
|
|
200
|
+
north: previousNorth || nextNorth,
|
|
184
201
|
active,
|
|
185
202
|
};
|
|
186
203
|
});
|
|
@@ -189,7 +206,7 @@ function renderer({
|
|
|
189
206
|
// Common Dimensions.
|
|
190
207
|
const unit = 8;
|
|
191
208
|
const minCanvasSize = Math.min(height, width);
|
|
192
|
-
const minOffset = minCanvasSize >
|
|
209
|
+
const minOffset = minCanvasSize > 800 ? 50 : unit / 2;
|
|
193
210
|
const cellSize = Math.min(
|
|
194
211
|
(width - minOffset * 2) / columns,
|
|
195
212
|
(height - minOffset * 2) / rows
|
|
@@ -312,7 +329,7 @@ function renderer({
|
|
|
312
329
|
|
|
313
330
|
// Canvas setup and reset.
|
|
314
331
|
const [bufferCanvas] = getCanvas("buffer");
|
|
315
|
-
const [canvas, c] = getCanvas("
|
|
332
|
+
const [canvas, c] = getCanvas("hungry_geese");
|
|
316
333
|
c.fillStyle = "#000B2A";
|
|
317
334
|
c.fillRect(0, 0, canvas.width, canvas.height);
|
|
318
335
|
|
|
@@ -384,7 +401,6 @@ function renderer({
|
|
|
384
401
|
width: 100,
|
|
385
402
|
height: 100,
|
|
386
403
|
});
|
|
387
|
-
canvas.style.marginLeft = "10000px";
|
|
388
404
|
ctx.drawImage(
|
|
389
405
|
bufferCanvas,
|
|
390
406
|
cellSize * agent.index,
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"title": "
|
|
2
|
+
"name": "hungry_geese",
|
|
3
|
+
"title": "Hungry Geese",
|
|
4
4
|
"description": "Similar to the classic snake game with multiple players",
|
|
5
5
|
"version": "1.0.0",
|
|
6
|
-
"agents": [2, 3, 4, 5, 6, 7, 8],
|
|
6
|
+
"agents": [1, 2, 3, 4, 5, 6, 7, 8],
|
|
7
7
|
"configuration": {
|
|
8
|
-
"episodeSteps":
|
|
8
|
+
"episodeSteps": 200,
|
|
9
|
+
"actTimeout": 1,
|
|
9
10
|
"columns": {
|
|
10
11
|
"description": "Horizontal number of cells on the board.",
|
|
11
12
|
"type": "integer",
|
|
12
13
|
"default": 11,
|
|
13
|
-
"minimum": 3
|
|
14
|
-
"maximum": 50
|
|
14
|
+
"minimum": 3
|
|
15
15
|
},
|
|
16
16
|
"rows": {
|
|
17
17
|
"description": "Vertical number of cells on the board.",
|
|
18
18
|
"type": "integer",
|
|
19
19
|
"default": 7,
|
|
20
|
-
"minimum": 3
|
|
21
|
-
"maximum": 50
|
|
20
|
+
"minimum": 3
|
|
22
21
|
},
|
|
23
22
|
"hunger_rate": {
|
|
24
23
|
"description": "The number of steps before the goose shrinks a cell.",
|
|
25
24
|
"type": "integer",
|
|
25
|
+
"minimum": 1,
|
|
26
26
|
"default": 40
|
|
27
27
|
},
|
|
28
28
|
"min_food": {
|
|
@@ -30,12 +30,18 @@
|
|
|
30
30
|
"type": "integer",
|
|
31
31
|
"default": 2,
|
|
32
32
|
"minimum": 1
|
|
33
|
+
},
|
|
34
|
+
"max_length": {
|
|
35
|
+
"description": "The max length any goose can be. Total reward = (max length + 1) * steps survived + goose length.",
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"default": 99
|
|
33
38
|
}
|
|
34
39
|
},
|
|
35
40
|
"reward": {
|
|
36
|
-
"description": "
|
|
41
|
+
"description": "steps survived * (max goose length + 1) + current goose length.",
|
|
37
42
|
"type": "integer",
|
|
38
|
-
"default": 0
|
|
43
|
+
"default": 0,
|
|
44
|
+
"minimum": 0
|
|
39
45
|
},
|
|
40
46
|
"observation": {
|
|
41
47
|
"geese": {
|
|
@@ -64,16 +70,17 @@
|
|
|
64
70
|
}
|
|
65
71
|
},
|
|
66
72
|
"index": {
|
|
67
|
-
"description": "Index of the
|
|
73
|
+
"description": "Index of the current agent's goose in the list of geese.",
|
|
68
74
|
"type": "integer",
|
|
69
75
|
"minimum": 0,
|
|
70
76
|
"defaults": [0, 1, 2, 3, 4, 5, 6, 7]
|
|
71
|
-
}
|
|
77
|
+
},
|
|
78
|
+
"remainingOverageTime": 60
|
|
72
79
|
},
|
|
73
80
|
"action": {
|
|
74
81
|
"description": "Direction to move the head of your players goose.",
|
|
75
82
|
"type": "string",
|
|
76
|
-
"enum": ["
|
|
77
|
-
"default": "
|
|
83
|
+
"enum": ["NORTH", "EAST", "SOUTH", "WEST"],
|
|
84
|
+
"default": "NORTH"
|
|
78
85
|
}
|
|
79
86
|
}
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Copyright 2020 Kaggle Inc
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
from enum import Enum, auto
|
|
17
|
+
from os import path
|
|
18
|
+
from random import choice, sample
|
|
19
|
+
from typing import *
|
|
20
|
+
|
|
21
|
+
import kaggle_environments.helpers
|
|
22
|
+
from kaggle_environments.helpers import histogram
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Observation(kaggle_environments.helpers.Observation):
|
|
26
|
+
@property
|
|
27
|
+
def geese(self) -> List[List[int]]:
|
|
28
|
+
return self["geese"]
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def food(self) -> List[int]:
|
|
32
|
+
return self["food"]
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def index(self) -> int:
|
|
36
|
+
return self["index"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Configuration(kaggle_environments.helpers.Configuration):
|
|
40
|
+
@property
|
|
41
|
+
def columns(self) -> int:
|
|
42
|
+
return self["columns"]
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def rows(self) -> int:
|
|
46
|
+
return self["rows"]
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def hunger_rate(self) -> int:
|
|
50
|
+
return self["hunger_rate"]
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def min_food(self) -> int:
|
|
54
|
+
return self["min_food"]
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def max_length(self) -> int:
|
|
58
|
+
return self["max_length"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class Action(Enum):
|
|
62
|
+
NORTH = auto()
|
|
63
|
+
EAST = auto()
|
|
64
|
+
SOUTH = auto()
|
|
65
|
+
WEST = auto()
|
|
66
|
+
|
|
67
|
+
def to_row_col(self):
|
|
68
|
+
if self == Action.NORTH:
|
|
69
|
+
return -1, 0
|
|
70
|
+
if self == Action.SOUTH:
|
|
71
|
+
return 1, 0
|
|
72
|
+
if self == Action.EAST:
|
|
73
|
+
return 0, 1
|
|
74
|
+
if self == Action.WEST:
|
|
75
|
+
return 0, -1
|
|
76
|
+
return 0, 0
|
|
77
|
+
|
|
78
|
+
def opposite(self):
|
|
79
|
+
if self == Action.NORTH:
|
|
80
|
+
return Action.SOUTH
|
|
81
|
+
if self == Action.SOUTH:
|
|
82
|
+
return Action.NORTH
|
|
83
|
+
if self == Action.EAST:
|
|
84
|
+
return Action.WEST
|
|
85
|
+
if self == Action.WEST:
|
|
86
|
+
return Action.EAST
|
|
87
|
+
raise TypeError(str(self) + " is not a valid Action.")
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def row_col(position: int, columns: int) -> Tuple[int, int]:
|
|
91
|
+
return position // columns, position % columns
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def translate(position: int, direction: Action, columns: int, rows: int) -> int:
|
|
95
|
+
row, column = row_col(position, columns)
|
|
96
|
+
row_offset, column_offset = direction.to_row_col()
|
|
97
|
+
row = (row + row_offset) % rows
|
|
98
|
+
column = (column + column_offset) % columns
|
|
99
|
+
return row * columns + column
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def adjacent_positions(position: int, columns: int, rows: int) -> List[int]:
|
|
103
|
+
return [translate(position, action, columns, rows) for action in Action]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def min_distance(position: int, food: List[int], columns: int):
|
|
107
|
+
row, column = row_col(position, columns)
|
|
108
|
+
return min(
|
|
109
|
+
abs(row - food_row) + abs(column - food_column)
|
|
110
|
+
for food_position in food
|
|
111
|
+
for food_row, food_column in [row_col(food_position, columns)]
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def random_agent():
|
|
116
|
+
return choice([action for action in Action]).name
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class GreedyAgent:
|
|
120
|
+
def __init__(self, configuration: Configuration):
|
|
121
|
+
self.configuration = configuration
|
|
122
|
+
self.last_action = None
|
|
123
|
+
|
|
124
|
+
def __call__(self, observation: Observation):
|
|
125
|
+
rows, columns = self.configuration.rows, self.configuration.columns
|
|
126
|
+
|
|
127
|
+
food = observation.food
|
|
128
|
+
geese = observation.geese
|
|
129
|
+
opponents = [goose for index, goose in enumerate(geese) if index != observation.index and len(goose) > 0]
|
|
130
|
+
|
|
131
|
+
# Don't move adjacent to any heads
|
|
132
|
+
head_adjacent_positions = {
|
|
133
|
+
opponent_head_adjacent
|
|
134
|
+
for opponent in opponents
|
|
135
|
+
for opponent_head in [opponent[0]]
|
|
136
|
+
for opponent_head_adjacent in adjacent_positions(opponent_head, columns, rows)
|
|
137
|
+
}
|
|
138
|
+
# Don't move into any bodies
|
|
139
|
+
bodies = {position for goose in geese for position in goose}
|
|
140
|
+
|
|
141
|
+
# Move to the closest food
|
|
142
|
+
position = geese[observation.index][0]
|
|
143
|
+
actions = {
|
|
144
|
+
action: min_distance(new_position, food, columns)
|
|
145
|
+
for action in Action
|
|
146
|
+
for new_position in [translate(position, action, columns, rows)]
|
|
147
|
+
if (
|
|
148
|
+
new_position not in head_adjacent_positions
|
|
149
|
+
and new_position not in bodies
|
|
150
|
+
and (self.last_action is None or action != self.last_action.opposite())
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
action = min(actions, key=actions.get) if any(actions) else choice([action for action in Action])
|
|
155
|
+
self.last_action = action
|
|
156
|
+
return action.name
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
cached_greedy_agents = {}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def greedy_agent(obs, config):
|
|
163
|
+
index = obs["index"]
|
|
164
|
+
if index not in cached_greedy_agents:
|
|
165
|
+
cached_greedy_agents[index] = GreedyAgent(Configuration(config))
|
|
166
|
+
return cached_greedy_agents[index](Observation(obs))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
agents = {"random": random_agent, "greedy": greedy_agent}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def interpreter(state, env):
|
|
173
|
+
configuration = Configuration(env.configuration)
|
|
174
|
+
columns = configuration.columns
|
|
175
|
+
rows = configuration.rows
|
|
176
|
+
min_food = configuration.min_food
|
|
177
|
+
state[0].observation = shared_observation = Observation(state[0].observation)
|
|
178
|
+
|
|
179
|
+
# Reset the environment.
|
|
180
|
+
if env.done:
|
|
181
|
+
agent_count = len(state)
|
|
182
|
+
heads = sample(range(columns * rows), agent_count)
|
|
183
|
+
shared_observation["geese"] = [[head] for head in heads]
|
|
184
|
+
food_candidates = set(range(columns * rows)).difference(heads)
|
|
185
|
+
# Ensure we only place as many food as there are open squares
|
|
186
|
+
min_food = min(min_food, len(food_candidates))
|
|
187
|
+
shared_observation["food"] = sample(food_candidates, min_food)
|
|
188
|
+
return state
|
|
189
|
+
|
|
190
|
+
geese = shared_observation.geese
|
|
191
|
+
food = shared_observation.food
|
|
192
|
+
|
|
193
|
+
# If there is no last state, reuse current state so that current action is never the opposite of the last action.
|
|
194
|
+
last_state = env.steps[-1] if len(env.steps) > 1 else state
|
|
195
|
+
# Apply the actions from active agents.
|
|
196
|
+
for index, agent in enumerate(state):
|
|
197
|
+
if agent.status != "ACTIVE":
|
|
198
|
+
if agent.status != "INACTIVE" and agent.status != "DONE":
|
|
199
|
+
# ERROR, INVALID, or TIMEOUT, remove the goose.
|
|
200
|
+
geese[index] = []
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
action = Action[agent.action]
|
|
204
|
+
|
|
205
|
+
# Check action direction
|
|
206
|
+
last_agent = last_state[index]
|
|
207
|
+
last_action = Action[last_agent["action"]] if "action" in last_agent else action
|
|
208
|
+
if last_action == action.opposite():
|
|
209
|
+
env.debug_print(f"Opposite action: {agent.observation.index, action, last_action}")
|
|
210
|
+
agent.status = "DONE"
|
|
211
|
+
geese[index] = []
|
|
212
|
+
continue
|
|
213
|
+
|
|
214
|
+
goose = geese[index]
|
|
215
|
+
head = translate(goose[0], action, columns, rows)
|
|
216
|
+
|
|
217
|
+
# Consume food or drop a tail piece.
|
|
218
|
+
if head in food:
|
|
219
|
+
food.remove(head)
|
|
220
|
+
else:
|
|
221
|
+
goose.pop()
|
|
222
|
+
|
|
223
|
+
# Self collision.
|
|
224
|
+
if head in goose:
|
|
225
|
+
env.debug_print(f"Body Hit: {agent.observation.index, action, head, goose}")
|
|
226
|
+
agent.status = "DONE"
|
|
227
|
+
geese[index] = []
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
while len(goose) >= configuration.max_length:
|
|
231
|
+
# Free a spot for the new head if needed
|
|
232
|
+
goose.pop()
|
|
233
|
+
# Add New Head to the Goose.
|
|
234
|
+
goose.insert(0, head)
|
|
235
|
+
|
|
236
|
+
# If hunger strikes remove from the tail.
|
|
237
|
+
if len(env.steps) % configuration.hunger_rate == 0:
|
|
238
|
+
if len(goose) > 0:
|
|
239
|
+
goose.pop()
|
|
240
|
+
if len(goose) == 0:
|
|
241
|
+
env.debug_print(f"Goose Starved: {action}")
|
|
242
|
+
agent.status = "DONE"
|
|
243
|
+
continue
|
|
244
|
+
|
|
245
|
+
goose_positions = histogram(position for goose in geese for position in goose)
|
|
246
|
+
|
|
247
|
+
# Check for collisions.
|
|
248
|
+
for index, agent in enumerate(state):
|
|
249
|
+
goose = geese[index]
|
|
250
|
+
if len(goose) > 0:
|
|
251
|
+
head = geese[index][0]
|
|
252
|
+
if goose_positions[head] > 1:
|
|
253
|
+
env.debug_print(f"Goose Collision: {agent.action}")
|
|
254
|
+
agent.status = "DONE"
|
|
255
|
+
geese[index] = []
|
|
256
|
+
|
|
257
|
+
# Add food if min_food threshold reached.
|
|
258
|
+
needed_food = min_food - len(food)
|
|
259
|
+
if needed_food > 0:
|
|
260
|
+
collisions = {position for goose in geese for position in goose}
|
|
261
|
+
available_positions = set(range(rows * columns)).difference(collisions).difference(food)
|
|
262
|
+
# Ensure we don't sample more food than available positions.
|
|
263
|
+
needed_food = min(needed_food, len(available_positions))
|
|
264
|
+
food.extend(sample(available_positions, needed_food))
|
|
265
|
+
|
|
266
|
+
# Set rewards after deleting all geese to ensure that geese don't receive a reward on the turn they perish.
|
|
267
|
+
for index, agent in enumerate(state):
|
|
268
|
+
if agent.status == "ACTIVE":
|
|
269
|
+
# Adding 1 to len(env.steps) ensures that if an agent gets reward 4507, it died on turn 45 with length 7.
|
|
270
|
+
agent.reward = (len(env.steps) + 1) * (configuration.max_length + 1) + len(geese[index])
|
|
271
|
+
|
|
272
|
+
# If only one ACTIVE agent left, set it to DONE.
|
|
273
|
+
active_agents = [a for a in state if a.status == "ACTIVE"]
|
|
274
|
+
if len(active_agents) == 1:
|
|
275
|
+
agent = active_agents[0]
|
|
276
|
+
agent.status = "DONE"
|
|
277
|
+
|
|
278
|
+
return state
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def renderer(state, env):
|
|
282
|
+
config = env.configuration
|
|
283
|
+
columns = config.columns
|
|
284
|
+
rows = config.rows
|
|
285
|
+
|
|
286
|
+
food_symbol = "F"
|
|
287
|
+
column_divider = "|"
|
|
288
|
+
row_divider = "+" + "+".join(["---"] * columns) + "+\n"
|
|
289
|
+
|
|
290
|
+
board = [" "] * (rows * columns)
|
|
291
|
+
for pos in state[0].observation.food:
|
|
292
|
+
board[pos] = food_symbol
|
|
293
|
+
|
|
294
|
+
for index, goose in enumerate(state[0].observation.geese):
|
|
295
|
+
for position in goose:
|
|
296
|
+
board[position] = index
|
|
297
|
+
|
|
298
|
+
out = row_divider
|
|
299
|
+
for row in range(rows):
|
|
300
|
+
for col in range(columns):
|
|
301
|
+
out += column_divider + f" {board[(row * columns) + col]} "
|
|
302
|
+
out += column_divider + "\n" + row_divider
|
|
303
|
+
|
|
304
|
+
return out
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
dirpath = path.dirname(__file__)
|
|
308
|
+
jsonpath = path.abspath(path.join(dirpath, "hungry_geese.json"))
|
|
309
|
+
with open(jsonpath) as f:
|
|
310
|
+
specification = json.load(f)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def html_renderer():
|
|
314
|
+
jspath = path.abspath(path.join(dirpath, "hungry_geese.js"))
|
|
315
|
+
with open(jspath, encoding="utf-8") as f:
|
|
316
|
+
return f.read()
|
|
File without changes
|