nima1's picture
Publish verified checkpoint and losslessly compressed study evidence
4be6a52 verified
Raw History Blame Contribute Delete
2.8 kB
import copy
import json
import pytest
from stackcraft.engine import legal_actions, new_game, step
from stackcraft.replay import make_replay, replay_states
def episode(seed: int = 123) -> tuple[list[str], list]:
states = [new_game(seed)]
actions = []
while not states[-1].terminal:
action = legal_actions(states[-1])[0].id
actions.append(action)
states.append(step(states[-1], action).state)
return actions, states
def test_json_roundtrip_recreates_every_state_and_terminal_outcome() -> None:
actions, states = episode()
artifact = make_replay(123, actions)
assert replay_states(json.loads(json.dumps(artifact))) == states
assert artifact["final"]["terminal"] is True
assert artifact["final"]["pieces"] == len(actions)
actions.clear()
assert artifact["actions"]
def test_empty_and_partial_games_are_valid_replays() -> None:
assert replay_states(make_replay(1, [])) == [new_game(1)]
actions, states = episode()
assert replay_states(make_replay(123, actions[:3])) == states[:4]
@pytest.mark.parametrize(
"key,value",
[
("schema_version", 2),
("schema_version", True),
("rules_version", "unknown"),
("seed", True),
("seed", "123"),
("actions", "r0x0"),
("actions", [4]),
("actions", ["invalid"]),
("final", {}),
("final", None),
],
)
def test_malformed_or_tampered_metadata_is_rejected(key: str, value: object) -> None:
artifact = make_replay(123, [])
artifact[key] = value
with pytest.raises(ValueError):
replay_states(artifact)
@pytest.mark.parametrize("field", ["score", "lines", "pieces", "terminal"])
def test_tampered_final_outcomes_are_rejected(field: str) -> None:
actions, _ = episode()
artifact = make_replay(123, actions)
artifact["final"][field] = not artifact["final"][field] if field == "terminal" else 999
with pytest.raises(ValueError, match="summary"):
replay_states(artifact)
def test_boolean_cannot_impersonate_numeric_summary() -> None:
artifact = make_replay(123, [])
artifact["final"]["score"] = False
with pytest.raises(ValueError, match="summary"):
replay_states(artifact)
def test_actions_after_game_over_are_rejected() -> None:
actions, _ = episode()
artifact = make_replay(123, actions)
artifact["actions"].append("r0x0")
with pytest.raises(ValueError, match="invalid replay move"):
replay_states(artifact)
def test_summary_optional_and_validation_does_not_mutate_artifact() -> None:
actions, states = episode()
artifact = make_replay(123, actions)
del artifact["final"]
before = copy.deepcopy(artifact)
assert replay_states(artifact) == states
assert artifact == before