diff --git a/check_game.py b/check_game.py index 777475a..ad7ab4b 100644 --- a/check_game.py +++ b/check_game.py @@ -59,7 +59,7 @@ def check_game(game_id: int) -> Tuple[int, GameState]: return unsolvable_turn, solution if __name__ == "__main__": - game_id = 961092 + game_id = 963339 export_game(game_id) print("checking game {}".format(game_id)) turn, sol = check_game(game_id) diff --git a/hanabi.py b/hanabi.py index 9944d71..9a35608 100644 --- a/hanabi.py +++ b/hanabi.py @@ -85,19 +85,20 @@ class HanabiInstance(): self, deck: List[DeckCard], # assumes a default deck, every suit has to be distributed either [1,1,1,2,2,3,3,4,4,5] or [1,2,3,4,5] num_players: int, # number of players that play this deck, in range [2,6] + hand_size: Optional[int] = None, # number of cards that each player holds num_strikes: Optional[int] = None, # number of strikes that leads to game loss clue_starved: bool = False, # if true, discarding and playing fives only gives back half a clue - variant_id: Optional[int] = None # optional: variant id of hanab.live, useful if instance gets exported to be viewed in browser + fives_give_clue: bool = True, # if false, then playing a five will not change the clue count ): - assert(2 <= num_players <= 6) - + # defining properties self.deck = deck self.num_players = num_players self.hand_size = hand_size or constants.HAND_SIZES[self.num_players] self.num_strikes = num_strikes or constants.NUM_STRIKES self.clue_starved = clue_starved + self.fives_give_clue = fives_give_clue # normalize deck indices for (idx, card) in enumerate(self.deck): @@ -119,9 +120,6 @@ class HanabiInstance(): + 8 + (self.num_suits - 1) \ + (-1 if self.num_players >= 5 else 0) - # TODO: set a meaningful default here for export? - self._variant_id: Optional[int] = variant_id - @property def num_dealt_cards(self): return self.num_players * self.hand_size @@ -130,15 +128,6 @@ class HanabiInstance(): def draw_pile_size(self): return self.deck_size - self.num_dealt_cards - @property - def variant_id(self): - if self._variant_id is not None: - return self._variant_id - else: - # ensure no key error can happen - assert(self.is_standard()) - return constants.VARIANT_IDS_STANDARD_DISTRIBUTIONS[self.num_suits][self.num_dark_suits] - @property def max_score(self): return 5 * self.num_suits @@ -147,21 +136,6 @@ class HanabiInstance(): def clue_increment(self): return 0.5 if self.clue_starved else 1 - # returns True if the instance has values matching hanabi-live rules - # (i.e. standard + extra variants with 5 / 6 suits) - def is_standard(self): - return all([ - 2 <= self.num_players <= 6, - self.hand_size == constants.HAND_SIZES[self.num_players], - self.num_strikes == constants.NUM_STRIKES, - 3 <= self.num_suits <= 6, - 0 <= self.num_dark_suits <= 2, - 4 <= self.num_suits - self.num_dark_suits or self.num_suits == 3 - # TODO: check that variant id matches deck distribution - ] - ) - - class GameState(): def __init__(self, instance: HanabiInstance): @@ -210,7 +184,7 @@ class GameState(): card = self.instance.deck[card_idx] if card.rank == self.stacks[card.suitIndex] + 1: self.stacks[card.suitIndex] += 1 - if card.rank == 5 and self.clues != 8: + if card.rank == 5 and self.clues != 8 and self.fives_give_clue: self.clues += self.instance.clue_increment else: self.strikes += 1 diff --git a/sat.py b/sat.py index fa37753..7e2e699 100644 --- a/sat.py +++ b/sat.py @@ -304,7 +304,7 @@ def solve_sat(starting_state: GameState | HanabiInstance) -> Tuple[bool, Optiona model = get_model(constraints) if model: -# print_model(model, game_state, ls) + print_model(model, game_state, ls) solution = evaluate_model(model, copy.deepcopy(game_state), ls) return True, solution else: @@ -355,11 +355,11 @@ def evaluate_model(model, cur_game_state: GameState, ls: Literals) -> GameState: def run_deck(): - puzzle = False + puzzle = True if puzzle: deck_str = 'p5 p3 b4 r5 y4 y4 y5 r4 b2 y2 y3 g5 g2 g3 g4 p4 r3 b2 b3 b3 p4 b1 p2 b1 b1 p2 p1 p1 g1 r4 g1 r1 r3 r1 g1 r1 p1 b4 p3 g2 g3 g4 b5 y1 y1 y1 r2 r2 y2 y3' - deck = [DeckCard(COLORS.index(c[0]), int(c[1])) for c in deck_str.split(" ")] + deck = [DeckCard(COLOR_INITIALS.index(c[0]), int(c[1])) for c in deck_str.split(" ")] num_p = 5 else: deck_str = "15gfvqluvuwaqnmrkpkaignlaxpjbmsprksfcddeybfixchuhtwo" diff --git a/variants.py b/variants.py index 95a5b5a..834b53d 100644 --- a/variants.py +++ b/variants.py @@ -13,3 +13,58 @@ def variant_name(variant_id): def num_suits(variant_id): return next(len(var['suits']) for var in VARIANTS if var['id'] == variant_id) + +def properties(variant_id): + return next(var for var in VARIANTS if var['id'] == variant_id) + + +if __name__ == "__main__": + x = set() + c = set() + for var in VARIANTS: + for k in var.keys(): + x.add(k) + for s in var['suits']: + c.add(s) + for y in x: + print(y) + + for s in c: + print(s) + + # need: suit name -> colors + +""" +# actual changes of theoretical instance +clueStarved +throwItInHole (no clues for fives) + +# general restrictions on what clues are allowed +alternatingClues +clueColors +clueRanks +synesthesia (no rank clused, but color touches rank as well) + +# can be ignored +cowPig +duck + +# -> use oracle? +# clue touch changed +chimneys +funnels +colorCluesTouchNothing +rankCluesTouchNothing +oddsAndEvens (ranks touch ranks of same parity) + +# changes behaviour of ones or fives +specialAllClueColors +specialAllClueRanks +specialNoClueColors +specialNoClueRanks +specialDeceptive +specialRank + +upOrDown +criticalFours +"""