fix: resolve duel roots and recover legacy snake data
- Resolve selected moves and enemy replies on the same simulated turn. - Add an Apex candidate hook and bump the Prism snake to version 1.1.0. - Rebuild benchmark states from normalized turn data when snapshots are empty. - Synthesize missing game snake identities during legacy database migration. - Add regression coverage for duel timing and partial legacy schemas.
This commit is contained in:
@@ -51,8 +51,8 @@ class TestPrismBattleSnake_GPT_5_6_Sol(unittest.TestCase):
|
||||
snake = PrismBattleSnake_GPT_5_6_Sol()
|
||||
|
||||
self.assertEqual(snake.name, "PrismBattleSnake")
|
||||
self.assertEqual(snake.version, "1.0.1")
|
||||
self.assertEqual(get_snake_version("PrismBattleSnake_GPT_5_6_Sol"), "1.0.1")
|
||||
self.assertEqual(snake.version, "1.1.0")
|
||||
self.assertEqual(get_snake_version("PrismBattleSnake_GPT_5_6_Sol"), "1.1.0")
|
||||
self.assertIsInstance(SnakeBuilder.build("PrismBattleSnake_GPT_5_6_Sol"), PrismBattleSnake_GPT_5_6_Sol)
|
||||
|
||||
def test_bitboard_primitives_match_apex(self):
|
||||
@@ -100,6 +100,50 @@ class TestPrismBattleSnake_GPT_5_6_Sol(unittest.TestCase):
|
||||
|
||||
self.assertGreater(value, 0)
|
||||
|
||||
def test_candidate_search_resolves_enemy_reply_on_the_same_turn(self):
|
||||
board = BitBoard(3, 3)
|
||||
search = BitboardDuelSearch(
|
||||
board=board, food=set(), hazards=set(), hazard_count={},
|
||||
hazard_damage=15, deadline=None,
|
||||
)
|
||||
my_body = [{"x": 0, "y": 1}, {"x": 0, "y": 0}]
|
||||
enemy_body = [{"x": 2, "y": 1}, {"x": 2, "y": 0}]
|
||||
|
||||
value, depth = search.search_candidate(
|
||||
my_body=my_body,
|
||||
enemy_body=enemy_body,
|
||||
my_target=(1, 1),
|
||||
my_health=100,
|
||||
enemy_health=100,
|
||||
max_depth=1,
|
||||
previous_hazards=set(),
|
||||
)
|
||||
|
||||
self.assertEqual(value, -500.0)
|
||||
self.assertEqual(depth, 1)
|
||||
|
||||
def test_candidate_search_does_not_advance_our_snake_twice_at_root(self):
|
||||
board = BitBoard(4, 1)
|
||||
search = BitboardDuelSearch(
|
||||
board=board, food=set(), hazards=set(), hazard_count={},
|
||||
hazard_damage=15, deadline=None,
|
||||
)
|
||||
my_body = [{"x": 0, "y": 0}]
|
||||
enemy_body = [{"x": 3, "y": 0}]
|
||||
|
||||
value, depth = search.search_candidate(
|
||||
my_body=my_body,
|
||||
enemy_body=enemy_body,
|
||||
my_target=(1, 0),
|
||||
my_health=100,
|
||||
enemy_health=100,
|
||||
max_depth=1,
|
||||
previous_hazards=set(),
|
||||
)
|
||||
|
||||
self.assertEqual(value, 0.0)
|
||||
self.assertEqual(depth, 1)
|
||||
|
||||
def test_duel_search_keeps_tail_blocked_when_its_snake_eats(self):
|
||||
board = BitBoard(3, 3)
|
||||
search = BitboardDuelSearch(
|
||||
|
||||
@@ -47,6 +47,54 @@ class TestMigrateGameplayDatabase(unittest.TestCase):
|
||||
'{"color":"#663399","head":"ferret","tail":"swirl"}',
|
||||
))
|
||||
|
||||
def test_copy_game_snakes_synthesizes_rows_when_table_is_empty(self):
|
||||
source = sqlite3.connect(":memory:")
|
||||
destination = sqlite3.connect(":memory:")
|
||||
try:
|
||||
source.execute("""
|
||||
CREATE TABLE game_snakes (
|
||||
game_id TEXT, snake_id TEXT, snake_name TEXT, is_you INTEGER,
|
||||
customizations_json TEXT NOT NULL DEFAULT '{}'
|
||||
)
|
||||
""")
|
||||
source.execute("""
|
||||
CREATE TABLE snake_turns (
|
||||
game_id TEXT, snake_id TEXT, snake_name TEXT, is_you INTEGER
|
||||
)
|
||||
""")
|
||||
source.executemany(
|
||||
"INSERT INTO snake_turns VALUES (?, ?, ?, ?)",
|
||||
[
|
||||
("game-1", "snake-1", "PrismBattleSnake", 1),
|
||||
("game-1", "snake-1", "PrismBattleSnake", 1),
|
||||
("game-1", "snake-2", "Enemy", 0),
|
||||
],
|
||||
)
|
||||
destination.execute("""
|
||||
CREATE TABLE game_snakes (
|
||||
game_id TEXT, snake_id TEXT, snake_name TEXT, is_you INTEGER,
|
||||
customizations_json TEXT NOT NULL DEFAULT '{}',
|
||||
PRIMARY KEY (game_id, snake_id)
|
||||
)
|
||||
""")
|
||||
|
||||
copied = copy_game_snakes(
|
||||
source, destination, batch_size=10, retained_ids={"game-1"},
|
||||
)
|
||||
rows = destination.execute("""
|
||||
SELECT snake_id, snake_name, is_you, customizations_json
|
||||
FROM game_snakes ORDER BY snake_id
|
||||
""").fetchall()
|
||||
finally:
|
||||
source.close()
|
||||
destination.close()
|
||||
|
||||
self.assertEqual(copied, 2)
|
||||
self.assertEqual(rows, [
|
||||
("snake-1", "PrismBattleSnake", 1, "{}"),
|
||||
("snake-2", "Enemy", 0, "{}"),
|
||||
])
|
||||
|
||||
def test_copy_game_snakes_defaults_legacy_schema_to_empty_customizations(self):
|
||||
source = sqlite3.connect(":memory:")
|
||||
destination = sqlite3.connect(":memory:")
|
||||
@@ -60,6 +108,11 @@ class TestMigrateGameplayDatabase(unittest.TestCase):
|
||||
"INSERT INTO game_snakes VALUES (?, ?, ?, ?)",
|
||||
("game-1", "snake-1", "LegacySnake", 0),
|
||||
)
|
||||
source.execute("""
|
||||
CREATE TABLE snake_turns (
|
||||
game_id TEXT, snake_id TEXT, snake_name TEXT, is_you INTEGER
|
||||
)
|
||||
""")
|
||||
destination.execute("""
|
||||
CREATE TABLE game_snakes (
|
||||
game_id TEXT, snake_id TEXT, snake_name TEXT, is_you INTEGER,
|
||||
|
||||
Reference in New Issue
Block a user