def get_meld_clusters( hand: List[Card], going_out_deadwood_count: int, is_going_out: bool = False, opponent_meld_piles: List[List[Card]] = None) -> List[List[Set[Card]]]: # if is_going_out is true, then return only meld_piles with deadwood count <= 10 # opponent_meld_piles are the meld_piles for the opponent who has knocked to be used for laying off cards result = [] all_run_melds = [set(x) for x in _get_all_run_melds(hand)] all_set_melds = [set(x) for x in _get_all_set_melds(hand)] all_melds = all_run_melds + all_set_melds all_melds_count = len(all_melds) for i in range(0, all_melds_count): first_meld = all_melds[i] meld_cluster_1 = [first_meld] if is_going_out: deadwood_count = utils.get_deadwood_count( hand=hand, meld_cluster=meld_cluster_1) if deadwood_count <= going_out_deadwood_count: result.append(meld_cluster_1) else: result.append(meld_cluster_1) for j in range(i + 1, all_melds_count): second_meld = all_melds[j] if not second_meld.isdisjoint(first_meld): continue meld_cluster_2 = [first_meld, second_meld] if is_going_out: deadwood_count = utils.get_deadwood_count( hand=hand, meld_cluster=meld_cluster_2) if deadwood_count <= going_out_deadwood_count: result.append(meld_cluster_2) else: result.append(meld_cluster_2) for k in range(j + 1, all_melds_count): third_meld = all_melds[k] if not third_meld.isdisjoint( first_meld) or not third_meld.isdisjoint(second_meld): continue meld_cluster_3 = [first_meld, second_meld, third_meld] if is_going_out: deadwood_count = utils.get_deadwood_count( hand=hand, meld_cluster=meld_cluster_3) if deadwood_count <= going_out_deadwood_count: result.append(meld_cluster_3) else: result.append(meld_cluster_3) return result
def get_payoff_gin_rummy_v1(player: GinRummyPlayer, game: 'GinRummyGame') -> int or float: ''' Get the payoff of player: a) 1.0 if player gins b) 0.2 if player knocks c) -deadwood_count / 100 otherwise Returns: payoff (int or float): payoff for player ''' # payoff is 1.0 if player gins # payoff is 0.2 if player knocks # payoff is -deadwood_count / 100 if otherwise # The goal is to have the agent learn how to knock and gin. # The negative payoff when the agent fails to knock or gin should encourage the agent to form melds. # The payoff is scaled to lie between -1 and 1. going_out_action = game.round.going_out_action going_out_player_id = game.round.going_out_player_id if going_out_player_id == player.player_id and type( going_out_action) is KnockAction: payoff = 0.2 elif going_out_player_id == player.player_id and type( going_out_action) is GinAction: payoff = 1 else: hand = player.hand best_meld_clusters = melding.get_best_meld_clusters(hand=hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[ 0] deadwood_count = utils.get_deadwood_count(hand, best_meld_cluster) payoff = -deadwood_count / 100 return payoff
def _get_best_discards(discard_action_events, state) -> List[Card]: best_discards = [] # type: List[Card] final_deadwood_count = 999 env_hand = state['obs'][0] hand = utils.decode_cards(env_cards=env_hand) for discard_action_event in discard_action_events: discard_card = discard_action_event.card next_hand = [card for card in hand if card != discard_card] meld_clusters = melding.get_meld_clusters(hand=next_hand) deadwood_counts = [] for meld_cluster in meld_clusters: deadwood_count = utils.get_deadwood_count(hand=next_hand, meld_cluster=meld_cluster) deadwood_counts.append(deadwood_count) best_deadwood_count = min(deadwood_counts, default=utils.get_deadwood_count(hand=next_hand, meld_cluster=[])) if best_deadwood_count < final_deadwood_count: final_deadwood_count = best_deadwood_count best_discards = [discard_card] elif best_deadwood_count == final_deadwood_count: best_discards.append(discard_card) return best_discards
def get_best_meld_clusters(hand: List[Card]) -> List[List[List[Card]]]: assert len(hand) == 10 result = [] # type: List[List[List[Card]]] meld_clusters = get_meld_clusters(hand=hand) # type: List[List[List[Card]]] meld_clusters_count = len(meld_clusters) if meld_clusters_count > 0: deadwood_counts = [utils.get_deadwood_count(hand=hand, meld_cluster=meld_cluster) for meld_cluster in meld_clusters] best_deadwood_count = min(deadwood_counts) for i in range(meld_clusters_count): if deadwood_counts[i] == best_deadwood_count: result.append(meld_clusters[i]) return result
def score_player_0(self, action: ScoreNorthPlayerAction): # when current_player takes ScoreNorthPlayerAction step, the move is recorded and executed # south becomes current player if not self.current_player_id == 0: raise GinRummyProgramError("current_player_id is {}: should be 0.".format(self.current_player_id)) current_player = self.get_current_player() best_meld_clusters = melding.get_best_meld_clusters(hand=current_player.hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[0] deadwood_count = utils.get_deadwood_count(hand=current_player.hand, meld_cluster=best_meld_cluster) self.move_sheet.append(ScoreNorthMove(player=current_player, action=action, best_meld_cluster=best_meld_cluster, deadwood_count=deadwood_count)) self.current_player_id = 1
def _get_payoff(self, player: GinRummyPlayer, game) -> float: going_out_action = game.round.going_out_action going_out_player_id = game.round.going_out_player_id if going_out_player_id == player.player_id and type(going_out_action) is KnockAction: payoff = self._knock_reward elif going_out_player_id == player.player_id and type(going_out_action) is GinAction: payoff = self._gin_reward else: hand = player.hand best_meld_clusters = melding.get_best_meld_clusters(hand=hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[0] deadwood_count = utils.get_deadwood_count(hand, best_meld_cluster) payoff = -deadwood_count / 100 return payoff
def can_gin(game: GinRummyGame) -> bool: result = False last_action = game.get_last_action() last_action_type = type(last_action) if last_action_type is DrawCardAction or last_action_type is PickUpDiscardAction: current_player = game.get_current_player() hand = current_player.hand going_out_deadwood_count = game.settings.going_out_deadwood_count meld_clusters = melding.get_meld_clusters(hand=hand, going_out_deadwood_count=going_out_deadwood_count, is_going_out=True) if meld_clusters: deadwood_counts = [utils.get_deadwood_count(hand, meld_cluster) for meld_cluster in meld_clusters] result = min(deadwood_counts) == 0 return result
def score_player_1(self, action: ScoreSouthPlayerAction): assert self.current_player_id == 1 current_player = self.get_current_player() best_meld_clusters = melding.get_best_meld_clusters( hand=current_player.hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[ 0] deadwood_count = utils.get_deadwood_count( hand=current_player.hand, meld_cluster=best_meld_cluster) self.move_sheet.append( ScoreSouthMove(player=current_player, action=action, best_meld_cluster=best_meld_cluster, deadwood_count=deadwood_count)) self.is_over = True
def get_best_meld_clusters(hand: List[Card]) -> List[List[Set[Card]]]: result = [] meld_clusters = get_meld_clusters(hand=hand, going_out_deadwood_count=100, is_going_out=False) meld_clusters_count = len(meld_clusters) if meld_clusters_count > 0: deadwood_counts = [ utils.get_deadwood_count(hand=hand, meld_cluster=meld_cluster) for meld_cluster in meld_clusters ] best_deadwood_count = min(deadwood_counts) for i in range(meld_clusters_count): if deadwood_counts[i] == best_deadwood_count: result.append(meld_clusters[i]) return result
def score_player_0(self, action: ScoreNorthPlayerAction): # when current_player takes ScoreNorthPlayerAction step, the move is recorded and executed # south becomes current player assert self.current_player_id == 0 current_player = self.get_current_player() best_meld_clusters = melding.get_best_meld_clusters( hand=current_player.hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[ 0] deadwood_count = utils.get_deadwood_count( hand=current_player.hand, meld_cluster=best_meld_cluster) self.move_sheet.append( ScoreNorthMove(player=current_player, action=action, best_meld_cluster=best_meld_cluster, deadwood_count=deadwood_count)) self.current_player_id = 1
def get_best_meld_clusters(hand: List[Card]) -> List[List[List[Card]]]: if len(hand) != 10: raise GinRummyProgramError( "Hand contain {} cards: should be 10 cards.".format(len(hand))) result = [] # type: List[List[List[Card]]] meld_clusters = get_meld_clusters( hand=hand) # type: List[List[List[Card]]] meld_clusters_count = len(meld_clusters) if meld_clusters_count > 0: deadwood_counts = [ utils.get_deadwood_count(hand=hand, meld_cluster=meld_cluster) for meld_cluster in meld_clusters ] best_deadwood_count = min(deadwood_counts) for i in range(meld_clusters_count): if deadwood_counts[i] == best_deadwood_count: result.append(meld_clusters[i]) return result
def get_payoffs(self, game: GinRummyGame): ''' Get the payoffs of players: a) 1 if gin b) 0.2 if knock c) -deadwood_count / 100 otherwise Returns: payoffs (list): a list of payoffs for each player ''' payoffs = [0, 0] game_round = game.round last_action = game.actions[-1] assert game_round.is_over assert type(last_action) is ScoreSouthPlayerAction going_out_action = game_round.going_out_action going_out_player_id = game_round.going_out_player_id for i in range(2): # FIXME: 200213 simplified calculation player = game.round.players[i] hand = player.hand if self.get_payoff: payoff = self.get_payoff(player, game) else: best_meld_clusters = melding.get_best_meld_clusters(hand=hand) best_meld_cluster = [] if not best_meld_clusters else best_meld_clusters[ 0] deadwood_count = utils.get_deadwood_count( hand, best_meld_cluster) payoff = -deadwood_count / 100 if going_out_player_id == player.player_id and type( going_out_action) is KnockAction: payoff = 0.2 # FIXME: 200213 simplified calculation elif going_out_player_id == player.player_id and type( going_out_action) is GinAction: payoff = 1 # FIXME: 200213 simplified calculation elif type(going_out_action) is DeclareDeadHandAction: pass # FIXME: 200213 payoffs should be zeros else: raise Exception("get_payoffs: ???") payoffs[i] = payoff return payoffs