def _get_flush_hands(self): upcard_nums = self.upcard_nums() card_suit_locations = {} for r in range(3): for c in range(3): upcard = int_to_card(upcard_nums[r][c]) if upcard is not None: upcard_suit = SUITS[upcard.suit_idx()] if upcard_suit not in card_suit_locations: card_suit_locations[upcard_suit] = [] card_suit_locations[upcard_suit].append((r, c)) flush_suits = set([]) for card_suit in card_suit_locations: if len(card_suit_locations[card_suit]) >= 5: flush_suits.add(card_suit) # print(f"flush_suits = {flush_suits}") flush_hands = [] for flush_suit in flush_suits: suit_locations = card_suit_locations[flush_suit] hands = itertools.combinations(suit_locations, 5) flush_hands.extend([set(hand) for hand in hands]) # print(f"flush_hands = {flush_hands}") return flush_hands
def _get_quad_hands(self): upcard_nums = self.upcard_nums() card_rank_locations = {} for r in range(3): for c in range(3): upcard = int_to_card(upcard_nums[r][c]) if upcard is not None: upcard_rank = RANKS[upcard.rank_idx()] if upcard_rank not in card_rank_locations: card_rank_locations[upcard_rank] = [] card_rank_locations[upcard_rank].append((r, c)) quad_ranks = set([]) for card_rank in card_rank_locations: if len(card_rank_locations[card_rank]) >= 4: quad_ranks.add(card_rank) # print(f"quad_ranks = {quad_ranks}") quad_hands = [] for quad_rank in quad_ranks: rank_locations = card_rank_locations[quad_rank] hands = itertools.combinations(rank_locations, 4) quad_hands.extend([set(hand) for hand in hands]) # the chosen piles must not all be on the same row quad_hands = self._ignore_invalid_hands(quad_hands) # print(f"quad_hands = {quad_hands}") return quad_hands
def _get_pair_hands(self): """ Returns a list of hands, represented as sets of tuples, where each tuple is the location of a card in the hand. """ upcard_nums = self.upcard_nums() card_rank_locations = {} for r in range(3): for c in range(3): upcard = int_to_card(upcard_nums[r][c]) if upcard is not None: upcard_rank = RANKS[upcard.rank_idx()] if upcard_rank not in card_rank_locations: card_rank_locations[upcard_rank] = [] card_rank_locations[upcard_rank].append((r, c)) pair_ranks = set([]) for card_rank in card_rank_locations: if len(card_rank_locations[card_rank]) >= 2: pair_ranks.add(card_rank) # print(f"pair_ranks = {pair_ranks}") pair_hands = [] for pair_rank in pair_ranks: rank_locations = card_rank_locations[pair_rank] hands = itertools.combinations(rank_locations, 2) pair_hands.extend([set(hand) for hand in hands]) # the chosen piles must not all be on the same row pair_hands = self._ignore_invalid_hands(pair_hands) # print(f"pair_hands = {pair_hands}") return pair_hands
def full_info(self): # Print full game state info (including the hidden cards) repr = (f"LUCKY SUIT = {SUITS[self.lucky_suit_idx]}\n" f"DISCARDS LEFT = {self.discards_remaining}\n" f"GAME OVER? = {self.is_game_over()}\n") upcard_nums = self.upcard_nums() pile_sizes = self.pile_sizes() # Print board/piles state for r in range(3): for c in range(3): pile_repr = f"pile ({r}, {c}): " if not self.is_pile_empty(r, c): pile_cards = [ int_to_card(card_num) for card_num in self.card_num_piles[r][c] ] pile_repr += str(pile_cards) else: pile_repr += "(EMPTY PILE)" pile_repr += "\n" repr += pile_repr # Print dead cards repr += f"DEAD CARDS = {[int_to_card(card_num) for card_num in self.dead_card_nums]}" return repr
def _is_lucky_hand(self, piles): upcard_nums = self.upcard_nums() for pile in piles: pile_row, pile_col = pile assert not self.is_pile_empty(pile_row, pile_col) card = int_to_card(upcard_nums[pile_row][pile_col]) if card.suit == self.lucky_suit: return True return False
def _get_full_house_hands(self): upcard_nums = self.upcard_nums() card_rank_locations = {} for r in range(3): for c in range(3): upcard = int_to_card(upcard_nums[r][c]) if upcard is not None: upcard_rank = RANKS[upcard.rank_idx()] if upcard_rank not in card_rank_locations: card_rank_locations[upcard_rank] = [] card_rank_locations[upcard_rank].append((r, c)) pair_ranks = set([]) trip_ranks = set([]) for card_rank in card_rank_locations: if len(card_rank_locations[card_rank]) >= 2: pair_ranks.add(card_rank) if len(card_rank_locations[card_rank]) >= 3: trip_ranks.add(card_rank) full_house_rank_pairs = set([]) for trip_rank in trip_ranks: for pair_rank in pair_ranks: if pair_rank == trip_rank: continue full_house_ranks = (trip_rank, pair_rank) full_house_rank_pairs.add(full_house_ranks) # print(f"full_house_rank_pairs = {full_house_rank_pairs}") full_house_hands = [] for trip_rank, pair_rank in full_house_rank_pairs: trip_rank_locations = card_rank_locations[trip_rank] pair_rank_locations = card_rank_locations[pair_rank] trip_hands = itertools.combinations(trip_rank_locations, 3) pair_hands = itertools.combinations(pair_rank_locations, 2) full_house_combos = itertools.product(trip_hands, pair_hands) for combo in full_house_combos: hand = set(combo[0]).union(set(combo[1])) if len(hand) != 5: continue full_house_hands.append(hand) # the chosen piles must not all be on the same row full_house_hands = self._ignore_invalid_hands(full_house_hands) # print("full_house_hands: ") # pprint(full_house_hands) return full_house_hands
def short_repr_gamestate(gamestate: GameState) -> str: board_repr = "" upcard_nums = gamestate.upcard_nums() for r in range(3): for c in range(3): if not gamestate.is_pile_empty(r, c): upcard = int_to_card(upcard_nums[r][c]) board_repr += str(upcard) + " " else: board_repr += "-- " if r != 2: board_repr += "/ " else: board_repr += "; " board_repr += f"Discards = {gamestate.discards_remaining}, Lucky suit = {gamestate.lucky_suit}" board_repr += "\n" return board_repr
def _get_straight_flush_hands(self): upcard_nums = self.upcard_nums() lg_straight_hands = self._get_lg_straight_hands() straight_flush_hands = [] for hand in lg_straight_hands: hand_suit = None valid_hand = True for location in hand: row, col = location card = int_to_card(upcard_nums[row][col]) if hand_suit is None: hand_suit = card.suit elif card.suit != hand_suit: valid_hand = False if valid_hand: straight_flush_hands.append(hand) # print(f"straight_flush_hands = {straight_flush_hands}") return straight_flush_hands
def _get_lg_straight_hands(self): upcard_nums = self.upcard_nums() card_rank_locations = {} for r in range(3): for c in range(3): upcard = int_to_card(upcard_nums[r][c]) if upcard is not None: upcard_rank = RANKS[upcard.rank_idx()] if upcard_rank not in card_rank_locations: card_rank_locations[upcard_rank] = [] card_rank_locations[upcard_rank].append((r, c)) wrapping_card_ranks = [ "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A" ] # looking for 5-straights, check every rank for a possible low-end of the straight: A to T (A-2-3-4-5 to T-J-Q-K-A) lg_straight_hands = [] for rank_idx in range(len(wrapping_card_ranks) - (5 - 1)): straight_ranks = wrapping_card_ranks[rank_idx:rank_idx + 5] straight_possible = True for rank in straight_ranks: if rank not in card_rank_locations or len( card_rank_locations[rank]) == 0: straight_possible = False if not straight_possible: continue straight_rank_locations = [ card_rank_locations[rank] for rank in straight_ranks ] hands = itertools.product(*straight_rank_locations) lg_straight_hands.extend([set(hand) for hand in hands]) # the chosen piles must not all be on the same row lg_straight_hands = self._ignore_invalid_hands(lg_straight_hands) # print(f"lg_straight_hands = {lg_straight_hands}") return lg_straight_hands
def __repr__(self): # Print general game state info repr = (f"LUCKY SUIT = {SUITS[self.lucky_suit_idx]}\n" f"DISCARDS LEFT = {self.discards_remaining}\n" f"GAME OVER? = {self.is_game_over()}\n") upcard_nums = self.upcard_nums() pile_sizes = self.pile_sizes() # Print board/piles state for r in range(3): row_repr = "| " for c in range(3): if not self.is_pile_empty(r, c): upcard = int_to_card(upcard_nums[r][c]) row_repr += str( upcard) + f"(+{int(pile_sizes[r][c] - 1)})" + " | " else: row_repr += "--(+0) | " row_repr += "\n" repr += row_repr # Print dead cards repr += f"DEAD CARDS = {[int_to_card(card_num) for card_num in self.dead_card_nums]}" return repr