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_should_go_for_defence_and_good_hand_with_drawn_tile(self): """ When we have 14 tiles in hand and someone declared a riichi """ table = Table() table.has_aka_dora = True tiles = self._string_to_136_array(sou='2223457899', honors='666') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='8')) table.player.add_called_meld(self._make_meld(Meld.PON, sou='222')) table.player.add_called_meld(self._make_meld(Meld.PON, honors='666')) self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_called_riichi(3) results, shanten = table.player.ai.calculate_outs( table.player.tiles, table.player.closed_hand, table.player.open_hand_34_tiles) selected_tile = table.player.ai.process_discard_options_and_select_tile_to_discard( results, shanten) self.assertEqual( table.player.ai.defence.should_go_to_defence_mode(selected_tile), False) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '8m')
def test_detect_enemy_tempai_and_riichi(self): table = Table() self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, False) self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, False) table.add_called_riichi(1) self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True) self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
def test_dont_call_kan_in_defence_mode(self): table = Table() tiles = self._string_to_136_array(man='12589', sou='111459', pin='12') table.player.init_hand(tiles) table.add_called_riichi(1) tile = self._string_to_136_tile(sou='1') self.assertEqual(table.player.should_call_kan(tile, False), None)
def test_detect_enemy_tempai_and_riichi(self): table = Table() self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, False) self.assertEqual( EnemyAnalyzer(table.get_player(1)).is_threatening, False) table.add_called_riichi(1) self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True) self.assertEqual( EnemyAnalyzer(table.get_player(1)).is_threatening, True)
def test_not_open_hand_in_defence_mode(self): table = Table() player = table.player tiles = self._string_to_136_array(sou='12368', pin='2358', honors='4455') player.init_hand(tiles) table.add_called_riichi(1) tile = self._string_to_136_tile(honors='5') meld, _ = player.try_to_call_meld(tile, False) self.assertEqual(meld, None)
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_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_call_riichi_with_bad_wait_against_other_player_riichi(self): table = Table() table.count_of_remaining_tiles = 60 table.player.scores = 25000 tiles = self._string_to_136_array(sou='11223', pin='234678', man='55') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='9')) table.add_called_riichi(3) discard = table.player.discard_tile() self.assertEqual(self._to_string([discard]), '9m') self.assertEqual(table.player.ai.in_defence, True) self.assertEqual(table.player.can_call_riichi(), False)
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_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_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_should_go_for_defence_and_good_hand(self): """ When we have 13 tiles in hand and someone declared a riichi """ table = Table() table.set_players_names_and_ranks([ { "name": "a", "rank": 1500 }, { "name": "b", "rank": 1500 }, { "name": "c", "rank": 1500 }, { "name": "d", "rank": 1500 }, ]) table.set_players_scores([250, 250, 250, 250]) tiles = self._string_to_136_array(sou='234678', pin='34789', man='77') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='6')) # discard here to reinit shanten number in AI table.player.discard_tile() self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_called_riichi(3) # our hand is in tempai, but it is really cheap self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_dora_indicator(self._string_to_136_tile(man='4')) table.add_dora_indicator(self._string_to_136_tile(pin='3')) # our hand in tempai, and it has a cost, so let's push it self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), 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_should_go_for_defence_and_bad_hand(self): """ When we have 13 tiles in hand and someone declared a riichi """ table = Table() tiles = self._string_to_136_array(sou='1259', pin='12348', honors='3456') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='6')) # discard here to reinit shanten number in AI table.player.discard_tile() self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_called_riichi(3) # our hand is pretty bad, there is no sense to push it against riichi self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), True)
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_should_go_for_defence_and_good_hand_with_drawn_tile(self): """ When we have 14 tiles in hand and someone declared a riichi """ table = Table() table.has_aka_dora = True tiles = self._string_to_136_array(sou='2223457899', honors='666') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='8')) table.player.add_called_meld(self._make_meld(Meld.PON, sou='222')) table.player.add_called_meld(self._make_meld(Meld.PON, honors='666')) self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_called_riichi(3) result = table.player.discard_tile() self.assertEqual(self._to_string([result]), '8m')
def test_find_impossible_waits_and_honor_tiles(self): table = Table() tiles = self._string_to_136_array(honors='1133', man='123', sou='456', pin='999') table.player.init_hand(tiles) table.player.add_called_meld(self._make_meld(Meld.CHI, man='123')) table.player.add_called_meld(self._make_meld(Meld.CHI, sou='456')) table.player.add_called_meld(self._make_meld(Meld.PON, pin='999')) 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_tile_to_discard_when_no_safe_tile(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='2234555', pin='11', man='11113') table.player.init_hand(tiles) table.add_discarded_tile(1, self._string_to_136_tile(man='8'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='9'), 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() # No safe tile to discard. So should discard 5s self.assertEqual(self._to_string([result]), '1m') tiles = self._string_to_136_array(sou='2234556', pin='11', man='34567') table.player.init_hand(tiles) result = table.player.discard_tile() # No safe tile to discard. So should discard 1p self.assertEqual(self._to_string([result]), '1p') tiles = self._string_to_136_array(sou='2345678', pin='19', man='34567') table.player.init_hand(tiles) result = table.player.discard_tile() # No safe tile to discard. So should discard 1p self.assertEqual(self._to_string([result]), '1p')
def test_should_go_for_defence_and_good_hand(self): """ When we have 13 tiles in hand and someone declared a riichi """ table = Table() tiles = self._string_to_136_array(sou='234678', pin='34789', man='77') table.player.init_hand(tiles) table.player.draw_tile(self._string_to_136_tile(man='6')) # discard here to reinit shanten number in AI table.player.discard_tile() self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False) table.add_called_riichi(3) # our hand is in tempai, but it is really cheap self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), True) table.add_dora_indicator(self._string_to_136_tile(man='4')) table.add_dora_indicator(self._string_to_136_tile(pin='3')) # our hand in tempai, and it has a cost, so let's push it self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False)
def test_find_impossible_waits_and_kabe_technique(self): table = Table() tiles = self._string_to_136_array(pin='11122777799', man='999') table.player.init_hand(tiles) table.player.add_called_meld(self._make_meld(Meld.PON, man='999')) 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', man='888999') table.player.init_hand(tiles) table.player.add_called_meld(self._make_meld(Meld.PON, man='888')) table.player.add_called_meld(self._make_meld(Meld.PON, man='999')) 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', man='999') table.player.init_hand(tiles) table.player.add_called_meld(self._make_meld(Meld.PON, man='999')) 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')
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 player_draw_regex.match(tag) and 'UN' not in tag: print('Player draw') tile = self.decoder.parse_tile(tag) table.player.draw_tile(tile) if dry_run: if self._is_draw(tag): print( '<-', TilesConverter.to_one_line_string( [self._parse_tile(tag)]), tag) elif self._is_discard(tag): print( '->', TilesConverter.to_one_line_string( [self._parse_tile(tag)]), tag) elif self._is_init_tag(tag): hands = { 0: [ int(x) for x in self._get_attribute_content( tag, 'hai0').split(',') ], 1: [ int(x) for x in self._get_attribute_content( tag, 'hai1').split(',') ], 2: [ int(x) for x in self._get_attribute_content( tag, 'hai2').split(',') ], 3: [ int(x) for x in self._get_attribute_content( tag, 'hai3').split(',') ], } print( 'Initial hand:', TilesConverter.to_one_line_string( hands[self.player_position])) else: 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_wind_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 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(tile) 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 reproduce(self, dry_run=True, verbose=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(self.params) total = defaultdict(int) results = defaultdict(int) for i, r in enumerate(self.rounds): print("Round:", i) print() for tag in r: if dry_run: #print(tag) pass 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: # TODO: add player's state, river, melds, and reach timepoint current_hand = TilesConverter.to_one_line_string( table.player.tiles) choice = table.player.ai.discard_tile(None) table.player.discard_tile(tile) match = int(tile == choice) total["TOTAL"] += 1 results["TOTAL"] += match total[table.player.play_state] += 1 results[table.player.play_state] += match if verbose: print("Hand:", current_hand) print("AI's Choice:", TilesConverter.to_one_line_string([choice])) print("MP's Choice:", TilesConverter.to_one_line_string(([tile]))) print("AI's State:", table.player.play_state) print("Same:", tile == choice) print() 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) # TODO: add reach time point if dry_run: print(total, results) return total, results 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 player_draw_regex.match(tag) and 'UN' not in tag: print('Player draw') tile = self.decoder.parse_tile(tag) table.player.draw_tile(tile) if dry_run: if self._is_draw(tag): print('<-', TilesConverter.to_one_line_string([self._parse_tile(tag)]), tag) elif self._is_discard(tag): print('->', TilesConverter.to_one_line_string([self._parse_tile(tag)]), tag) elif self._is_init_tag(tag): hands = { 0: [int(x) for x in self._get_attribute_content(tag, 'hai0').split(',')], 1: [int(x) for x in self._get_attribute_content(tag, 'hai1').split(',')], 2: [int(x) for x in self._get_attribute_content(tag, 'hai2').split(',')], 3: [int(x) for x in self._get_attribute_content(tag, 'hai3').split(',')], } print('Initial hand:', TilesConverter.to_one_line_string(hands[self.player_position])) else: 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_wind_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 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(tile) 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])))