def test_defence_against_honitsu_first_case(self): table = Table() tiles = self._string_to_136_array(sou='22', pin='222367899', man='45', honors='1') table.player.init_hand(tiles) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='567'))) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='123'))) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='345'))) table.add_discarded_tile(1, self._string_to_136_tile(sou='6'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='6'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='8'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='9'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) result = table.player.discard_tile() # we can't discard pin and honor tiles against honitsu self.assertEqual(self._to_string([result]), '2s')
def test_find_suji_tiles_to_discard_for_one_player(self): table = Table() tiles = self._string_to_136_array(sou='2345678', pin='12789', man='55') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(man='2'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='8'), False) table.add_called_riichi(1) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '5m')
def test_temporary_safe_tiles(self): table = Table() table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) self.assertEqual(table.get_player(1).temporary_safe_tiles, []) self.assertEqual(table.get_player(2).temporary_safe_tiles, [3]) self.assertEqual(table.get_player(3).temporary_safe_tiles, [3]) table.add_discarded_tile(2, self._string_to_136_tile(man='5'), False) self.assertEqual(table.get_player(1).temporary_safe_tiles, [4]) self.assertEqual(table.get_player(2).temporary_safe_tiles, []) self.assertEqual(table.get_player(3).temporary_safe_tiles, [3, 4]) table.add_discarded_tile(3, self._string_to_136_tile(man='6'), False) self.assertEqual(table.get_player(1).temporary_safe_tiles, [4, 5]) self.assertEqual(table.get_player(2).temporary_safe_tiles, [5]) self.assertEqual(table.get_player(3).temporary_safe_tiles, []) table.add_discarded_tile(1, self._string_to_136_tile(man='7'), False) self.assertEqual(table.get_player(1).temporary_safe_tiles, []) self.assertEqual(table.get_player(2).temporary_safe_tiles, [5, 6]) self.assertEqual(table.get_player(3).temporary_safe_tiles, [6])
def test_find_common_suji_tiles_to_discard_for_multiple_players(self): table = Table() tiles = self._string_to_136_array(sou='2345678', pin='12789', man='55') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(pin='4'), False) table.add_discarded_tile(2, self._string_to_136_tile(pin='4'), False) table.add_called_riichi(1) table.add_called_riichi(2) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '1p')
def test_mark_dora_as_dangerous_tile_for_suji(self): table = Table() table.add_dora_indicator(self._string_to_136_tile(man='8')) tiles = self._string_to_136_array(man='112235', pin='4557788') table.player.init_hand(tiles) # 9 man is dora! table.player.draw_tile(self._string_to_136_tile(man='9')) table.add_discarded_tile(1, self._string_to_136_tile(man='6'), False) table.add_called_riichi(1) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '3m')
def test_try_to_discard_not_needed_tiles_first_in_defence_mode(self): table = Table() tiles = self._string_to_136_array(sou='2345678', pin='789', man='55', honors='12') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(man='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='1'), False) table.add_called_riichi(1) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '1z')
def test_add_safe_tile_after_discard(self): table = Table() table.add_called_riichi(3) table.add_discarded_tile(1, self._string_to_136_tile(man='3'), False) table.add_discarded_tile(0, self._string_to_136_tile(man='4'), False) self.assertEqual(len(table.get_player(1).discards), 1) self.assertEqual(len(table.get_player(2).discards), 0) self.assertEqual(len(table.get_player(3).discards), 0) self.assertEqual(len(table.get_player(1).safe_tiles), 1) self.assertEqual(len(table.get_player(2).safe_tiles), 0) self.assertEqual(len(table.get_player(3).safe_tiles), 2) # "2" is 3 man self.assertEqual(table.get_player(1).safe_tiles, [2]) self.assertEqual(table.get_player(3).safe_tiles, [2, 3])
def test_dont_discard_safe_tiles_when_call_riichi(self): table = Table() table.count_of_remaining_tiles = 70 table.player.scores = 2000 tiles = self._string_to_136_array(sou='12356789', pin='22678') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(honors='1')) table.player.discard_tile() table.player.draw_tile(self._string_to_136_tile(honors='1')) table.add_discarded_tile(1, self._string_to_136_tile(sou='1'), False) table.add_called_riichi(1) result = table.player.discard_tile() self.assertEqual(table.player.can_call_riichi(), True) self.assertEqual(self._to_string([result]), '1z')
def test_try_to_discard_less_valuable_tiles_first_in_defence_mode(self): table = Table() tiles = self._string_to_136_array(sou='234678', pin='6789', man='55', honors='13') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(pin='7'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='2'), False) table.add_called_riichi(1) result = table.player.discard_tile() # discard of 2s will do less damage to our hand shape than 7p discard self.assertEqual(table.player.ai.in_defence, True) self.assertEqual(self._to_string([result]), '2s')
def test_dont_activate_strategy_if_we_dont_have_enough_tiles_in_the_wall( self): table = Table() player = table.player strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) tiles = self._string_to_136_array(sou='12355689', man='89', honors='44') player.init_hand(tiles) player.dealer_seat = 1 self.assertEqual(strategy.should_activate_strategy(), True) table.add_discarded_tile(3, self._string_to_136_tile(honors='4'), False) table.add_discarded_tile(3, self._string_to_136_tile(honors='4'), False) # we can't complete yakuhai, because there is not enough honor tiles self.assertEqual(strategy.should_activate_strategy(), False)
def test_find_impossible_waits_and_honor_tiles(self): table = Table() tiles = self._string_to_136_array(honors='1133') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(honors='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) table.add_called_riichi(2) table.player.ai.defence.hand_34 = self._to_34_array(table.player.tiles) table.player.ai.defence.closed_hand_34 = self._to_34_array( table.player.closed_hand) result = table.player.ai.defence.impossible_wait.find_tiles_to_discard( []) # dora is not safe against tanki wait, so let's hold it self.assertEqual([x.value for x in result], [EAST, WEST])
def test_find_common_safe_tile_to_discard(self): table = Table() tiles = self._string_to_136_array(sou='2456', pin='234478', man='2336') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(sou='6'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(2, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(2, self._string_to_136_tile(sou='6'), False) table.add_called_riichi(1) table.add_called_riichi(2) # for this test we don't need temporary_safe_tiles table.get_player(1).temporary_safe_tiles = [] table.get_player(2).temporary_safe_tiles = [] result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '6s')
def test_find_dealer_tile_to_discard(self): dealer = 2 dora = self._string_to_136_tile(honors='3') table = Table() table.init_round(0, 0, 0, dora, dealer, []) tiles = self._string_to_136_array(sou='2234678', pin='34', man='45789') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='5'), False) table.add_discarded_tile(2, self._string_to_136_tile(man='8'), False) table.add_discarded_tile(2, self._string_to_136_tile(man='9'), False) table.add_called_riichi(1) table.add_called_riichi(2) # for this test we don't need temporary_safe_tiles table.get_player(1).temporary_safe_tiles = [] table.get_player(2).temporary_safe_tiles = [] result = table.player.discard_tile() # second player is a dealer, let's fold against him self.assertEqual(self._to_string([result]), '9m') tiles = self._string_to_136_array(sou='234567', pin='348', man='234', honors='23') table.player.init_hand(tiles) result = table.player.discard_tile() # there is no safe tiles against dealer, so let's fold against other players self.assertEqual(table.player.ai.in_defence, True) self.assertEqual(self._to_string([result]), '4m')
def test_try_to_detect_honitsu_hand(self): table = Table() table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='567'))) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='123'))) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(pin='345'))) table.add_discarded_tile(1, self._string_to_136_tile(sou='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='8'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='9'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='1'), False) self.assertEqual(table.get_player(1).is_threatening, True) self.assertEqual(table.get_player(1).chosen_suit, is_pin)
def test_priority_of_players_safe_tiles(self): table = Table() tiles = self._string_to_136_array(man='789', pin='2789', sou='23789', honors='1') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(sou='1')) table.add_discarded_tile(1, self._string_to_136_tile(sou='7'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='1'), False) table.add_called_riichi(1) table.add_discarded_tile(2, self._string_to_136_tile(honors='1'), False) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '1z')
def test_discard_not_needed_winds(self): table = Table() player = table.player player.scores = 25000 table.count_of_remaining_tiles = 100 tiles = self._string_to_136_array(man='24', pin='4', sou='12344668', honors='36') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='5')) table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) tile_to_discard = player.discard_tile() # west was discarded three times, we don't need it self.assertEqual(self._to_string([tile_to_discard]), '3z')
def test_defence_against_honitsu_second_case(self): table = Table() tiles = self._string_to_136_array(sou='4', pin='223456', man='678', honors='66') table.player.init_hand(tiles) table.add_called_meld( 1, self._make_meld(Meld.CHI, self._string_to_136_array(sou='789'))) table.add_called_meld( 1, self._make_meld(Meld.PON, self._string_to_136_array(honors='444'))) table.add_called_meld( 1, self._make_meld(Meld.PON, self._string_to_136_array(honors='222'))) table.add_discarded_tile(1, self._string_to_136_tile(man='2'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='8'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='1'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='3'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='4'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='6'), False) table.add_discarded_tile(1, self._string_to_136_tile(sou='7'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='9'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(honors='7'), False) table.player.draw_tile(self._string_to_136_tile(sou='9')) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '3p')
def reproduce(self, dry_run=False): draw_tags = ['T', 'U', 'V', 'W'] discard_tags = ['D', 'E', 'F', 'G'] player_draw = draw_tags[self.player_position] player_draw_regex = re.compile('^<[{}]+\d*'.format(''.join(player_draw))) draw_regex = re.compile('^<[{}]+\d*'.format(''.join(draw_tags))) discard_regex = re.compile('^<[{}]+\d*'.format(''.join(discard_tags))) table = Table() # previous_tag = None for tag in self.round_content: if dry_run: print(tag) if not dry_run and tag == self.stop_tag: break if 'INIT' in tag: values = self.decoder.parse_initial_values(tag) shifted_scores = [] for x in range(0, 4): shifted_scores.append(values['scores'][self._normalize_position(x, self.player_position)]) table.init_round( values['round_number'], values['count_of_honba_sticks'], values['count_of_riichi_sticks'], values['dora_indicator'], self._normalize_position(self.player_position, values['dealer']), shifted_scores, ) hands = [ [int(x) for x in self.decoder.get_attribute_content(tag, 'hai0').split(',')], [int(x) for x in self.decoder.get_attribute_content(tag, 'hai1').split(',')], [int(x) for x in self.decoder.get_attribute_content(tag, 'hai2').split(',')], [int(x) for x in self.decoder.get_attribute_content(tag, 'hai3').split(',')], ] # DEL: we can't only initialize the main player, we must initialize # other players as well. #table.player.init_hand(hands[self.player_position]) # ADD: initialize all players on the table table.players[0].init_hand(hands[self.player_position]) table.players[1].init_hand(hands[(self.player_position+1)%4]) table.players[2].init_hand(hands[(self.player_position+2)%4]) table.players[3].init_hand(hands[(self.player_position+3)%4]) # ADD: when restart a new game, we need to reinitialize the config self.extract_features.__init__() # We must deal with ALL players. #if player_draw_regex.match(tag) and 'UN' not in tag: if draw_regex.match(tag) and 'UN' not in tag: tile = self.decoder.parse_tile(tag) # CHG: we must deal with ALL players #table.player.draw_tile(tile) if "T" in tag: table.players[0].draw_tile(tile) elif "U" in tag: table.players[1].draw_tile(tile) elif "V" in tag: table.players[2].draw_tile(tile) elif "W" in tag: table.players[3].draw_tile(tile) #print("After draw `W`:", table.players[3].tiles) if discard_regex.match(tag) and 'DORA' not in tag: tile = self.decoder.parse_tile(tag) player_sign = tag.upper()[1] # TODO: I don't know why the author wrote the code as below, the # player_seat won't work if we use self._normalize_position. This # might be a tricky part, and we need to review it later. #player_seat = self._normalize_position(self.player_position, discard_tags.index(player_sign)) # Temporally solution to modify the player_seat player_seat = (discard_tags.index(player_sign) + self.player_position)%4 #print("updated player seat:",player_seat) if player_seat == 0: table.players[player_seat].discard_tile(DiscardOption(table.players[player_seat], tile // 4, 0, [], 0)) else: # ADD: we must take care of ALL players tile_to_discard = tile is_tsumogiri = tile_to_discard == table.players[player_seat].last_draw # it is important to use table method, # to recalculate revealed tiles and etc. table.add_discarded_tile(player_seat, tile_to_discard, is_tsumogiri) #print("seat:",player_seat) #print("tiles:", TilesConverter.to_one_line_string(table.players[player_seat].tiles), " discard?:", TilesConverter.to_one_line_string([tile_to_discard])) table.players[player_seat].tiles.remove(tile_to_discard) # DEL #table.add_discarded_tile(player_seat, tile, False) if '<N who=' in tag: meld = self.decoder.parse_meld(tag) #player_seat = self._normalize_position(self.player_position, meld.who) # Again, we change the player_seat here player_seat = (meld.who + self.player_position) % 4 table.add_called_meld(player_seat, meld) #if player_seat == 0: # CHG: we need to handle ALL players here if True: # we had to delete called tile from hand # to have correct tiles count in the hand if meld.type != Meld.KAN and meld.type != Meld.CHANKAN: table.players[player_seat].draw_tile(meld.called_tile) if '<REACH' in tag and 'step="1"' in tag: who_called_riichi = self._normalize_position(self.player_position, self.decoder.parse_who_called_riichi(tag)) table.add_called_riichi(who_called_riichi) # This part is to extract the features that will be used to train # our model. # if '<AGARI' in tag: # print(previous_tag) # print(tag) # print("~~~~~~~~~\n") if '<D' in tag: #features = self.extract_features.get_is_waiting_features(table) features = self.extract_features.get_waiting_tiles_features(table) if features is not None: features_list = features.split(";") assert len(features_list)==6, "<D> Features format incorrect!" table_info = features_list[0] player_info = features_list[1] logger2.info(table_info + ";" + player_info) if '<E' in tag: #features = self.extract_features.get_is_waiting_features(table) features = self.extract_features.get_waiting_tiles_features(table) if features is not None: features_list = features.split(";") assert len(features_list)==6, "<E> Features format incorrect!" table_info = features_list[0] player_info = features_list[2] logger2.info(table_info + ";" + player_info) if '<F' in tag: #features = self.extract_features.get_is_waiting_features(table) features = self.extract_features.get_waiting_tiles_features(table) if features is not None: features_list = features.split(";") assert len(features_list)==6, "<F> Features format incorrect!" table_info = features_list[0] player_info = features_list[3] logger2.info(table_info + ";" + player_info) if '<G' in tag: #features = self.extract_features.get_is_waiting_features(table) features = self.extract_features.get_waiting_tiles_features(table) if features is not None: features_list = features.split(";") assert len(features_list)==6, "<G> Features format incorrect!" table_info = features_list[0] player_info = features_list[4] logger2.info(table_info + ";" + player_info) # previous_tag = tag if not dry_run: tile = self.decoder.parse_tile(self.stop_tag) print('Hand: {}'.format(table.player.format_hand_for_print(tile))) # to rebuild all caches table.player.draw_tile(tile) tile = table.player.discard_tile() # real run, you can stop debugger here table.player.draw_tile(tile) tile = table.player.discard_tile() print('Discard: {}'.format(TilesConverter.to_one_line_string([tile])))
def reproduce(self, dry_run=False): draw_tags = ['T', 'U', 'V', 'W'] discard_tags = ['D', 'E', 'F', 'G'] player_draw = draw_tags[self.player_position] player_draw_regex = re.compile('^<[{}]+\d*'.format( ''.join(player_draw))) draw_regex = re.compile('^<[{}]+\d*'.format(''.join(draw_tags))) discard_regex = re.compile('^<[{}]+\d*'.format(''.join(discard_tags))) table = Table() previous_tag = "" score = 1 is_valid_sample = False for n, tag in enumerate(self.round_content): if dry_run: print(tag) if not dry_run and tag == self.stop_tag: break if 'INIT' in tag: values = self.decoder.parse_initial_values(tag) shifted_scores = [] for x in range(0, 4): shifted_scores.append( values['scores'][self._normalize_position( x, self.player_position)]) table.init_round( values['round_number'], values['count_of_honba_sticks'], values['count_of_riichi_sticks'], values['dora_indicator'], self._normalize_position(self.player_position, values['dealer']), shifted_scores, ) hands = [ [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai0').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai1').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai2').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai3').split(',') ], ] # DEL: we can't only initialize the main player, we must initialize # other players as well. #table.player.init_hand(hands[self.player_position]) # ADD: initialize all players on the table table.players[0].init_hand(hands[self.player_position]) table.players[1].init_hand(hands[(self.player_position + 1) % 4]) table.players[2].init_hand(hands[(self.player_position + 2) % 4]) table.players[3].init_hand(hands[(self.player_position + 3) % 4]) # ADD: when restart a new game, we need to reinitialize the config self.extract_features.__init__() # We must deal with ALL players. #if player_draw_regex.match(tag) and 'UN' not in tag: if draw_regex.match(tag) and 'UN' not in tag: tile = self.decoder.parse_tile(tag) # CHG: we must deal with ALL players #table.player.draw_tile(tile) if "T" in tag: table.players[0].draw_tile(tile) elif "U" in tag: table.players[1].draw_tile(tile) elif "V" in tag: table.players[2].draw_tile(tile) elif "W" in tag: table.players[3].draw_tile(tile) #print("After draw `W`:", table.players[3].tiles) if discard_regex.match(tag) and 'DORA' not in tag: tile = self.decoder.parse_tile(tag) player_sign = tag.upper()[1] # TODO: I don't know why the author wrote the code as below, the # player_seat won't work if we use self._normalize_position. This # might be a tricky part, and we need to review it later. #player_seat = self._normalize_position(self.player_position, discard_tags.index(player_sign)) # Temporally solution to modify the player_seat player_seat = (discard_tags.index(player_sign) + self.player_position) % 4 #print("updated player seat:",player_seat) if player_seat == 0: table.players[player_seat].discard_tile( DiscardOption(table.players[player_seat], tile // 4, 0, [], 0)) else: # ADD: we must take care of ALL players tile_to_discard = tile is_tsumogiri = tile_to_discard == table.players[ player_seat].last_draw # it is important to use table method, # to recalculate revealed tiles and etc. table.add_discarded_tile(player_seat, tile_to_discard, is_tsumogiri) #print("seat:",player_seat) #print("tiles:", TilesConverter.to_one_line_string(table.players[player_seat].tiles), " discard?:", TilesConverter.to_one_line_string([tile_to_discard])) table.players[player_seat].tiles.remove(tile_to_discard) # DEL #table.add_discarded_tile(player_seat, tile, False) if '<N who=' in tag: meld = self.decoder.parse_meld(tag) #player_seat = self._normalize_position(self.player_position, meld.who) # Again, we change the player_seat here player_seat = (meld.who + self.player_position) % 4 table.add_called_meld(player_seat, meld) #if player_seat == 0: # CHG: we need to handle ALL players here if True: # we had to delete called tile from hand # to have correct tiles count in the hand if meld.type != Meld.KAN and meld.type != Meld.CHANKAN: table.players[player_seat].draw_tile(meld.called_tile) if '<REACH' in tag and 'step="1"' in tag: who_called_riichi = self._normalize_position( self.player_position, self.decoder.parse_who_called_riichi(tag)) table.add_called_riichi(who_called_riichi) # This part is to extract the features that will be used to train # our model. try: next_tag = self.round_content[n + 1] except IndexError: next_tag = "" if '<AGARI' in next_tag: who_regex = re.compile("who=\"\d+\"") fromWho_regex = re.compile("fromWho=\"\d+\"") sc_regex = "sc=\"[+-]?\d+,[+-]?\d+,[+-]?\d+,[+-]?\d+,[+-]?\d+,[+-]?\d+,[+-]?\d+,[+-]?\d+\"" score_regex = re.compile(sc_regex) who = int( who_regex.search(next_tag).group(0).replace( '"', '').split("=")[1]) fromWho = int( fromWho_regex.search(next_tag).group(0).replace( '"', '').split("=")[1]) scores = [ float(s) for s in score_regex.search(next_tag).group(0).replace( '"', '').split("=")[1].split(",") ] score = scores[fromWho * 2 + 1] player_seat, features = self.execute_extraction(tag, table) # # tsumo is not a valid sample for our training. # if (who!=fromWho): # not tsumo (lose the score to winner, score<0) # if (features is not None) and (player_seat is not None) and (score<0): # # The first element before ";" is table_info, therefore player_info starts # # from index 1, and we put who+1 here. # self.feature_to_logger(features, who+1, score) # score = 1 # tsumo is a valid sample for our training if (who == fromWho): # tsumo (win the score, score>0) if (features is not None) and (player_seat is not None) and (score > 0): self.feature_to_logger(features, who + 1, score) score = -1 else: player_seat, features = self.execute_extraction(tag, table) if not dry_run: tile = self.decoder.parse_tile(self.stop_tag) print('Hand: {}'.format(table.player.format_hand_for_print(tile))) # to rebuild all caches table.player.draw_tile(tile) tile = table.player.discard_tile() # real run, you can stop debugger here table.player.draw_tile(tile) tile = table.player.discard_tile() print('Discard: {}'.format( TilesConverter.to_one_line_string([tile])))
def reproduce(self, dry_run=False): draw_tags = ['T', 'U', 'V', 'W'] discard_tags = ['D', 'E', 'F', 'G'] player_draw = draw_tags[self.player_position] player_draw_regex = re.compile('^<[{}]+\d*'.format( ''.join(player_draw))) discard_regex = re.compile('^<[{}]+\d*'.format(''.join(discard_tags))) table = Table() for tag in self.round_content: if dry_run: print(tag) if not dry_run and tag == self.stop_tag: break if 'INIT' in tag: values = self.decoder.parse_initial_values(tag) shifted_scores = [] for x in range(0, 4): shifted_scores.append( values['scores'][self._normalize_position( x, self.player_position)]) table.init_round( values['round_number'], values['count_of_honba_sticks'], values['count_of_riichi_sticks'], values['dora_indicator'], self._normalize_position(self.player_position, values['dealer']), shifted_scores, ) hands = [ [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai0').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai1').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai2').split(',') ], [ int(x) for x in self.decoder.get_attribute_content( tag, 'hai3').split(',') ], ] table.player.init_hand(hands[self.player_position]) if player_draw_regex.match(tag) and 'UN' not in tag: tile = self.decoder.parse_tile(tag) table.player.draw_tile(tile) if discard_regex.match(tag) and 'DORA' not in tag: tile = self.decoder.parse_tile(tag) player_sign = tag.upper()[1] player_seat = self._normalize_position( self.player_position, discard_tags.index(player_sign)) if player_seat == 0: table.player.discard_tile( DiscardOption(table.player, tile // 4, 0, [], 0)) else: table.add_discarded_tile(player_seat, tile, False) if '<N who=' in tag: meld = self.decoder.parse_meld(tag) player_seat = self._normalize_position(self.player_position, meld.who) table.add_called_meld(player_seat, meld) if player_seat == 0: # we had to delete called tile from hand # to have correct tiles count in the hand if meld.type != Meld.KAN and meld.type != Meld.CHANKAN: table.player.draw_tile(meld.called_tile) if '<REACH' in tag and 'step="1"' in tag: who_called_riichi = self._normalize_position( self.player_position, self.decoder.parse_who_called_riichi(tag)) table.add_called_riichi(who_called_riichi) if not dry_run: tile = self.decoder.parse_tile(self.stop_tag) print('Hand: {}'.format(table.player.format_hand_for_print(tile))) # to rebuild all caches table.player.draw_tile(tile) tile = table.player.discard_tile() # real run, you can stop debugger here table.player.draw_tile(tile) tile = table.player.discard_tile() print('Discard: {}'.format( TilesConverter.to_one_line_string([tile])))
def test_find_impossible_waits_and_kabe_technique(self): table = Table() tiles = self._string_to_136_array(pin='11122777799') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(pin='2'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='2'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='9'), False) table.add_called_riichi(2) table.player.ai.defence.hand_34 = self._to_34_array(table.player.tiles) table.player.ai.defence.closed_hand_34 = self._to_34_array( table.player.closed_hand) result = table.player.ai.defence.kabe.find_tiles_to_discard([]) self.assertEqual(self._to_string([x.value * 4 for x in result]), '19p') table = Table() tiles = self._string_to_136_array(pin='33337777') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_called_riichi(2) table.player.ai.defence.hand_34 = self._to_34_array(table.player.tiles) table.player.ai.defence.closed_hand_34 = self._to_34_array( table.player.closed_hand) result = table.player.ai.defence.kabe.find_tiles_to_discard([]) self.assertEqual(self._to_string([x.value * 4 for x in result]), '5p') table = Table() tiles = self._string_to_136_array(pin='33334446666') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_discarded_tile(1, self._string_to_136_tile(pin='5'), False) table.add_called_riichi(2) table.player.ai.defence.hand_34 = self._to_34_array(table.player.tiles) table.player.ai.defence.closed_hand_34 = self._to_34_array( table.player.closed_hand) result = table.player.ai.defence.kabe.find_tiles_to_discard([]) self.assertEqual(self._to_string([x.value * 4 for x in result]), '45p')