Store cert games in DB. Check for bottom/topdeck losses
This commit is contained in:
parent
3e5f727eda
commit
6ad728de4d
3 changed files with 135 additions and 8 deletions
|
@ -93,7 +93,7 @@ def load_instance(seed: str) -> Optional[hanabi.live.hanab_live.HanabLiveInstanc
|
|||
return hanabi.live.hanab_live.HanabLiveInstance(deck, num_players, var_id)
|
||||
|
||||
|
||||
def load_game_parts(game_id: int) -> Tuple[hanabi.live.hanab_live.HanabLiveInstance, List[hanabi.hanab_game.Action]]:
|
||||
def load_game_parts(game_id: int, cert_game: bool = False) -> Tuple[hanabi.live.hanab_live.HanabLiveInstance, List[hanabi.hanab_game.Action]]:
|
||||
"""
|
||||
Loads information on game from database
|
||||
@param game_id: ID of game
|
||||
|
@ -119,7 +119,7 @@ def load_game_parts(game_id: int) -> Tuple[hanabi.live.hanab_live.HanabLiveInsta
|
|||
# Unpack results now
|
||||
(num_players, seed, one_extra_card, one_less_card, deck_plays, all_or_nothing, clue_starved, variant_name, variant_id, throw_it_in_a_hole) = res
|
||||
|
||||
actions = load_actions(game_id)
|
||||
actions = load_actions(game_id, cert_game)
|
||||
deck = load_deck(seed)
|
||||
|
||||
instance = hanabi.live.hanab_live.HanabLiveInstance(
|
||||
|
@ -136,8 +136,8 @@ def load_game_parts(game_id: int) -> Tuple[hanabi.live.hanab_live.HanabLiveInsta
|
|||
return instance, actions
|
||||
|
||||
|
||||
def load_game(game_id: int) -> hanabi.live.hanab_live.HanabLiveGameState:
|
||||
instance, actions = load_game_parts(game_id)
|
||||
def load_game(game_id: int, cert_game: bool = False) -> hanabi.live.hanab_live.HanabLiveGameState:
|
||||
instance, actions = load_game_parts(game_id, cert_game)
|
||||
game = hanabi.live.hanab_live.HanabLiveGameState(instance)
|
||||
for action in actions:
|
||||
game.make_action(action)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import hanabi.live.compress
|
||||
from hanabi.hanab_game import DeckCard
|
||||
from hanabi import database
|
||||
from hanabi.live.variants import Variant
|
||||
from hanabi.database import games_db_interface
|
||||
import random
|
||||
|
||||
from src.hanabi.solvers.sat import solve_sat
|
||||
|
||||
|
||||
def get_deck(variant: Variant):
|
||||
deck = []
|
||||
for suit_index, suit in enumerate(variant.suits):
|
||||
|
@ -33,6 +37,20 @@ def generate_deck(variant: Variant, num_players: int, seed: int, seed_class: int
|
|||
random.shuffle(deck)
|
||||
return seed, deck
|
||||
|
||||
def link():
|
||||
seed = "p5v0sunblinkingly-kobe-prescriptively"
|
||||
|
||||
deck = database.games_db_interface.load_deck(seed)
|
||||
database.cur.execute("SELECT id FROM certificate_games WHERE seed = %s", (seed,))
|
||||
(game_id, ) = database.cur.fetchone()
|
||||
actions = database.games_db_interface.load_actions(game_id, True)
|
||||
inst = hanabi.hanab_game.HanabiInstance(deck, 5)
|
||||
game = hanabi.hanab_game.GameState(inst)
|
||||
for action in actions:
|
||||
game.make_action(action)
|
||||
|
||||
print(hanabi.live.compress.link(game))
|
||||
|
||||
def generate_decks_for_variant(variant_id: int, num_players: int, num_seeds: int, seed_class: int = 1):
|
||||
variant = Variant.from_db(variant_id)
|
||||
for seed_num in range(num_seeds):
|
||||
|
@ -48,7 +66,8 @@ def generate_decks_for_variant(variant_id: int, num_players: int, num_seeds: int
|
|||
def main():
|
||||
database.global_db_connection_manager.read_config()
|
||||
database.global_db_connection_manager.connect()
|
||||
generate_decks_for_variant(0, 2, 100)
|
||||
link()
|
||||
# generate_decks_for_variant(0, 2, 100)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
import collections
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from typing import List, Any, Optional, Tuple
|
||||
from dataclasses import dataclass
|
||||
|
||||
import alive_progress
|
||||
|
||||
import hanabi.hanab_game
|
||||
from hanabi import database
|
||||
from hanabi import logger
|
||||
from hanabi import hanab_game
|
||||
from hanabi.hanab_game import DeckCard
|
||||
from hanabi.live import compress
|
||||
|
||||
from hanabi.database import games_db_interface
|
||||
|
||||
|
||||
class InfeasibilityType(Enum):
|
||||
Pace = 0 # idx denotes index of last card drawn before being forced to reduce pace, value denotes how bad pace is
|
||||
|
@ -21,11 +26,13 @@ class InfeasibilityType(Enum):
|
|||
HandSizeWithSqueeze = 12
|
||||
HandSizeWithBdr = 13
|
||||
HandSizeWithBdrSqueeze = 14
|
||||
BottomTopDeck = 20
|
||||
|
||||
# further reasons, currently not scanned for
|
||||
BottomTopDeck = 20
|
||||
DoubleBottomTopDeck = 30
|
||||
CritAtBottom = 40
|
||||
|
||||
# Default reason when we have nothing else
|
||||
SAT = 50
|
||||
|
||||
|
||||
|
@ -46,6 +53,77 @@ class InfeasibilityReason:
|
|||
return "Critical non-5 at bottom"
|
||||
|
||||
|
||||
def generate_all_choices(l: List[List[Any]]):
|
||||
if len(l) == 0:
|
||||
yield []
|
||||
return
|
||||
head, *tail = l
|
||||
for option in head:
|
||||
for back in generate_all_choices(tail):
|
||||
yield [option] + back
|
||||
|
||||
def check_for_top_bottom_deck_loss(instance: hanab_game.HanabiInstance) -> bool:
|
||||
hands = [instance.deck[p * instance.hand_size : (p+1) * instance.hand_size] for p in range(instance.num_players)]
|
||||
|
||||
# scan the deck in reverse order if any card is forced to be late
|
||||
found = {}
|
||||
# Note that only the last 4 cards are relevant for single-suit distribution loss
|
||||
for i, card in enumerate(reversed(instance.deck[-4:])):
|
||||
if card in found.keys():
|
||||
found[card] += 1
|
||||
else:
|
||||
found[card] = 1
|
||||
|
||||
if found[card] >= 3 or (card.rank != 1 and found[card] >= 2):
|
||||
max_rank_starting_extra_round = card.rank + (instance.deck_size - card.deck_index - 2)
|
||||
|
||||
# Next, need to figure out what positions of cards of the same suit are fixed
|
||||
positions_by_rank = [[] for _ in range(6)]
|
||||
for rank in range(max_rank_starting_extra_round, 6):
|
||||
for player, hand in enumerate(hands):
|
||||
card_test = DeckCard(card.suitIndex, rank)
|
||||
for card_hand in hand:
|
||||
if card_test == card_hand:
|
||||
positions_by_rank[rank].append(player)
|
||||
|
||||
|
||||
# clean up where we have free choice anyway
|
||||
for rank, positions in enumerate(positions_by_rank):
|
||||
if rank != 5 and len(positions) < 2:
|
||||
positions.clear()
|
||||
if len(positions) == 0:
|
||||
positions.append(None)
|
||||
|
||||
|
||||
|
||||
# Now, iterate through all choices in starting hands (None stands for free choice of a card) and check them
|
||||
assignment_found = False
|
||||
for assignment in generate_all_choices(positions_by_rank):
|
||||
cur_player = None
|
||||
num_turns = 0
|
||||
for rank in range(max_rank_starting_extra_round, 6):
|
||||
if cur_player is None or assignment[rank] is None:
|
||||
num_turns += 1
|
||||
else:
|
||||
# Note the -1 and +1 to output things in range [1,5] instead of [0,4]
|
||||
num_turns += (assignment[rank] - cur_player - 1) % instance.num_players + 1
|
||||
|
||||
if assignment[rank] is not None:
|
||||
cur_player = assignment[rank]
|
||||
elif cur_player is not None:
|
||||
cur_player = (cur_player + 1) % instance.num_players
|
||||
|
||||
if num_turns <= instance.num_players + 1:
|
||||
assignment_found = True
|
||||
|
||||
# If no assignment worked out, the deck is infeasible because of this suit
|
||||
if not assignment_found:
|
||||
return True
|
||||
|
||||
# If we reach this point, we checked for every card near the bottom of the deck and found a possible endgame each
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def analyze(instance: hanab_game.HanabiInstance, only_find_first=False) -> List[InfeasibilityReason]:
|
||||
"""
|
||||
|
@ -65,6 +143,12 @@ def analyze(instance: hanab_game.HanabiInstance, only_find_first=False) -> List[
|
|||
"""
|
||||
reasons = []
|
||||
|
||||
top_bottom_deck_loss = check_for_top_bottom_deck_loss(instance)
|
||||
if top_bottom_deck_loss:
|
||||
reasons.append(InfeasibilityReason(InfeasibilityType.BottomTopDeck))
|
||||
if only_find_first:
|
||||
return reasons
|
||||
|
||||
# check for critical non-fives at bottom of the deck
|
||||
bottom_card = instance.deck[-1]
|
||||
if bottom_card.rank != 5 and bottom_card.suitIndex in instance.dark_suits:
|
||||
|
@ -133,7 +217,8 @@ def analyze(instance: hanab_game.HanabiInstance, only_find_first=False) -> List[
|
|||
artificial_crits.add(filtered_deck[-2])
|
||||
|
||||
# Last card in the deck can never be played
|
||||
artificial_crits.add(filtered_deck[-1])
|
||||
if instance.deck[-1].rank != 5:
|
||||
artificial_crits.add(instance.deck[-1])
|
||||
|
||||
for (i, card) in enumerate(instance.deck):
|
||||
if card.rank == stacks[card.suitIndex] + 1:
|
||||
|
@ -240,3 +325,26 @@ def run_on_database(variant_id):
|
|||
)
|
||||
bar()
|
||||
database.conn.commit()
|
||||
|
||||
|
||||
def main():
|
||||
seed = "p5v0sporcupines-underclass-phantasmagorical"
|
||||
seed = 'p5c1s98804'
|
||||
seed = 'p4c1s1116'
|
||||
seed = 'p5c1s14459'
|
||||
num_players = 5
|
||||
database.global_db_connection_manager.read_config()
|
||||
database.global_db_connection_manager.connect()
|
||||
|
||||
database.cur.execute("SELECT seed, num_players FROM seeds WHERE (feasible IS NULL OR feasible = false) AND class = 1 AND num_players = 5")
|
||||
# for (seed, num_players) in database.cur.fetchall():
|
||||
for _ in range(1):
|
||||
deck = database.games_db_interface.load_deck(seed)
|
||||
inst = hanabi.hanab_game.HanabiInstance(deck, num_players)
|
||||
lost = check_for_top_bottom_deck_loss(inst)
|
||||
if lost:
|
||||
print(seed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue