add method to check replay for last feasible state (binary search)
This commit is contained in:
parent
ab82ec00ed
commit
d91dec3c81
1 changed files with 70 additions and 0 deletions
70
check_game.py
Normal file
70
check_game.py
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import copy
|
||||||
|
from typing import Tuple, Optional
|
||||||
|
|
||||||
|
from database import conn
|
||||||
|
from compress import decompress_deck, decompress_actions, compress_actions, link
|
||||||
|
from hanabi import Action, HanabiInstance, GameState
|
||||||
|
from sat import solve_sat
|
||||||
|
from download_data import export_game
|
||||||
|
|
||||||
|
|
||||||
|
# returns number of first turn before which the game was infeasible (counting from 0)
|
||||||
|
# and a replay achieving maximum score (from this turn onwards) if instance is feasible
|
||||||
|
# 0 if instance is infeasible
|
||||||
|
# 1 if instance is feasible but first turn is suboptimal
|
||||||
|
# ...
|
||||||
|
# (number of turns) if replay was winning
|
||||||
|
def check_game(game_id: int) -> Tuple[int, GameState]:
|
||||||
|
with conn.cursor() as cur:
|
||||||
|
cur.execute("SELECT games.num_players, deck, actions, score, games.variant_id FROM games "
|
||||||
|
"INNER JOIN seeds ON seeds.seed = games.seed "
|
||||||
|
"WHERE games.id = (%s)",
|
||||||
|
(game_id,)
|
||||||
|
)
|
||||||
|
(num_players, compressed_deck, compressed_actions, score, variant_id) = cur.fetchone()
|
||||||
|
deck = decompress_deck(compressed_deck)
|
||||||
|
actions = decompress_actions(compressed_actions)
|
||||||
|
|
||||||
|
instance = HanabiInstance(deck, num_players, variant_id=variant_id)
|
||||||
|
|
||||||
|
if instance.max_score == score:
|
||||||
|
# instance has been won, nothing to compute here
|
||||||
|
return len(actions)
|
||||||
|
|
||||||
|
# store the turn numbers *before* we know the game was (in)feasible
|
||||||
|
solvable_turn = 0
|
||||||
|
unsolvable_turn = len(actions)
|
||||||
|
|
||||||
|
# first, check if the instance itself is feasible:
|
||||||
|
game = GameState(instance)
|
||||||
|
solvable, solution = solve_sat(game)
|
||||||
|
if not solvable:
|
||||||
|
return -1, solution
|
||||||
|
|
||||||
|
while (unsolvable_turn - solvable_turn > 1):
|
||||||
|
try_turn = (unsolvable_turn + solvable_turn) // 2
|
||||||
|
try_game = copy.deepcopy(game)
|
||||||
|
assert(len(try_game.actions) == solvable_turn)
|
||||||
|
for a in range(solvable_turn, try_turn):
|
||||||
|
try_game.make_action(actions[a])
|
||||||
|
solvable, potential_sol = solve_sat(try_game)
|
||||||
|
if solvable:
|
||||||
|
solution = potential_sol
|
||||||
|
game = try_game
|
||||||
|
solvable_turn = try_turn
|
||||||
|
else:
|
||||||
|
unsolvable_turn = try_turn
|
||||||
|
|
||||||
|
assert(unsolvable_turn - 1 == solvable_turn)
|
||||||
|
return unsolvable_turn, solution
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
game_id = 961092
|
||||||
|
export_game(game_id)
|
||||||
|
print("checking game {}".format(game_id))
|
||||||
|
turn, sol = check_game(game_id)
|
||||||
|
if turn != 0:
|
||||||
|
print(turn, link(sol))
|
||||||
|
else:
|
||||||
|
print("instance is unfeasible")
|
||||||
|
pass
|
Loading…
Reference in a new issue