def test_step(self):
     game = Game()
     state, _ = game.init_game()
     action = state['actions'][0]
     state, next_player_id = game.step(action)
     next_player = game.players[next_player_id]
     player_id = get_upstream_player_id(next_player, game.players)
     self.assertEqual(state['trace'][0][0], player_id)
     self.assertEqual(state['trace'][0][1], action)
 def test_init_game(self):
     game = Game()
     state, current_player = game.init_game()
     total_cards = list(state['current_hand'] + state['others_hand'])
     total_cards.sort(key=functools.cmp_to_key(doudizhu_sort_str))
     deck = list(game.round.deck_str)
     self.assertEqual(state['self'], current_player)
     self.assertEqual(state['landlord'], current_player)
     self.assertIs(len(state['played_cards']), 0)
     self.assertEqual(len(total_cards), 54)
     self.assertListEqual(total_cards, deck)
 def test_proceed_game(self):
     game = Game()
     state, player_id = game.init_game()
     while not game.is_over():
         action = np.random.choice(list(state['actions']))
         state, next_player_id = game.step(action)
         player = game.players[player_id]
         self.assertEqual(get_downstream_player_id(player, game.players),
                          next_player_id)
         player_id = next_player_id
         if not game.is_over():
             self.assertIsNotNone(state['actions'])
     for player_id in range(3):
         state = game.get_state(player_id)
         self.assertIsNone(state['actions'])
    def test_step_back(self):
        #case 1: action, stepback
        game = Game(allow_step_back=True)
        state, player_id = game.init_game()
        action = state['actions'][0]
        playable_cards = game.judger.playable_cards
        game.step(action)
        game.step_back()
        self.assertEqual(game.judger.playable_cards, playable_cards)
        self.assertEqual(game.round.greater_player, None)
        self.assertEqual(game.round.current_player, player_id)
        self.assertEqual(len(game.history), 0)
        game.state['actions'].sort()
        state['actions'].sort()
        self.assertEqual(game.state, state)
        self.assertEqual(game.step_back(), False)

        #case 2: action, pass, stepback
        game = Game(allow_step_back=True)
        state, player_id = game.init_game()
        action = state['actions'][0]
        game.step(action)
        actions = game.state['actions']
        playable_cards = game.judger.playable_cards
        played_cards = game.players[game.round.current_player].played_cards
        game.step('pass')
        game.step_back()
        #judger.playable_cards should be the same
        self.assertEqual(game.judger.playable_cards, playable_cards)
        #players[current_player].played_cards should be the same
        self.assertEqual(game.players[game.round.current_player].played_cards,
                         played_cards)
        #greater_player should be the same
        self.assertEqual(game.round.greater_player.player_id, 0)
        actions.sort()
        game.state['actions'].sort()
        #actions should be the same after step_back()
        self.assertEqual(game.state['actions'], actions)

        #case 3: action, pass, pass, action, stepback
        game = Game(allow_step_back=True)
        state, player_id = game.init_game()
        action = state['actions'][0]
        game.step(action)
        game.step('pass')
        game.step('pass')
        actions = game.state['actions']
        playable_cards = game.judger.playable_cards
        played_cards = game.players[game.round.current_player].played_cards
        game.step(actions[0])
        game.step_back()
        #judger.playable_cards should be the same
        self.assertEqual(game.judger.playable_cards, playable_cards)
        #players[current_player].played_cards should be the same
        self.assertEqual(game.players[game.round.current_player].played_cards,
                         played_cards)
        #greater_player should be the same
        self.assertEqual(game.round.greater_player.player_id, 0)
        actions.sort()
        game.state['actions'].sort()
        #actions should be the same after step_back()
        self.assertEqual(game.state['actions'], actions)
        game.step_back()
        #greater_player should be the same
        self.assertEqual(game.round.greater_player.player_id, 0)
 def test_get_player_id(self):
     game = Game()
     _, player_id = game.init_game()
     current_player_id = game.get_player_id()
     self.assertEqual(current_player_id, player_id)
 def test_get_action_num(self):
     game = Game()
     action_num = game.get_action_num()
     self.assertEqual(action_num, 309)
 def test_get_player_num(self):
     game = Game()
     player_num = game.get_player_num()
     self.assertEqual(player_num, 3)
