def run(players, verbose=False): assert len(players) >= 2 names = [] for i in range(len(players)): if players[i].name is None: names.append("Player {}".format(i + 1)) else: names.append("{} (Player {})".format(players[i].name, i + 1)) new_env = BigTwoEnv(num_players=len(players)) state = new_env.reset() num_finished = 0 done = False while not done: cur_turn = state.turn must_pass = False if state.pass_only[cur_turn] or state.player_card_count[cur_turn] == 0: must_pass = True if verbose: print("\n{} must pass.".format(names[cur_turn])) action = CardCollection() else: if verbose: print("\n{}'s turn to play {}.".format(names[cur_turn], state.mode)) action = players[cur_turn].get_action(state) state, reward = new_env.step(action) done = state.done if not must_pass: if verbose: print("Played action {} and obtained reward {}.".format( action, reward)) else: if action.num_cards() > 0: print("{} played {}.".format(names[cur_turn], action)) else: print("{} passed.".format(names[cur_turn])) if state.player_card_count[cur_turn] == 0 and action.num_cards() > 0: if num_finished == 0: print("{} finished in 1st place!".format(names[cur_turn])) elif num_finished == 1: print("{} finished in 2nd place!".format(names[cur_turn])) elif num_finished == 2: print("{} finished in 3rd place!".format(names[cur_turn])) for i in range(len(players)): if state.player_card_count[i] > 0: print("{} lost and had the remaining cards: {}".format( names[i], new_env.player_cards[i])) num_finished += 1
def get_valid_moves(self): if self.done: return [] if self.pass_only[self.turn]: return [CardCollection()] possible_plays = [] if self.mode == "any" or self.mode == "single": possible_plays.extend(search_plays(self.hand, "single")) if self.mode == "any" or self.mode == "double": possible_plays.extend(search_plays(self.hand, "double")) if self.mode == "any" or self.mode == "triple": possible_plays.extend(search_plays(self.hand, "triple")) if self.mode == "any" or self.mode == "quad": possible_plays.extend(search_plays(self.hand, "quad")) if self.mode == "any" or self.mode == "fivecard": possible_plays.extend(search_plays(self.hand, "straight")) flushes = search_plays(self.hand, "flush") pure_flushes = [] for f in flushes: play, _ = identify_play(f) if play == "flush": pure_flushes.append(f) possible_plays.extend(pure_flushes) possible_plays.extend(search_plays(self.hand, "fullhouse")) else: possible_plays.extend(search_plays(self.hand, "straightflush")) possible_plays.extend(search_plays(self.hand, "bomb")) valid_plays = [] if self.start: for play in possible_plays: if play.contains(Card(3, 'C')): valid_plays.append(play) return valid_plays else: if self.mode == "any": return possible_plays else: valid_plays.append(CardCollection()) for play in possible_plays: if compare_plays(play, self.last_play) == 1: valid_plays.append(play) return valid_plays
def get_action(self, state): print("Your hand contains: {}".format(state.hand)) cards_to_play = [] played_cards = 0 command = None while command != "END" and command != "PASS" and played_cards < 5: command = input("Enter a card to play -> ") command = command.upper() if command != "END" and command != "PASS": try: card = Card.str_to_card(command) cards_to_play.append(card) played_cards += 1 except UnrecognizedCardException: print("That command is invalid. Please try again.") return CardCollection(cards_to_play)
def reset(self): deck = Deck() deck.shuffle() self.player_cards = [ sort_cards(hand) for hand in deck.deal(self.num_players) ] self.player_card_count = [pc.num_cards() for pc in self.player_cards] self.history = [CardCollection()] self.last_play = None self.pass_only = [False] * self.num_players self.mode = "any" self.start = True self.done = False for i in range(len(self.player_cards)): if self.player_cards[i].contains(Card(3, 'C')): self.turn = i return self.get_state()
def sort_cards(cards): new_cards = sorted(cards.cards, key=get_card_value) return CardCollection(new_cards)
def step(self, action): valid = self.is_valid(action) if not valid: if self.start: action = CardCollection([Card(3, 'C')]) print( "Warning: invalid play was submitted, playing 3C single..." ) elif self.mode == "any": chosen_card = self.player_cards[self.turn].cards[0] action = CardCollection([chosen_card]) print( "Warning: invalid play was submitted, playing {} single..." .format(chosen_card)) else: action = CardCollection() print("Warning: invalid play was submitted, passing turn...") play, play_rep = identify_play(action) if self.mode == "any": if play == "single" or play == "double" or play == "triple" or play == "quad": self.mode = play else: self.mode = "fivecard" if play == "bomb" or play == "straightflush": self.mode = "fivecard" if play == "pass": self.pass_only[self.turn] = True if len(self.history) >= (self.num_players - 2): all_pass = True for past_play in self.history[-(self.num_players - 2):]: if past_play.num_cards() != 0: all_pass = False if all_pass: self.pass_only = [False] * self.num_players self.mode = "any" reward = action.num_cards() / 5.0 self.player_cards[self.turn].subtract(action) self.player_card_count[self.turn] -= action.num_cards() self.history.append(action) if play != "pass": self.last_play = action self.start = False nonzero = 0 for count in self.player_card_count: if count > 0: nonzero += 1 if self.player_card_count[self.turn] == 0 and play != "pass": reward += 2.0 * nonzero if nonzero <= 1: self.done = True self.turn = (self.turn + 1) % self.num_players while self.mode == "any" and self.player_card_count[self.turn] == 0: self.turn = (self.turn - 1) % self.num_players return self.get_state(), reward
def _collection_combination(iterable, num): return [ CardCollection(list(hand)) for hand in combinations(iterable, num) ]
def search_plays(hand, type): def _collection_combination(iterable, num): return [ CardCollection(list(hand)) for hand in combinations(iterable, num) ] def _enumerate_multiples(hand, num): plays = [] cur_rank = None start_ind = 0 counter = 0 for i in range(hand.num_cards()): if hand.cards[i].rank == cur_rank: counter += 1 else: if counter >= num: plays.extend( _collection_combination(hand.cards[start_ind:i], num)) cur_rank = hand.cards[i].rank start_ind = i counter = 1 if counter >= num: plays.extend(_collection_combination(hand.cards[start_ind:], num)) return plays def _enumerate_straights(cards, c_count): if len(c_count) == 1: return [[card] for card in cards] straights = [] for i in range(c_count[0]): sub_straights = _enumerate_straights(cards[c_count[0]:], c_count[1:]) for s in sub_straights: new_s = [cards[i]] new_s.extend(s) straights.append(new_s) return straights hand = sort_cards(hand) if hand.num_cards() == 0: return [] if type == "single": return [CardCollection([card]) for card in hand.cards] elif type == "double": return _enumerate_multiples(hand, 2) elif type == "triple": return _enumerate_multiples(hand, 3) elif type == "quad": return _enumerate_multiples(hand, 4) elif type == "bomb": possible_plays = [] quads = _enumerate_multiples(hand, 4) for q in quads: for card in hand.cards: if q.cards[0].rank != card.rank: possible_plays.append( CardCollection.merge_collections( (q, CardCollection([card])))) return possible_plays elif type == "fullhouse": possible_plays = [] doubles = _enumerate_multiples(hand, 2) triples = _enumerate_multiples(hand, 3) for d in doubles: for t in triples: if d.cards[0].rank != t.cards[0].rank: possible_plays.append( CardCollection.merge_collections((d, t))) return possible_plays elif type == "straight": possible_plays = [] c_count = [0] * 15 end_cards = [] for card in hand.cards: c_count[card.rank - 1] += 1 if card.rank == 1 or card.rank == 2: c_count[card.rank + 12] += 1 end_cards.append(card) end_cards.extend(hand.cards) last_zero = -1 cumul_start = 0 cumul_cur = 0 for i in range(len(c_count)): if c_count[i] == 0: last_zero = i cumul_start = cumul_cur else: cumul_cur += c_count[i] if i - last_zero >= 5: play_list = _enumerate_straights( end_cards[cumul_start:cumul_cur], c_count[i - 4:i + 1]) possible_plays.extend( [CardCollection(c_list) for c_list in play_list]) cumul_start += c_count[i - 4] return possible_plays elif type == "flush": possible_plays = [] card_suits = {'C': [], 'D': [], 'H': [], 'S': []} for card in hand.cards: card_suits[card.suit].append(card) for suit in card_suits: possible_plays.extend(_collection_combination(card_suits[suit], 5)) return possible_plays elif type == "straightflush": possible_plays = [] flushes = search_plays(hand, "flush") for f in flushes: play, _ = identify_play(f) if play == "straightflush": possible_plays.append(f) return possible_plays else: raise InvalidHandTypeException("That hand type is not recognized.")