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.

Files changed (215) hide show
  1. kaggle_environments/__init__.py +49 -13
  2. kaggle_environments/agent.py +177 -124
  3. kaggle_environments/api.py +31 -0
  4. kaggle_environments/core.py +295 -170
  5. kaggle_environments/envs/cabt/cabt.js +164 -0
  6. kaggle_environments/envs/cabt/cabt.json +28 -0
  7. kaggle_environments/envs/cabt/cabt.py +186 -0
  8. kaggle_environments/envs/cabt/cg/__init__.py +0 -0
  9. kaggle_environments/envs/cabt/cg/cg.dll +0 -0
  10. kaggle_environments/envs/cabt/cg/game.py +75 -0
  11. kaggle_environments/envs/cabt/cg/libcg.so +0 -0
  12. kaggle_environments/envs/cabt/cg/sim.py +48 -0
  13. kaggle_environments/envs/cabt/test_cabt.py +120 -0
  14. kaggle_environments/envs/chess/chess.js +4289 -0
  15. kaggle_environments/envs/chess/chess.json +60 -0
  16. kaggle_environments/envs/chess/chess.py +4241 -0
  17. kaggle_environments/envs/chess/test_chess.py +60 -0
  18. kaggle_environments/envs/connectx/connectx.ipynb +3186 -0
  19. kaggle_environments/envs/connectx/connectx.js +1 -1
  20. kaggle_environments/envs/connectx/connectx.json +15 -1
  21. kaggle_environments/envs/connectx/connectx.py +6 -23
  22. kaggle_environments/envs/connectx/test_connectx.py +70 -24
  23. kaggle_environments/envs/football/football.ipynb +75 -0
  24. kaggle_environments/envs/football/football.json +91 -0
  25. kaggle_environments/envs/football/football.py +277 -0
  26. kaggle_environments/envs/football/helpers.py +95 -0
  27. kaggle_environments/envs/football/test_football.py +360 -0
  28. kaggle_environments/envs/halite/__init__.py +0 -0
  29. kaggle_environments/envs/halite/halite.ipynb +44741 -0
  30. kaggle_environments/envs/halite/halite.js +199 -83
  31. kaggle_environments/envs/halite/halite.json +31 -18
  32. kaggle_environments/envs/halite/halite.py +164 -303
  33. kaggle_environments/envs/halite/helpers.py +720 -0
  34. kaggle_environments/envs/halite/test_halite.py +190 -0
  35. kaggle_environments/envs/hungry_geese/__init__.py +0 -0
  36. kaggle_environments/envs/{battlegeese/battlegeese.js → hungry_geese/hungry_geese.js} +38 -22
  37. kaggle_environments/envs/{battlegeese/battlegeese.json → hungry_geese/hungry_geese.json} +21 -14
  38. kaggle_environments/envs/hungry_geese/hungry_geese.py +316 -0
  39. kaggle_environments/envs/hungry_geese/test_hungry_geese.py +0 -0
  40. kaggle_environments/envs/identity/identity.json +6 -5
  41. kaggle_environments/envs/identity/identity.py +15 -2
  42. kaggle_environments/envs/kore_fleets/__init__.py +0 -0
  43. kaggle_environments/envs/kore_fleets/helpers.py +1005 -0
  44. kaggle_environments/envs/kore_fleets/kore_fleets.ipynb +114 -0
  45. kaggle_environments/envs/kore_fleets/kore_fleets.js +658 -0
  46. kaggle_environments/envs/kore_fleets/kore_fleets.json +164 -0
  47. kaggle_environments/envs/kore_fleets/kore_fleets.py +555 -0
  48. kaggle_environments/envs/kore_fleets/starter_bots/java/Bot.java +54 -0
  49. kaggle_environments/envs/kore_fleets/starter_bots/java/README.md +26 -0
  50. kaggle_environments/envs/kore_fleets/starter_bots/java/jars/hamcrest-core-1.3.jar +0 -0
  51. kaggle_environments/envs/kore_fleets/starter_bots/java/jars/junit-4.13.2.jar +0 -0
  52. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Board.java +518 -0
  53. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Cell.java +61 -0
  54. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Configuration.java +24 -0
  55. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Direction.java +166 -0
  56. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Fleet.java +72 -0
  57. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/KoreJson.java +97 -0
  58. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Observation.java +72 -0
  59. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Pair.java +13 -0
  60. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Player.java +68 -0
  61. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Point.java +65 -0
  62. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/Shipyard.java +70 -0
  63. kaggle_environments/envs/kore_fleets/starter_bots/java/kore/ShipyardAction.java +59 -0
  64. kaggle_environments/envs/kore_fleets/starter_bots/java/main.py +73 -0
  65. kaggle_environments/envs/kore_fleets/starter_bots/java/test/BoardTest.java +567 -0
  66. kaggle_environments/envs/kore_fleets/starter_bots/java/test/ConfigurationTest.java +25 -0
  67. kaggle_environments/envs/kore_fleets/starter_bots/java/test/KoreJsonTest.java +62 -0
  68. kaggle_environments/envs/kore_fleets/starter_bots/java/test/ObservationTest.java +46 -0
  69. kaggle_environments/envs/kore_fleets/starter_bots/java/test/PointTest.java +21 -0
  70. kaggle_environments/envs/kore_fleets/starter_bots/java/test/ShipyardTest.java +22 -0
  71. kaggle_environments/envs/kore_fleets/starter_bots/java/test/configuration.json +1 -0
  72. kaggle_environments/envs/kore_fleets/starter_bots/java/test/fullob.json +1 -0
  73. kaggle_environments/envs/kore_fleets/starter_bots/java/test/observation.json +1 -0
  74. kaggle_environments/envs/kore_fleets/starter_bots/python/__init__.py +0 -0
  75. kaggle_environments/envs/kore_fleets/starter_bots/python/main.py +27 -0
  76. kaggle_environments/envs/kore_fleets/starter_bots/ts/Bot.ts +34 -0
  77. kaggle_environments/envs/kore_fleets/starter_bots/ts/DoNothingBot.ts +12 -0
  78. kaggle_environments/envs/kore_fleets/starter_bots/ts/MinerBot.ts +62 -0
  79. kaggle_environments/envs/kore_fleets/starter_bots/ts/README.md +55 -0
  80. kaggle_environments/envs/kore_fleets/starter_bots/ts/interpreter.ts +402 -0
  81. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Board.ts +514 -0
  82. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Cell.ts +63 -0
  83. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Configuration.ts +25 -0
  84. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Direction.ts +169 -0
  85. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Fleet.ts +76 -0
  86. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/KoreIO.ts +70 -0
  87. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Observation.ts +45 -0
  88. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Pair.ts +11 -0
  89. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Player.ts +68 -0
  90. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Point.ts +65 -0
  91. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/Shipyard.ts +72 -0
  92. kaggle_environments/envs/kore_fleets/starter_bots/ts/kore/ShipyardAction.ts +58 -0
  93. kaggle_environments/envs/kore_fleets/starter_bots/ts/main.py +73 -0
  94. kaggle_environments/envs/kore_fleets/starter_bots/ts/miner.py +73 -0
  95. kaggle_environments/envs/kore_fleets/starter_bots/ts/package.json +23 -0
  96. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/BoardTest.ts +551 -0
  97. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ConfigurationTest.ts +16 -0
  98. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ObservationTest.ts +33 -0
  99. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/PointTest.ts +17 -0
  100. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/ShipyardTest.ts +18 -0
  101. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/configuration.json +1 -0
  102. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/fullob.json +1 -0
  103. kaggle_environments/envs/kore_fleets/starter_bots/ts/test/observation.json +1 -0
  104. kaggle_environments/envs/kore_fleets/starter_bots/ts/tsconfig.json +22 -0
  105. kaggle_environments/envs/kore_fleets/test_kore_fleets.py +331 -0
  106. kaggle_environments/envs/lux_ai_2021/README.md +3 -0
  107. kaggle_environments/envs/lux_ai_2021/__init__.py +0 -0
  108. kaggle_environments/envs/lux_ai_2021/agents.py +11 -0
  109. kaggle_environments/envs/lux_ai_2021/dimensions/754.js +2 -0
  110. kaggle_environments/envs/lux_ai_2021/dimensions/754.js.LICENSE.txt +296 -0
  111. kaggle_environments/envs/lux_ai_2021/dimensions/main.js +1 -0
  112. kaggle_environments/envs/lux_ai_2021/index.html +43 -0
  113. kaggle_environments/envs/lux_ai_2021/lux_ai_2021.json +100 -0
  114. kaggle_environments/envs/lux_ai_2021/lux_ai_2021.py +231 -0
  115. kaggle_environments/envs/lux_ai_2021/test_agents/__init__.py +0 -0
  116. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.js +6 -0
  117. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_constants.json +59 -0
  118. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/game_objects.js +145 -0
  119. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/io.js +14 -0
  120. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/kit.js +209 -0
  121. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/map.js +107 -0
  122. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/lux/parser.js +79 -0
  123. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.js +88 -0
  124. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/main.py +75 -0
  125. kaggle_environments/envs/lux_ai_2021/test_agents/js_simple/simple.tar.gz +0 -0
  126. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/__init__.py +0 -0
  127. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/annotate.py +20 -0
  128. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/constants.py +25 -0
  129. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game.py +86 -0
  130. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.json +59 -0
  131. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_constants.py +7 -0
  132. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_map.py +106 -0
  133. kaggle_environments/envs/lux_ai_2021/test_agents/python/lux/game_objects.py +154 -0
  134. kaggle_environments/envs/lux_ai_2021/test_agents/python/random_agent.py +38 -0
  135. kaggle_environments/envs/lux_ai_2021/test_agents/python/simple_agent.py +82 -0
  136. kaggle_environments/envs/lux_ai_2021/test_lux.py +19 -0
  137. kaggle_environments/envs/lux_ai_2021/testing.md +23 -0
  138. kaggle_environments/envs/lux_ai_2021/todo.md.og +18 -0
  139. kaggle_environments/envs/lux_ai_s3/README.md +21 -0
  140. kaggle_environments/envs/lux_ai_s3/agents.py +5 -0
  141. kaggle_environments/envs/lux_ai_s3/index.html +42 -0
  142. kaggle_environments/envs/lux_ai_s3/lux_ai_s3.json +47 -0
  143. kaggle_environments/envs/lux_ai_s3/lux_ai_s3.py +178 -0
  144. kaggle_environments/envs/lux_ai_s3/luxai_s3/__init__.py +1 -0
  145. kaggle_environments/envs/lux_ai_s3/luxai_s3/env.py +819 -0
  146. kaggle_environments/envs/lux_ai_s3/luxai_s3/globals.py +9 -0
  147. kaggle_environments/envs/lux_ai_s3/luxai_s3/params.py +101 -0
  148. kaggle_environments/envs/lux_ai_s3/luxai_s3/profiler.py +141 -0
  149. kaggle_environments/envs/lux_ai_s3/luxai_s3/pygame_render.py +222 -0
  150. kaggle_environments/envs/lux_ai_s3/luxai_s3/spaces.py +27 -0
  151. kaggle_environments/envs/lux_ai_s3/luxai_s3/state.py +464 -0
  152. kaggle_environments/envs/lux_ai_s3/luxai_s3/utils.py +12 -0
  153. kaggle_environments/envs/lux_ai_s3/luxai_s3/wrappers.py +156 -0
  154. kaggle_environments/envs/lux_ai_s3/test_agents/python/agent.py +78 -0
  155. kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/__init__.py +0 -0
  156. kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/kit.py +31 -0
  157. kaggle_environments/envs/lux_ai_s3/test_agents/python/lux/utils.py +17 -0
  158. kaggle_environments/envs/lux_ai_s3/test_agents/python/main.py +66 -0
  159. kaggle_environments/envs/lux_ai_s3/test_lux.py +9 -0
  160. kaggle_environments/envs/mab/__init__.py +0 -0
  161. kaggle_environments/envs/mab/agents.py +12 -0
  162. kaggle_environments/envs/mab/mab.js +100 -0
  163. kaggle_environments/envs/mab/mab.json +74 -0
  164. kaggle_environments/envs/mab/mab.py +146 -0
  165. kaggle_environments/envs/open_spiel/__init__.py +0 -0
  166. kaggle_environments/envs/open_spiel/games/__init__.py +0 -0
  167. kaggle_environments/envs/open_spiel/games/chess/chess.js +441 -0
  168. kaggle_environments/envs/open_spiel/games/chess/image_config.jsonl +20 -0
  169. kaggle_environments/envs/open_spiel/games/chess/openings.jsonl +20 -0
  170. kaggle_environments/envs/open_spiel/games/connect_four/__init__.py +0 -0
  171. kaggle_environments/envs/open_spiel/games/connect_four/connect_four.js +284 -0
  172. kaggle_environments/envs/open_spiel/games/connect_four/connect_four_proxy.py +86 -0
  173. kaggle_environments/envs/open_spiel/games/go/__init__.py +0 -0
  174. kaggle_environments/envs/open_spiel/games/go/go.js +481 -0
  175. kaggle_environments/envs/open_spiel/games/go/go_proxy.py +99 -0
  176. kaggle_environments/envs/open_spiel/games/tic_tac_toe/__init__.py +0 -0
  177. kaggle_environments/envs/open_spiel/games/tic_tac_toe/tic_tac_toe.js +345 -0
  178. kaggle_environments/envs/open_spiel/games/tic_tac_toe/tic_tac_toe_proxy.py +98 -0
  179. kaggle_environments/envs/open_spiel/games/universal_poker/__init__.py +0 -0
  180. kaggle_environments/envs/open_spiel/games/universal_poker/universal_poker.js +431 -0
  181. kaggle_environments/envs/open_spiel/games/universal_poker/universal_poker_proxy.py +159 -0
  182. kaggle_environments/envs/open_spiel/html_playthrough_generator.py +31 -0
  183. kaggle_environments/envs/open_spiel/observation.py +128 -0
  184. kaggle_environments/envs/open_spiel/open_spiel.py +565 -0
  185. kaggle_environments/envs/open_spiel/proxy.py +138 -0
  186. kaggle_environments/envs/open_spiel/test_open_spiel.py +191 -0
  187. kaggle_environments/envs/rps/__init__.py +0 -0
  188. kaggle_environments/envs/rps/agents.py +84 -0
  189. kaggle_environments/envs/rps/helpers.py +25 -0
  190. kaggle_environments/envs/rps/rps.js +117 -0
  191. kaggle_environments/envs/rps/rps.json +63 -0
  192. kaggle_environments/envs/rps/rps.py +90 -0
  193. kaggle_environments/envs/rps/test_rps.py +110 -0
  194. kaggle_environments/envs/rps/utils.py +7 -0
  195. kaggle_environments/envs/tictactoe/test_tictactoe.py +43 -77
  196. kaggle_environments/envs/tictactoe/tictactoe.ipynb +1397 -0
  197. kaggle_environments/envs/tictactoe/tictactoe.json +10 -2
  198. kaggle_environments/envs/tictactoe/tictactoe.py +1 -1
  199. kaggle_environments/errors.py +2 -4
  200. kaggle_environments/helpers.py +377 -0
  201. kaggle_environments/main.py +340 -0
  202. kaggle_environments/schemas.json +23 -18
  203. kaggle_environments/static/player.html +206 -74
  204. kaggle_environments/utils.py +46 -73
  205. kaggle_environments-1.20.0.dist-info/METADATA +25 -0
  206. kaggle_environments-1.20.0.dist-info/RECORD +211 -0
  207. {kaggle_environments-0.2.1.dist-info → kaggle_environments-1.20.0.dist-info}/WHEEL +1 -2
  208. kaggle_environments-1.20.0.dist-info/entry_points.txt +3 -0
  209. kaggle_environments/envs/battlegeese/battlegeese.py +0 -223
  210. kaggle_environments/temp.py +0 -14
  211. kaggle_environments-0.2.1.dist-info/METADATA +0 -393
  212. kaggle_environments-0.2.1.dist-info/RECORD +0 -32
  213. kaggle_environments-0.2.1.dist-info/entry_points.txt +0 -3
  214. kaggle_environments-0.2.1.dist-info/top_level.txt +0 -1
  215. {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
- const getRowCol = (cell) => [Math.floor(cell / columns), cell % columns];
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, index) => {
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 >= 0 && !goose.length) {
167
- goose = environment.steps[--s][0].observation.geese[index];
176
+ while (s > 0 && !goose.length) {
177
+ goose = environment.steps[--s][0].observation.geese[gooseIndex];
168
178
  }
169
179
  }
170
- goose.forEach((pos, posIndex) => {
171
- if (!active && pos in geesePositions) return;
172
- const [r, c] = getRowCol(pos);
173
- const [br, bc] = posIndex === 0 ? [r, c] : getRowCol(goose[posIndex - 1]);
174
- const [ar, ac] =
175
- posIndex === goose.length - 1 ? [r, c] : getRowCol(goose[posIndex + 1]);
176
- geesePositions[pos] = {
177
- index,
178
- head: posIndex === 0,
179
- tail: posIndex === goose.length - 1,
180
- east: bc > c || ac > c,
181
- west: bc < c || ac < c,
182
- south: br > r || ar > r,
183
- north: br < r || ar < r,
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 > 400 ? 30 : unit / 2;
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("battlegeese");
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": "battlegeese",
3
- "title": "Battle Geese",
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": 100,
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": "The number of steps the goose has moved plus it's length.",
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 agents goose in the list of geese.",
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": ["N", "E", "S", "W"],
77
- "default": "N"
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()