Example #8
0
class DoudizhuEnv(Env):
    ''' Doudizhu Environment
    '''

    def __init__(self, config):
        self.game = Game()
        super().__init__(config)
        self.state_shape = [6, 5, 15]

    def _extract_state(self, state):
        ''' Encode state

        Args:
            state (dict): dict of original state

        Returns:
            numpy array: 6*5*15 array
                         6 : current hand
                             the union of the other two players' hand
                             the recent three actions
                             the union of all played cards
        '''
        obs = np.zeros((6, 5, 15), dtype=int)
        for index in range(6):
            obs[index][0] = np.ones(15, dtype=int)
        encode_cards(obs[0], state['current_hand'])
        encode_cards(obs[1], state['others_hand'])
        for i, action in enumerate(state['trace'][-3:]):
            if action[1] != 'pass':
                encode_cards(obs[4-i], action[1])
        if state['played_cards'] is not None:
            encode_cards(obs[5], state['played_cards'])

        extracted_state = {'obs': obs, 'legal_actions': self._get_legal_actions()}
        if self.allow_raw_data:
            extracted_state['raw_obs'] = state
            # TODO: state['actions'] can be None, may have bugs
            if state['actions'] == None:
                extracted_state['raw_legal_actions'] = []
            else:
                extracted_state['raw_legal_actions'] = [a for a in state['actions']]
        if self.record_action:
            extracted_state['action_record'] = self.action_recorder
        return extracted_state

    def get_payoffs(self):
        ''' Get the payoffs of players. Must be implemented in the child class.

        Returns:
            payoffs (list): a list of payoffs for each player
        '''
        return self.game.judger.judge_payoffs(self.game.round.landlord_id, self.game.winner_id)

    def _decode_action(self, action_id):
        ''' Action id -> the action in the game. Must be implemented in the child class.

        Args:
            action_id (int): the id of the action

        Returns:
            action (string): the action that will be passed to the game engine.
        '''
        abstract_action = ACTION_LIST[action_id]
        # without kicker
        if '*' not in abstract_action:
            return abstract_action
        # with kicker
        legal_actions = self.game.state['actions']
        specific_actions = []
        kickers = []
        for legal_action in legal_actions:
            for abstract in SPECIFIC_MAP[legal_action]:
                main = abstract.strip('*')
                if abstract == abstract_action:
                    specific_actions.append(legal_action)
                    kickers.append(legal_action.replace(main, '', 1))
                    break
        # choose kicker with minimum score
        player_id = self.game.get_player_id()
        kicker_scores = []
        for kicker in kickers:
            score = 0
            for action in self.game.judger.playable_cards[player_id]:
                if kicker in action:
                    score += 1
            kicker_scores.append(score+CARD_RANK_STR.index(kicker[0]))
        min_index = 0
        min_score = kicker_scores[0]
        for index, score in enumerate(kicker_scores):
            if score < min_score:
                min_score = score
                min_index = index
        return specific_actions[min_index]

    def _get_legal_actions(self):
        ''' Get all legal actions for current state

        Returns:
            legal_actions (list): a list of legal actions' id
        '''
        legal_action_id = []
        legal_actions = self.game.state['actions']
        if legal_actions:
            for action in legal_actions:
                for abstract in SPECIFIC_MAP[action]:
                    action_id = ACTION_SPACE[abstract]
                    if action_id not in legal_action_id:
                        legal_action_id.append(action_id)
        return legal_action_id
Example #9
0
 def __init__(self, config):
     self.game = Game()
     super().__init__(config)
     self.state_shape = [6, 5, 15]