kaggle-environments 1.17.6__py2.py3-none-any.whl → 1.17.7__py2.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.

@@ -20,7 +20,7 @@ from .core import *
20
20
  from .main import http_request
21
21
  from . import errors
22
22
 
23
- __version__ = "1.17.6"
23
+ __version__ = "1.17.7"
24
24
 
25
25
  __all__ = ["Agent", "environments", "errors", "evaluate", "http_request",
26
26
  "make", "register", "utils", "__version__",
@@ -4045,14 +4045,14 @@ async function renderer(context) {
4045
4045
 
4046
4046
  // Create the Download PGN button
4047
4047
  let downloadButton = parent.querySelector("#copy-pgn");
4048
- if (!downloadButton) {
4048
+ if (!downloadButton && environment.steps.length) {
4049
4049
  try {
4050
4050
  const board = environment.steps[0][0].observation.board;
4051
4051
  const info = environment.info;
4052
4052
  const agent1 = info?.TeamNames?.[0] || "Agent 1";
4053
4053
  const agent2 = info?.TeamNames?.[1] || "Agent 2";
4054
4054
  const game = new Chess();
4055
- let result = environment.rewards;
4055
+ let result = environment.rewards ?? [];
4056
4056
  if (result.some((r) => r === undefined || r === null)) {
4057
4057
  result = result.map((r) => (r === undefined || r === null ? 0 : 1));
4058
4058
  }
@@ -4103,7 +4103,9 @@ async function renderer(context) {
4103
4103
  downloadButton.style.top = "10px";
4104
4104
  downloadButton.style.left = "10px";
4105
4105
  downloadButton.style.zIndex = 1;
4106
- parent.appendChild(downloadButton);
4106
+ if(!environment.viewer) {
4107
+ parent.appendChild(downloadButton);
4108
+ }
4107
4109
 
4108
4110
  downloadButton.addEventListener("click", async () => {
4109
4111
  try {
@@ -4179,26 +4181,28 @@ async function renderer(context) {
4179
4181
  }
4180
4182
  }
4181
4183
  // Draw the team names and game status
4182
- const info = environment.info;
4183
- const agent1 = info?.TeamNames?.[0] || "Agent 1";
4184
- const agent2 = info?.TeamNames?.[1] || "Agent 2";
4185
- const firstGame = environment.steps[step][0].observation.mark == "white";
4186
- const fontSize = Math.round(0.33 * offset);
4187
- c.font = `${fontSize}px sans-serif`;
4188
- c.fillStyle = "#FFFFFF";
4189
- const agent1Reward = environment.steps[step][0].reward;
4190
- const agent2Reward = environment.steps[step][1].reward;
4191
- const charCount = agent1.length + agent2.length + 12;
4192
- const title = `${
4193
- firstGame ? "\u25A0" : "\u25A1"
4194
- }${agent1} (${agent1Reward}) vs ${
4195
- firstGame ? "\u25A1" : "\u25A0"
4196
- }${agent2} (${agent2Reward})`;
4197
- c.fillText(
4198
- title,
4199
- offset + 4 * squareSize - Math.floor((charCount * fontSize) / 4),
4200
- 40
4201
- );
4184
+ if (!environment.viewer) {
4185
+ const info = environment.info;
4186
+ const agent1 = info?.TeamNames?.[0] || "Agent 1";
4187
+ const agent2 = info?.TeamNames?.[1] || "Agent 2";
4188
+ const firstGame = environment.steps[step][0].observation.mark == "white";
4189
+ const fontSize = Math.round(0.33 * offset);
4190
+ c.font = `${fontSize}px sans-serif`;
4191
+ c.fillStyle = "#FFFFFF";
4192
+ const agent1Reward = environment.steps[step][0].reward;
4193
+ const agent2Reward = environment.steps[step][1].reward;
4194
+ const charCount = agent1.length + agent2.length + 12;
4195
+ const title = `${
4196
+ firstGame ? "\u25A0" : "\u25A1"
4197
+ }${agent1} (${agent1Reward}) vs ${
4198
+ firstGame ? "\u25A1" : "\u25A0"
4199
+ }${agent2} (${agent2Reward})`;
4200
+ c.fillText(
4201
+ title,
4202
+ offset + 4 * squareSize - Math.floor((charCount * fontSize) / 4),
4203
+ 40
4204
+ );
4205
+ }
4202
4206
 
4203
4207
  // Draw the Pieces
4204
4208
  const board = environment.steps[step][0].observation.board;
@@ -1,5 +1,5 @@
1
1
  function renderer(options) {
2
- const { environment, step, parent, interactive, isInteractive } = options;
2
+ const { environment, step, parent, width = 400, height = 400 } = options;
3
3
 
4
4
  // Chess-specific constants
5
5
  const DEFAULT_NUM_ROWS = 8;
@@ -28,6 +28,7 @@ function renderer(options) {
28
28
  let currentMessageBoxElement = typeof document !== 'undefined' ? document.getElementById('messageBox') : null;
29
29
  let currentRendererContainer = null;
30
30
  let currentTitleElement = null;
31
+ let squareSize = 50;
31
32
 
32
33
  function _showMessage(message, type = 'info', duration = 3000) {
33
34
  if (typeof document === 'undefined' || !document.body) return;
@@ -74,25 +75,30 @@ function renderer(options) {
74
75
  fontFamily: "'Inter', sans-serif"
75
76
  });
76
77
 
77
- currentTitleElement = document.createElement('h1');
78
- currentTitleElement.textContent = 'Chess';
79
- // Identical styling to the Connect Four renderer's title
80
- Object.assign(currentTitleElement.style, {
78
+ if (!environment.viewer) {
79
+ currentTitleElement = document.createElement('h1');
80
+ currentTitleElement.textContent = 'Chess';
81
+ // Identical styling to the Connect Four renderer's title
82
+ Object.assign(currentTitleElement.style, {
81
83
  fontSize: '1.875rem',
82
84
  fontWeight: 'bold',
83
85
  marginBottom: '1rem',
84
86
  textAlign: 'center',
85
87
  color: '#2563eb'
86
- });
87
- currentRendererContainer.appendChild(currentTitleElement);
88
+ });
89
+ currentRendererContainer.appendChild(currentTitleElement);
90
+ }
91
+ parentElementToClear.appendChild(currentRendererContainer);
88
92
 
93
+ const smallestParentEdge = Math.min(width, height);
94
+ squareSize = Math.floor(smallestParentEdge / DEFAULT_NUM_COLS);
89
95
  currentBoardElement = document.createElement('div');
90
96
  Object.assign(currentBoardElement.style, {
91
97
  display: 'grid',
92
- gridTemplateColumns: `repeat(${cols}, 50px)`,
93
- gridTemplateRows: `repeat(${rows}, 50px)`,
94
- width: `${cols * 50}px`,
95
- height: `${rows * 50}px`,
98
+ gridTemplateColumns: `repeat(${cols}, ${squareSize}px)`,
99
+ gridTemplateRows: `repeat(${rows}, ${squareSize}px)`,
100
+ width: `${cols * squareSize}px`,
101
+ height: `${rows * squareSize}px`,
96
102
  border: '2px solid #333'
97
103
  });
98
104
 
@@ -101,8 +107,8 @@ function renderer(options) {
101
107
  const square = document.createElement('div');
102
108
  square.id = `cell-${r}-${c}`;
103
109
  Object.assign(square.style, {
104
- width: '50px',
105
- height: '50px',
110
+ width: `${squareSize}px`,
111
+ height: `${squareSize}px`,
106
112
  backgroundColor: (r + c) % 2 === 0 ? LIGHT_SQUARE_COLOR : DARK_SQUARE_COLOR,
107
113
  display: 'flex',
108
114
  alignItems: 'center',
@@ -126,7 +132,9 @@ function renderer(options) {
126
132
  maxWidth: '90vw',
127
133
  marginTop: '20px'
128
134
  });
129
- currentRendererContainer.appendChild(statusContainer);
135
+ if (!environment.viewer) {
136
+ currentRendererContainer.appendChild(statusContainer);
137
+ }
130
138
 
131
139
  currentStatusTextElement = document.createElement('p');
132
140
  Object.assign(currentStatusTextElement.style, {
@@ -144,8 +152,6 @@ function renderer(options) {
144
152
  });
145
153
  statusContainer.appendChild(currentWinnerTextElement);
146
154
 
147
- parentElementToClear.appendChild(currentRendererContainer);
148
-
149
155
  if (typeof document !== 'undefined' && !document.body.hasAttribute('data-renderer-initialized')) {
150
156
  document.body.setAttribute('data-renderer-initialized', 'true');
151
157
  }
@@ -204,8 +210,9 @@ function renderer(options) {
204
210
  }
205
211
 
206
212
 
207
- const { board, activeColor, is_terminal, winner } = gameStateToDisplay;
213
+ const { board, activeColor, isTerminal, winner } = gameStateToDisplay;
208
214
 
215
+ const pieceSize = Math.floor(squareSize * 0.9);
209
216
  for (let r_data = 0; r_data < displayRows; r_data++) {
210
217
  for (let c_data = 0; c_data < displayCols; c_data++) {
211
218
  const piece = board[r_data][c_data];
@@ -213,8 +220,8 @@ function renderer(options) {
213
220
  if (squareElement && piece) {
214
221
  const pieceImg = document.createElement('img');
215
222
  pieceImg.src = PIECE_SVG_URLS[piece];
216
- pieceImg.style.width = '45px';
217
- pieceImg.style.height = '45px';
223
+ pieceImg.style.width = `${pieceSize}px`;
224
+ pieceImg.style.height = `${pieceSize}px`;
218
225
  squareElement.appendChild(pieceImg);
219
226
  }
220
227
  }
@@ -222,7 +229,7 @@ function renderer(options) {
222
229
 
223
230
  currentStatusTextElement.innerHTML = '';
224
231
  currentWinnerTextElement.innerHTML = '';
225
- if (is_terminal) {
232
+ if (isTerminal) {
226
233
  currentStatusTextElement.textContent = "Game Over!";
227
234
  if (winner) {
228
235
  if (String(winner).toLowerCase() === 'draw') {
@@ -235,8 +242,8 @@ function renderer(options) {
235
242
  currentWinnerTextElement.textContent = "Game ended.";
236
243
  }
237
244
  } else {
238
- const playerColor = String(activeColor).toLowerCase() === 'w' ? 'White' : 'Black';
239
- currentStatusTextElement.innerHTML = `Current Player: <span style="font-weight: bold;">${playerColor}</span>`;
245
+ const playerColor = String(activeColor).toLowerCase() === 'w' ? 'White' : 'Black';
246
+ currentStatusTextElement.innerHTML = `Current Player: <span style="font-weight: bold;">${playerColor}</span>`;
240
247
  }
241
248
  }
242
249
 
@@ -278,10 +285,10 @@ function renderer(options) {
278
285
  const fen = observationForRenderer.observationString;
279
286
  const parsedFen = _parseFen(fen);
280
287
  if (parsedFen) {
281
- // Assuming `is_terminal` and `winner` are provided in the top-level observation
288
+ // Assuming `isTerminal` and `winner` are provided in the top-level observation
282
289
  gameSpecificState = {
283
290
  ...parsedFen,
284
- is_terminal: observationForRenderer.isTerminal,
291
+ isTerminal: observationForRenderer.isTerminal,
285
292
  winner: observationForRenderer.winner
286
293
  };
287
294
  }
@@ -291,4 +298,4 @@ function renderer(options) {
291
298
  }
292
299
 
293
300
  _renderBoardDisplay(gameSpecificState, DEFAULT_NUM_ROWS, DEFAULT_NUM_COLS);
294
- }
301
+ }
@@ -19,6 +19,7 @@ DONE = "DONE"
19
19
  INACTIVE = "INACTIVE"
20
20
  ACTIVE = "ACTIVE"
21
21
  TIMEOUT = "TIMEOUT"
22
+ INVALID = "INVALID"
22
23
 
23
24
  _log = logging.getLogger(__name__)
24
25
  _log.setLevel(logging.INFO)
@@ -41,8 +42,16 @@ for proxy_file in GAMES_DIR.glob("**/*_proxy.py"):
41
42
 
42
43
 
43
44
  # --- Constants ---
44
- DEFAULT_ACT_TIMEOUT = 120
45
- DEFAULT_RUN_TIMEOUT = 1200
45
+ # TODO(jhtschultz): Make this configurable per-game. For instance, in poker, a
46
+ # invalid action would likely result in a fold, forfeiting the player's
47
+ # contribution to the pot.
48
+ DEFAULT_INVALID_ACTION_REWARD = -1
49
+
50
+ # Can be used by agents to signal an internal error to the environement.
51
+ AGENT_ERROR_ACTION = -2
52
+
53
+ DEFAULT_ACT_TIMEOUT = 60 * 60 # sixty minutes
54
+ DEFAULT_RUN_TIMEOUT = 60 * 60 * 30 # thirty hours
46
55
  # Buffer in addition to max game length to account for timeouts, retrys, etc.
47
56
  DEFAULT_STEP_BUFFER = 100
48
57
  # TODO(jhtschultz): Add individual game descriptions.
@@ -71,6 +80,11 @@ CONFIGURATION_SPEC_TEMPLATE = {
71
80
  "type": "object",
72
81
  "default": {}
73
82
  },
83
+ "metadata": {
84
+ "description": "Arbitrary metadata.",
85
+ "type": "object",
86
+ "default": {}
87
+ },
74
88
  }
75
89
 
76
90
  OBSERVATION_SPEC_TEMPLATE = {
@@ -113,6 +127,10 @@ OBSERVATION_SPEC_TEMPLATE = {
113
127
  "description": "Boolean indicating game end.",
114
128
  "type": "boolean"
115
129
  },
130
+ "serializedGameAndState": {
131
+ "description": "Enables reconstructing the Game and State objects.",
132
+ "type": "string"
133
+ },
116
134
  "remainingOverageTime": 60,
117
135
  "step": 0
118
136
  },
@@ -171,19 +189,6 @@ def interpreter(
171
189
  os_game = env.os_game
172
190
  os_state = env.os_state
173
191
  num_players = os_game.num_players()
174
- statuses = [
175
- kaggle_state[player_id].status for player_id in range(num_players)
176
- ]
177
- if not any(status == ACTIVE for status in statuses):
178
- for player_id in range(num_players):
179
- p = kaggle_state[player_id]
180
- if p.status in [ERROR, TIMEOUT]:
181
- # TODO: properly set the reward per game
182
- p.reward = -1
183
- elif p.status in [INACTIVE]:
184
- p.reward = 1
185
- p.status = DONE
186
- return;
187
192
 
188
193
  # TODO(jhtschultz): Test reset behavior.
189
194
  is_initial_step = len(env.steps) == 1
@@ -193,31 +198,36 @@ def interpreter(
193
198
 
194
199
  # --- Apply agent action ---
195
200
  acting_agent = os_state.current_player()
196
- action_submitted = None
197
- action_applied = None
201
+ action_submitted: int | None = None
202
+ action_submitted_to_string: str | None = None
203
+ action_applied: int | None = None
204
+ move_duration: float | None = None
198
205
  if is_initial_step:
199
206
  pass
200
207
  elif 0 <= acting_agent < num_players:
201
- if kaggle_state[acting_agent].status != "ACTIVE":
208
+ if kaggle_state[acting_agent]["status"] != "ACTIVE":
202
209
  pass
203
210
  else:
204
- action_submitted = kaggle_state[acting_agent].action["submission"]
211
+ action_submitted = kaggle_state[acting_agent]["action"]["submission"]
205
212
  if action_submitted in os_state.legal_actions():
206
- try:
207
- os_state.apply_action(action_submitted)
208
- action_applied = action_submitted
209
- env.info['actionHistory'].append(str(action_applied))
210
- env.info['stateHistory'].append(str(os_state))
211
- except Exception as e: # pylint: disable=broad-exception-caught
212
- _log.debug(e)
213
- kaggle_state[acting_agent].status = "ERROR"
214
- else:
215
- kaggle_state[acting_agent].status = "INVALID"
216
- if "duration" in logs[acting_agent]:
217
- move_duration = round(logs[acting_agent]["duration"], 3)
218
- env.info["moveDurations"].append(move_duration)
213
+ action_submitted_to_string = os_state.action_to_string(action_submitted)
214
+ os_state.apply_action(action_submitted)
215
+ action_applied = action_submitted
216
+ env.info['actionHistory'].append(str(action_applied))
217
+ env.info['stateHistory'].append(str(os_state))
218
+ elif action_submitted == AGENT_ERROR_ACTION:
219
+ kaggle_state[acting_agent]["status"] = "ERROR"
219
220
  else:
220
- env.info["moveDurations"].append(None)
221
+ kaggle_state[acting_agent]["status"] = "INVALID"
222
+ try:
223
+ if "duration" in logs[acting_agent]:
224
+ move_duration = round(logs[acting_agent]["duration"], 3)
225
+ env.info["moveDurations"].append(move_duration)
226
+ else:
227
+ env.info["moveDurations"].append(None)
228
+ except Exception:
229
+ pass # No logs when stepping the env manually.
230
+
221
231
  elif acting_agent == pyspiel.PlayerId.SIMULTANEOUS:
222
232
  raise NotImplementedError
223
233
  elif acting_agent == pyspiel.PlayerId.TERMINAL:
@@ -236,10 +246,36 @@ def interpreter(
236
246
  env.info['stateHistory'].append(str(os_state))
237
247
 
238
248
  # --- Update agent states ---
249
+ agent_error = any(
250
+ kaggle_state[player_id]["status"] in ["TIMEOUT", "ERROR"]
251
+ for player_id in range(num_players)
252
+ )
253
+ if agent_error:
254
+ _log.info("AGENT ERROR DETECTED")
255
+
256
+ invalid_action = any(
257
+ kaggle_state[player_id]["status"] == "INVALID"
258
+ for player_id in range(num_players)
259
+ )
260
+ if invalid_action:
261
+ _log.info("INVALID ACTION DETECTED")
262
+
263
+ status: str | None = None
239
264
  for player_id, agent_state in enumerate(kaggle_state):
240
265
  reward = None
241
- if agent_state.status in ["TIMEOUT", "ERROR", "INVALID"]:
242
- status = agent_state.status
266
+ if agent_error:
267
+ # Set all agent statuses to ERROR in order not to score episode. Preserve
268
+ # TIMEOUT which has the same effect.
269
+ if agent_state["status"] == "TIMEOUT":
270
+ status = "TIMEOUT"
271
+ else:
272
+ status = "ERROR"
273
+ elif invalid_action:
274
+ if agent_state["status"] == "INVALID":
275
+ reward = DEFAULT_INVALID_ACTION_REWARD
276
+ else:
277
+ reward = -DEFAULT_INVALID_ACTION_REWARD
278
+ status = "DONE"
243
279
  elif os_state.is_terminal():
244
280
  status = "DONE"
245
281
  reward = os_state.returns()[player_id]
@@ -251,11 +287,17 @@ def interpreter(
251
287
  )
252
288
  else:
253
289
  status = "INACTIVE"
290
+ assert status is not None
254
291
 
255
292
  info_dict = {}
256
293
  if acting_agent == player_id:
257
294
  info_dict["actionSubmitted"] = action_submitted
295
+ info_dict["actionSubmittedToString"] = action_submitted_to_string
258
296
  info_dict["actionApplied"] = action_applied
297
+ info_dict["timeTaken"] = move_duration
298
+ info_dict[
299
+ "agentSelfReportedStatus"
300
+ ] = kaggle_state[acting_agent]["action"].get("status") if kaggle_state[acting_agent]["action"] else "unknown"
259
301
 
260
302
  obs_update_dict = {
261
303
  "observationString": os_state.observation_string(player_id),
@@ -267,14 +309,17 @@ def interpreter(
267
309
  "currentPlayer": os_state.current_player(),
268
310
  "playerId": player_id,
269
311
  "isTerminal": os_state.is_terminal(),
312
+ "serializedGameAndState": pyspiel.serialize_game_and_state(
313
+ os_game, os_state
314
+ ),
270
315
  }
271
316
 
272
317
  # Apply updates
273
318
  for k, v in obs_update_dict.items():
274
319
  setattr(agent_state.observation, k, v)
275
- agent_state.reward = reward
276
- agent_state.info = info_dict
277
- agent_state.status = status
320
+ agent_state["reward"] = reward
321
+ agent_state["info"] = info_dict
322
+ agent_state["status"] = status
278
323
 
279
324
  return kaggle_state
280
325
 
@@ -400,14 +445,13 @@ def _build_env(game_string: str) -> dict[str, Any]:
400
445
  env_spec["title"] = f"Open Spiel: {short_name}"
401
446
  env_spec["agents"] = [game.num_players()]
402
447
 
403
- env_config = copy.deepcopy(CONFIGURATION_SPEC_TEMPLATE)
404
- env_spec["configuration"] = env_config
448
+ env_config = env_spec["configuration"]
405
449
  env_config["episodeSteps"] = game.max_history_length() + DEFAULT_STEP_BUFFER
406
450
  env_config["openSpielGameString"]["default"] = str(game)
407
451
  env_config["openSpielGameName"]["default"] = short_name
452
+ env_config["openSpielGameParameters"]["default"] = game.get_parameters()
408
453
 
409
- env_obs = copy.deepcopy(OBSERVATION_SPEC_TEMPLATE)
410
- env_spec["observation"] = env_obs
454
+ env_obs = env_spec["observation"]
411
455
  env_obs["properties"]["openSpielGameString"]["default"] = str(game)
412
456
  env_obs["properties"]["openSpielGameName"]["default"] = short_name
413
457
 
@@ -19,15 +19,78 @@ class OpenSpielEnvTest(absltest.TestCase):
19
19
  )
20
20
  self.assertTrue(len(envs) > _REGISTERED_GAMES_THRESHOLD)
21
21
 
22
- def test_tic_tac_toe_playthrough(self):
22
+ def test_tic_tac_toe_agent_playthrough(self):
23
23
  envs = open_spiel_env._register_game_envs(["tic_tac_toe"])
24
- print(envs)
25
24
  env = make("open_spiel_tic_tac_toe", debug=True)
26
25
  env.run(["random", "random"])
27
26
  json = env.toJSON()
28
27
  self.assertEqual(json["name"], "open_spiel_tic_tac_toe")
29
28
  self.assertTrue(all([status == "DONE" for status in json["statuses"]]))
30
29
 
30
+ def test_tic_tac_toe_manual_playthrough(self):
31
+ envs = open_spiel_env._register_game_envs(["tic_tac_toe"])
32
+ env = make("open_spiel_tic_tac_toe", debug=True)
33
+ env.reset()
34
+ env.step([{"submission": -1}, {"submission": -1}]) # Initial setup step.
35
+ env.step([{"submission": 0}, {"submission": -1}])
36
+ env.step([{"submission": -1}, {"submission": 1}])
37
+ env.step([{"submission": 3}, {"submission": -1}])
38
+ env.step([{"submission": -1}, {"submission": 4}])
39
+ env.step([{"submission": 6}, {"submission": -1}])
40
+ self.assertTrue(env.done)
41
+ self.assertEqual(env.toJSON()["rewards"], [1, -1])
42
+
43
+ def test_invalid_action(self):
44
+ envs = open_spiel_env._register_game_envs(["tic_tac_toe"])
45
+ env = make("open_spiel_tic_tac_toe", debug=True)
46
+ env.reset()
47
+ for i in range(5): # Try repeatedly applying an illegal action
48
+ env.step([
49
+ {"submission": pyspiel.INVALID_ACTION},
50
+ {"submission": pyspiel.INVALID_ACTION},
51
+ ])
52
+ if env.done:
53
+ break
54
+ self.assertEqual(i, 1) # Zeroth step is setup step, should fail next step.
55
+ json = env.toJSON()
56
+ self.assertTrue(all([status == "DONE" for status in json["statuses"]]))
57
+ self.assertEqual(
58
+ json["rewards"],
59
+ [
60
+ open_spiel_env.DEFAULT_INVALID_ACTION_REWARD,
61
+ -open_spiel_env.DEFAULT_INVALID_ACTION_REWARD,
62
+ ]
63
+ )
64
+
65
+ def test_serialized_game_and_state(self):
66
+ envs = open_spiel_env._register_game_envs(["tic_tac_toe"])
67
+ env = make("open_spiel_tic_tac_toe", debug=True)
68
+ env.reset()
69
+ env.step([{"submission": -1}, {"submission": -1}]) # Initial setup step.
70
+ kaggle_state = env.step([{"submission": 0}, {"submission": -1}])
71
+ serialize_game_and_state = kaggle_state[1]["observation"]["serializedGameAndState"]
72
+ game, state = pyspiel.deserialize_game_and_state(serialize_game_and_state)
73
+ self.assertEqual(game.get_type().short_name, "tic_tac_toe_proxy")
74
+ self.assertEqual(state.history(), [0])
75
+
76
+ def test_agent_error(self):
77
+ envs = open_spiel_env._register_game_envs(["tic_tac_toe"])
78
+ env = make("open_spiel_tic_tac_toe", debug=True)
79
+ env.reset()
80
+ # Setup step
81
+ env.step([
82
+ {"submission": pyspiel.INVALID_ACTION},
83
+ {"submission": pyspiel.INVALID_ACTION},
84
+ ])
85
+ env.step([
86
+ {"submission": open_spiel_env.AGENT_ERROR_ACTION},
87
+ {"submission": pyspiel.INVALID_ACTION},
88
+ ])
89
+ self.assertTrue(env.done)
90
+ json = env.toJSON()
91
+ self.assertEqual(json["rewards"], [None, None])
92
+ self.assertEqual(json["statuses"], ["ERROR", "ERROR"])
93
+
31
94
 
32
95
  if __name__ == '__main__':
33
- absltest.main()
96
+ absltest.main()
@@ -199,12 +199,7 @@
199
199
  ${processing && h`<${Processing} />`}
200
200
  </div>`;
201
201
  })`
202
- background-color: #000b2a;
203
- background-image: radial-gradient(
204
- circle closest-side,
205
- #000b49,
206
- #000b2a
207
- );
202
+ background-color: #1C1D20;
208
203
  display: flex;
209
204
  flex: 1;
210
205
  overflow: hidden;
@@ -242,7 +237,7 @@
242
237
  </ul>`)}
243
238
  </div>`;
244
239
  })`
245
- background-color: #000b2a;
240
+ background-color: #1C1D20;
246
241
  font-family: sans-serif;
247
242
  font-size: 14px;
248
243
  height: 48px;
@@ -442,7 +437,6 @@
442
437
  })`
443
438
  align-items: center;
444
439
  background: #212121;
445
- border: 4px solid #212121;
446
440
  box-sizing: border-box;
447
441
  display: flex;
448
442
  flex-direction: column;
@@ -450,6 +444,10 @@
450
444
  justify-content: center;
451
445
  position: relative;
452
446
  width: 100%;
447
+
448
+ &:not(.no-border) {
449
+ border: 4px solid #212121;
450
+ }
453
451
  `;
454
452
 
455
453
  const App = () => {
@@ -545,7 +543,7 @@
545
543
  // Initialize context with window.kaggle.
546
544
  updateContext(window.kaggle || {});
547
545
 
548
- if (window.kaggle.playing) {
546
+ if (window.kaggle?.playing) {
549
547
  play(true);
550
548
  }
551
549
 
@@ -556,10 +554,49 @@
556
554
  // Ensure the environment names match before updating.
557
555
  try {
558
556
  if (
559
- event.data.environment.name ==
560
- contextRef.current.environment.name
557
+ event.data.environment?.name == contextRef.current.environment.name ||
558
+ event.data.environment?.name && !contextRef.current.environment.name ||
559
+ event.data.environment?.loading !== contextRef.current.environment.loading
561
560
  ) {
562
- updateContext(event.data);
561
+ const nextContext = {
562
+ ...event.data,
563
+ environment: {
564
+ ...event.data.environment,
565
+ steps: event.data.environment?.steps ?? []
566
+ }
567
+ };
568
+ window.kaggle = nextContext;
569
+ if (!window.kaggle.renderer) {
570
+ window.kaggle.renderer = window.renderer;
571
+ }
572
+ updateContext(nextContext);
573
+ }
574
+ if (event.data.setSteps) {
575
+ const nextContext = {
576
+ ...(window.kaggle ?? {}),
577
+ environment: {
578
+ ...(window.kaggle?.environment ?? {}),
579
+ steps: event.data.setSteps
580
+ }
581
+ }
582
+ window.kaggle = nextContext;
583
+ if (!window.kaggle.renderer) {
584
+ window.kaggle.renderer = window.renderer;
585
+ }
586
+ updateContext(nextContext)
587
+ }
588
+
589
+ // Side load the renderer script if it is specified by the post message event and not already loaded.
590
+ const renderScriptId = 'rendererScript'
591
+ if (event.data.environment?.rendererUrl && !document.querySelector(`#${renderScriptId}`)) {
592
+ window.rendererUrl = event.data.environment?.rendererUrl;
593
+ const script = document.createElement('script');
594
+ script.id = renderScriptId;
595
+ script.onload = () => {
596
+ window.kaggle.renderer = window.renderer;
597
+ };
598
+ script.src = window.rendererUrl;
599
+ document.body.appendChild(script);
563
600
  }
564
601
  } catch {}
565
602
  },
@@ -674,7 +711,7 @@
674
711
 
675
712
  return h`
676
713
  <${Context.Provider} value=${contextRef.current}>
677
- <${Player} />
714
+ <${Player} className="${contextRef.current.environment.viewer ? 'no-border' : ''}" />
678
715
  <//>`;
679
716
  };
680
717
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaggle-environments
3
- Version: 1.17.6
3
+ Version: 1.17.7
4
4
  Summary: Kaggle Environments
5
5
  Home-page: https://github.com/Kaggle/kaggle-environments
6
6
  Author: Kaggle
@@ -1,4 +1,4 @@
1
- kaggle_environments/__init__.py,sha256=7GZjb3wxfZNOWoRk8zrsHJne9OMzVOMNomEwrP7CXI8,2175
1
+ kaggle_environments/__init__.py,sha256=GzrKQBWH-dKbhfDK2K0GYTnHxw31m8DhnbzcHNMRn7c,2175
2
2
  kaggle_environments/agent.py,sha256=j9rLnCK_Gy0eRIuvlJ9vcMh3vxn-Wvu-pjCpannOolc,6703
3
3
  kaggle_environments/api.py,sha256=eLBKqr11Ku4tdsMUdUqy74FIVEA_hdV3_QUpX84x3Z8,798
4
4
  kaggle_environments/core.py,sha256=Ense_M-2rP4KmVeuKFjM0NQ8M6ucUZTbhwrGekyR9LY,27857
@@ -8,7 +8,7 @@ kaggle_environments/main.py,sha256=10wtcEFcGIjdOd9AEps5WOAwslc6Wsx3eZ43LXJa8jE,1
8
8
  kaggle_environments/schemas.json,sha256=zGzLyhqPdH6QQ0d48SrP5LKbvupprrWvgfQBerLqmhw,3307
9
9
  kaggle_environments/status_codes.json,sha256=6a8HuS_Vth95W0f2fov21QLdRfA3KbizUvjKmJhYtBc,995
10
10
  kaggle_environments/utils.py,sha256=FcI17PA4QK2-hyNp0dryS0TQ2pFlH9K19zDUMmuF4-E,5713
11
- kaggle_environments/envs/chess/chess.js,sha256=sZBnS1RKkP1kss2FrxMI6ceurjnlAA4yUJU_jfkgCZw,274447
11
+ kaggle_environments/envs/chess/chess.js,sha256=QYA1arYF_qOfchN8HHQ4hL5z3C9XeT5hYigT75CgvKo,274596
12
12
  kaggle_environments/envs/chess/chess.json,sha256=fc8Qa1IHWmYbiEgorXIjMj2JmqP95x_reqFe6Mon2Wk,1692
13
13
  kaggle_environments/envs/chess/chess.py,sha256=41LMA8--APQOhl6aeWgJvkSaRBjAch2FgS7ELIoOT3w,245361
14
14
  kaggle_environments/envs/chess/test_chess.py,sha256=ayZEIWRSD3D6BqhJv0q_M5a-j8-pMm8kb4ACAW-DhBE,1748
@@ -191,12 +191,12 @@ kaggle_environments/envs/mab/mab.py,sha256=bkSIxkstS98Vr3eOA9kxQkseDqa1MlG2Egfze
191
191
  kaggle_environments/envs/open_spiel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
192
  kaggle_environments/envs/open_spiel/html_playthrough_generator.py,sha256=qzvi9wMZKz1WfpaIpBxP5LPlVYKAk9npEtdKLXYHMWo,950
193
193
  kaggle_environments/envs/open_spiel/observation.py,sha256=yrJ_iZ9sBUTB6YOyEpKNwYiQEWmsPPtaDYtL4zsw1Ko,4834
194
- kaggle_environments/envs/open_spiel/open_spiel.py,sha256=2SRuz08htViEINCQ7odHCA0UeLQ7Tj0AEz__1w1PLvI,15914
194
+ kaggle_environments/envs/open_spiel/open_spiel.py,sha256=HzN1ngjeOqxLoetI1g56PdlioRY26VoCJMQpbD4S-Vk,17517
195
195
  kaggle_environments/envs/open_spiel/proxy.py,sha256=8Shane4KWYKvbP9nV3l8VQfAFOfFSUrS78h_4xQthVM,4881
196
196
  kaggle_environments/envs/open_spiel/proxy_test.py,sha256=QkmRo_uS0DgDDm2pbU2vwal5KOMCWKw92rC2_g3MziM,1837
197
- kaggle_environments/envs/open_spiel/test_open_spiel.py,sha256=YhmEyyxc68JmjzmsdoIUEO-ds1O8HiICsiCKkMFth5s,1033
197
+ kaggle_environments/envs/open_spiel/test_open_spiel.py,sha256=MwyjH-e00-3SP8_r10drYTFvplacbo0cDCI0XKtE4wU,3596
198
198
  kaggle_environments/envs/open_spiel/games/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
- kaggle_environments/envs/open_spiel/games/chess/chess.js,sha256=hQktt0rQ4msQKUiR1Z4qWi__aK6KcUSNPn6eP54TF1U,12543
199
+ kaggle_environments/envs/open_spiel/games/chess/chess.js,sha256=e5QFnMCaNXMRskO0qbdhy7q0W3ov9yFGmvfpC48Dd6k,12931
200
200
  kaggle_environments/envs/open_spiel/games/connect_four/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
201
  kaggle_environments/envs/open_spiel/games/connect_four/connect_four.js,sha256=iO74ar3Hh64VYEx9v3eysgrPU-Mcokl9dkFxie6uISg,14893
202
202
  kaggle_environments/envs/open_spiel/games/connect_four/connect_four_proxy.py,sha256=2otG99felDYhNhWpsadbM9YUaHtrXqhV1GFNEHhuPwA,2348
@@ -223,10 +223,10 @@ kaggle_environments/envs/tictactoe/test_tictactoe.py,sha256=6CgQbRz-yxNoMfD5tzmC
223
223
  kaggle_environments/envs/tictactoe/tictactoe.js,sha256=NZDT-oSG0a6a-rso9Ldh9qkJwVrxrAsjKUC3_tJu3tw,8002
224
224
  kaggle_environments/envs/tictactoe/tictactoe.json,sha256=zMXZ8-fpT7FBhzz2FFBvRLn4XwtngjEqOieMvI6cCj8,1121
225
225
  kaggle_environments/envs/tictactoe/tictactoe.py,sha256=uq3sTHWNMg0dxX2v9pTbJAKM7fwerxQt7OQjCX96m-Y,3657
226
- kaggle_environments/static/player.html,sha256=XyVoe0XxMa2MO1fTDY_rjyjzPN-JZgbVwJIDoLSnlw0,23016
227
- kaggle_environments-1.17.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
228
- kaggle_environments-1.17.6.dist-info/METADATA,sha256=kFwWfkd4-DwVzrxYOSZyUoRCERzdFwLzMJo-61FsWkk,10955
229
- kaggle_environments-1.17.6.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
230
- kaggle_environments-1.17.6.dist-info/entry_points.txt,sha256=HbVC-LKGQFV6lEEYBYyDTtrkHgdHJUWQ8_qt9KHGqz4,70
231
- kaggle_environments-1.17.6.dist-info/top_level.txt,sha256=v3MMWIPMQFcI-WuF_dJngHWe9Bb2yH_6p4wat1x4gAc,20
232
- kaggle_environments-1.17.6.dist-info/RECORD,,
226
+ kaggle_environments/static/player.html,sha256=TTxN-EU7_KCNIVDX4E4TrZ61FRWkGUDHMSbnUYsQSvg,24975
227
+ kaggle_environments-1.17.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
228
+ kaggle_environments-1.17.7.dist-info/METADATA,sha256=va0iBjyCpXB4osbBWVdDGdhgUUig8nPEIR540OJU7vM,10955
229
+ kaggle_environments-1.17.7.dist-info/WHEEL,sha256=7wAbZI8A1UjN-j4-aYf66qBxOZ0Ioy0QNykkY5NcGJo,109
230
+ kaggle_environments-1.17.7.dist-info/entry_points.txt,sha256=HbVC-LKGQFV6lEEYBYyDTtrkHgdHJUWQ8_qt9KHGqz4,70
231
+ kaggle_environments-1.17.7.dist-info/top_level.txt,sha256=v3MMWIPMQFcI-WuF_dJngHWe9Bb2yH_6p4wat1x4gAc,20
232
+ kaggle_environments-1.17.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (78.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any