def test_atodzuke_keep_yakuhai_wait(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) tiles = self._string_to_136_array(man='11144', sou='567', pin='567', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) # two of 4 man tiles are already out, so it would seem our wait is worse, but we know # we must keep two pairs in order to be atodzuke tempai table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) player.draw_tile(self._string_to_136_tile(man='2')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '2m')
def test_choose_1_shanten_with_cost_possibility_meld(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(man='557', pin='468', sou='55577', honors='66') player.init_hand(tiles) meld = self._make_meld(Meld.PON, sou='555') player.add_called_meld(meld) tile = self._string_to_136_tile(sou='7') meld, discard_option = player.try_to_call_meld(tile, False) self.assertNotEqual(meld, None) self.assertEqual(meld.type, Meld.PON) self.assertEqual(self._to_string(meld.tiles), '777s') self.assertNotEqual(player.ai.current_strategy, None) self.assertEqual(player.ai.current_strategy.type, BaseStrategy.YAKUHAI) discarded_tile = discard_option.find_tile_in_hand(player.closed_hand) self.assertEqual(self._to_string([discarded_tile]), '7m')
def test_keep_only_yakuhai_pair(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) table.add_dora_indicator(self._string_to_136_tile(man='3')) table.add_discarded_tile(1, self._string_to_136_tile(honors='7'), False) tiles = self._string_to_136_array(man='11144', sou='567', pin='156', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) player.draw_tile(self._string_to_136_tile(pin='1')) discarded_tile = player.discard_tile() self.assertNotEqual(self._to_string([discarded_tile]), '7z')
def test_dont_open_bad_hand_if_there_are_expensive_threat(): table = Table() table.add_dora_indicator(string_to_136_tile(man="4")) player = table.player player.round_step = 10 table.has_open_tanyao = True table.has_aka_dora = True enemy_seat = 1 table.add_called_riichi_step_one(enemy_seat) table.add_discarded_tile(enemy_seat, string_to_136_tile(honors="4"), True) tiles = string_to_136_array(sou="226", pin="2469", man="3344", honors="4") + [FIVE_RED_MAN] player.init_hand(tiles) # cheap enemy tempai, but this meld is garbage, let's not push tile = string_to_136_array(man="4444")[2] meld, _ = player.try_to_call_meld(tile, True) assert meld is None # cheap enemy tempai, and good chi, let's take this meld tile = string_to_136_tile(man="2") meld, _ = player.try_to_call_meld(tile, True) assert meld is not None table.add_called_meld( enemy_seat, make_meld(MeldPrint.KAN, is_open=False, honors="1111")) # enemy hand is more expensive now (12000) # in this case let's not open this hand tile = string_to_136_tile(man="2") meld, _ = player.try_to_call_meld(tile, True) assert meld is None
def test_atodzuke_dont_open_no_yaku_tempai(): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(man="9")) tiles = string_to_136_array(man="111445", sou="567", pin="56", honors="77") player.init_hand(tiles) meld = make_meld(MeldPrint.PON, man="111") player.add_called_meld(meld) # 6 man is bad meld, we lose our second pair and so is 4 man tile = string_to_136_tile(man="6") meld, _ = player.try_to_call_meld(tile, True) assert meld is None strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) assert strategy.should_activate_strategy(player.tiles) is True tile = string_to_136_tile(man="4") meld, _ = player.try_to_call_meld(tile, True) assert meld is None strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) assert strategy.should_activate_strategy(player.tiles) is True # 7 pin is a good meld, we get to tempai keeping yakuhai wait tile = string_to_136_tile(pin="7") meld, _ = player.try_to_call_meld(tile, True) assert meld is not None strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) assert strategy.should_activate_strategy(player.tiles) is True
def test_must_push_1st_and_4th_place_riichi(): table = Table() player = table.player table.has_aka_dora = True table.has_open_tanyao = True # orasu table.round_wind_number = 7 table.dealer_seat = 1 player.dealer_seat = 1 table.add_dora_indicator(string_to_136_tile(sou="1")) # we have 1-shanten with no doras, but we must push because we have 4th place in oorasu tiles = string_to_136_array(man="3488", sou="334678", pin="456") table.player.init_hand(tiles) table.player.round_step = 5 player.scores = 45000 assert table.players[0] == player table.players[1].scores = 42000 table.players[2].scores = 5000 table.players[3].scores = 8000 enemy_seat = 3 table.add_called_riichi_step_one(enemy_seat) threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 1 assert not player.ai.placement.must_push(threatening_players, 0, 1)
def test_calculate_our_hand_cost_1_shanten(): table = Table() player = table.player enemy_seat = 2 table.has_open_tanyao = True table.has_aka_dora = False table.add_called_riichi_step_one(enemy_seat) tiles = string_to_136_array(sou="22245677", pin="145", man="67") tile = string_to_136_tile(honors="1") player.init_hand(tiles) player.add_called_meld(make_meld(MeldPrint.PON, sou="222")) player.draw_tile(tile) discard_option = find_discard_option(player, honors="1") cost = discard_option.average_second_level_cost assert cost == 1500 table.add_dora_indicator(string_to_136_tile(sou="6")) discard_option = find_discard_option(player, honors="1") cost = discard_option.average_second_level_cost assert cost == 5850 table.add_dora_indicator(string_to_136_tile(pin="2")) discard_option = find_discard_option(player, honors="1") cost = discard_option.average_second_level_cost assert cost == 8737
def test_discard_tile_and_wrong_tiles_valuation(): """ Bot wanted to discard 5m from the first hand, because valuation for 2p was miscalculated (too high) Same issue with wrong valuation was with second hand """ table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(honors="2")) tiles = string_to_136_array(man="5", pin="256678", sou="2333467") player.init_hand(tiles) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "2p" table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(man="5")) tiles = string_to_136_array(man="45667", pin="34677", sou="38", honors="22") player.init_hand(tiles) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "8s"
def test_skip_cheap_meld_1_shanten_can_move_to_west(): table = Table() player = table.player table.has_aka_dora = True table.has_open_tanyao = True # orasu table.round_wind_number = 7 table.dealer_seat = 1 player.dealer_seat = 1 table.add_dora_indicator(string_to_136_tile(sou="2")) tiles = string_to_136_array(man="3488", sou="334678", pin="268") table.player.init_hand(tiles) table.player.round_step = 12 player.scores = 18000 assert table.players[0] == player table.players[1].scores = 28000 table.players[2].scores = 29000 table.players[3].scores = 31000 # it's cheap, but with ron from first place we can move game to west round, so let's do it tile = string_to_136_tile(sou="2") meld, _ = table.player.try_to_call_meld(tile, True) assert meld is not None # now this is the cost we might win with tile = string_to_136_tile(sou="3") meld, _ = table.player.try_to_call_meld(tile, True) assert meld is not None
def test_choose_better_tanki_honor(): table = Table() player = table.player player.round_step = 2 player.dealer_seat = 3 table.add_dora_indicator(string_to_136_tile(man="8")) tiles = string_to_136_array(man="11447799", sou="556", honors="45") player.init_hand(tiles) player.draw_tile(string_to_136_tile(honors="4")) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "6s" tiles = string_to_136_array(man="11447799", sou="556", honors="45") player.init_hand(tiles) player.draw_tile(string_to_136_tile(honors="5")) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "6s" tiles = string_to_136_array(man="11447799", sou="556", honors="45") player.init_hand(tiles) player.draw_tile(string_to_136_tile(sou="6")) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "5z"
def test_discard_tile_and_wrong_tiles_valuation(self): """ Bot wanted to discard 5m from the first hand, because valuation for 2p was miscalculated (too high) Same issue with wrong valuation was with second hand """ table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(honors='2')) tiles = self._string_to_136_array(man='445567', pin='245678', sou='67') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '2p') table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='5')) tiles = self._string_to_136_array(man='45667', pin='34677', sou='38', honors='22') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '8s')
def test_choose_better_tanki_honor(self): table = Table() player = table.player player.round_step = 2 player.dealer_seat = 3 table.add_dora_indicator(self._string_to_136_tile(man='8')) tiles = self._string_to_136_array(man='11447799', sou='556', honors='45') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(honors='4')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '6s') tiles = self._string_to_136_array(man='11447799', sou='556', honors='45') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(honors='5')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '6s') tiles = self._string_to_136_array(man='11447799', sou='556', honors='45') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='6')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '5z') tiles = self._string_to_136_array(man='11447799', sou='556', honors='34') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='6')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '3z')
def test_is_threatening_and_toitoi_melds(): table = Table() threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 0 enemy_seat = 2 table.player.round_step = 2 table.add_called_meld(enemy_seat, make_meld(MeldPrint.PON, pin="222")) table.add_called_meld(enemy_seat, make_meld(MeldPrint.PON, honors="444")) table.add_called_meld(enemy_seat, make_meld(MeldPrint.PON, sou="999")) table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="1"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(man="5"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="8"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(pin="9"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="4"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(man="3"), False) table.add_dora_indicator(string_to_136_tile(pin="1")) threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 1 assert threatening_players[0].enemy.seat == enemy_seat assert threatening_players[0].threat_reason[ "id"] == EnemyDanger.THREAT_EXPENSIVE_OPEN_HAND["id"] assert threatening_players[0].get_assumed_hand_cost( string_to_136_tile(man="2")) == 8000
def test_discard_tile_and_wrong_tiles_valuation(self): """ Bot wanted to discard 5m from the first hand, because valuation for 2p was miscalculated (too high) Same issue with wrong valuation was with second hand """ table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(honors='2')) tiles = self._string_to_136_array(man='445567', pin='245678', sou='67') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '2p') table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='5')) tiles = self._string_to_136_array(man='45667', pin='34677', sou='38', honors='22') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '8s')
def test_is_threatening_and_dora_pon(): table = Table() threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 0 enemy_seat = 2 table.add_called_meld(enemy_seat, make_meld(MeldPrint.PON, man="333")) table.player.round_step = 7 table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="1"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(man="5"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="8"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(pin="9"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="4"), False) table.add_discarded_tile(enemy_seat, string_to_136_tile(man="7"), False) # simple pon it is no threat threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 0 # dora pon is threat table.add_dora_indicator(string_to_136_tile(man="2")) threatening_players = table.player.ai.defence.get_threatening_players() assert len(threatening_players) == 1 assert threatening_players[0].enemy.seat == enemy_seat assert threatening_players[0].threat_reason[ "id"] == EnemyDanger.THREAT_OPEN_HAND_AND_MULTIPLE_DORA["id"] assert threatening_players[0].get_assumed_hand_cost( string_to_136_tile(man="2")) == 8000
def test_skip_cheap_meld_2_shanten(): table = Table() player = table.player table.has_aka_dora = True table.has_open_tanyao = True # orasu table.round_wind_number = 7 table.dealer_seat = 1 player.dealer_seat = 1 table.add_dora_indicator(string_to_136_tile(sou="2")) tiles = string_to_136_array(man="34889", sou="33468", pin="268") table.player.init_hand(tiles) table.player.round_step = 12 player.scores = 18000 assert table.players[0] == player table.players[1].scores = 28000 table.players[2].scores = 35000 table.players[3].scores = 40000 # it's too cheap, let's not open tile = string_to_136_tile(sou="2") meld, _ = table.player.try_to_call_meld(tile, True) assert meld is None # now this is the cost we might win with tile = string_to_136_tile(sou="3") meld, _ = table.player.try_to_call_meld(tile, True) assert meld is not None
def test_atodzuke_dont_open_no_yaku_tempai(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) tiles = self._string_to_136_array(man='111445', sou='567', pin='56', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) # 6 man is bad meld, we lose our second pair and so is 4 man tile = self._string_to_136_tile(man='6') meld, _ = player.try_to_call_meld(tile, True) self.assertEqual(meld, None) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) tile = self._string_to_136_tile(man='4') meld, _ = player.try_to_call_meld(tile, True) self.assertEqual(meld, None) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) # 7 pin is a good meld, we get to tempai keeping yakuhai wait tile = self._string_to_136_tile(pin='7') meld, _ = player.try_to_call_meld(tile, True) self.assertNotEqual(meld, None) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True)
def test_skip_ron_in_west_4(): table = Table() player = table.player table.has_aka_dora = True table.has_open_tanyao = True # orasu table.round_wind_number = 11 table.dealer_seat = 1 player.dealer_seat = 1 table.add_dora_indicator(string_to_136_tile(sou="1")) tiles = string_to_136_array(man="23488", sou="34678", pin="567") table.player.init_hand(tiles) table.player.add_called_meld(make_meld(MeldPrint.CHI, pin="567")) table.player.round_step = 14 player.scores = 20100 assert table.players[0] == player table.players[1].scores = 22000 table.players[2].scores = 26000 table.players[3].scores = 29900 assert player.should_call_win(string_to_136_tile(sou="5"), False, 1) assert not player.should_call_win(string_to_136_tile(sou="5"), False, 2) assert not player.should_call_win(string_to_136_tile(sou="5"), False, 3)
def test_discard_tile_with_better_wait_in_iishanten(): table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(sou="4")) tiles = string_to_136_array(man="123567", pin="113788", sou="99") player.init_hand(tiles) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "8p"
def _make_table(): table = Table() table.has_open_tanyao = True # add doras so we are sure to go for tanyao table.add_dora_indicator(string_to_136_tile(sou="1")) table.add_dora_indicator(string_to_136_tile(man="1")) table.add_dora_indicator(string_to_136_tile(pin="1")) return table
def test_discard_tile_with_better_wait_in_iishanten(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(man='123567', pin='113788', sou='99') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '8p')
def test_discard_tile_with_better_wait_in_iishanten(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(man='123567', pin='113788', sou='99') player.init_hand(tiles) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '8p')
def test_prefer_valuable_tiles_with_almost_same_tiles_count(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(sou='1366', pin='123456', man='345') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='5')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '1s')
def test_prefer_valuable_tiles_with_almost_same_ukeire(): table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(sou="4")) tiles = string_to_136_array(sou="1366", pin="123456", man="345") player.init_hand(tiles) player.draw_tile(string_to_136_tile(sou="5")) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "1s"
def test_discard_tile_with_max_ukeire_second_level(): table = Table() player = table.player table.add_dora_indicator(string_to_136_tile(sou="4")) tiles = string_to_136_array(sou="11367", pin="45", man="344778") player.init_hand(tiles) player.draw_tile(string_to_136_tile(pin="6")) discarded_tile, _ = player.discard_tile() assert tiles_to_string([discarded_tile]) == "3s"
def test_prefer_valuable_tiles_with_almost_same_ukeire(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(sou='1366', pin='123456', man='345') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='5')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '1s')
def test_discard_tile_with_max_ukeire_second_level(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(sou='11367', pin='45', man='344778') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(pin='6')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '3s')
def test_discard_tile_with_max_ukeire_second_level(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(sou='11367', pin='45', man='344778') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(pin='6')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '3s')
def test_calculate_second_level_ukeire(self): """ There was a bug with 2356 form and second level ukeire """ table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='2')) table.add_discarded_tile(1, self._string_to_136_tile(man='2'), 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='3'), False) tiles = self._string_to_136_array(man='34678', pin='2356', sou='4467') tile = self._string_to_136_tile(sou='8') player.init_hand(tiles) player.draw_tile(tile) discard_options, _ = player.ai.hand_builder.find_discard_options( player.tiles, player.closed_hand, player.melds ) tile = self._string_to_136_tile(man='4') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 108) tile = self._string_to_136_tile(man='3') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 108) tile = self._string_to_136_tile(pin='2') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 96) tile = self._string_to_136_tile(pin='3') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 96) tile = self._string_to_136_tile(pin='5') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 96) tile = self._string_to_136_tile(pin='6') discard_option = [x for x in discard_options if x.tile_to_discard == tile // 4][0] player.ai.hand_builder.calculate_second_level_ukeire(discard_option, player.tiles, player.melds) self.assertEqual(discard_option.ukeire_second, 96)
def test_get_common_tempai_sanshoku(): table = Table() table.add_dora_indicator(string_to_136_tile(man="8")) tiles = string_to_136_array(man="13999", sou="123", pin="12899") table.player.init_hand(tiles) tile = string_to_136_tile(pin="3") meld, _ = table.player.try_to_call_meld(tile, True) assert meld is not None assert tiles_to_string(meld.tiles) == "123p"
def test_should_activate_strategy(self): table = Table() player = table.player strategy = HonitsuStrategy(BaseStrategy.HONITSU, player) table.add_dora_indicator(self._string_to_136_tile(pin='1')) table.add_dora_indicator(self._string_to_136_tile(honors='5')) tiles = self._string_to_136_array(sou='12355', man='12389', honors='123') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # many tiles in one suit and yakuhai pair, but still many useless winds tiles = self._string_to_136_array(sou='12355', man='23', pin='68', honors='2355') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # many tiles in one suit and yakuhai pair and another honor pair, so # now this is honitsu tiles = self._string_to_136_array(sou='12355', man='238', honors='22355') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) # same conditions, but ready suit with dora in another suit, so no honitsu tiles = self._string_to_136_array(sou='12355', pin='234', honors='22355') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # same conditions, but we have a pon of yakuhai doras, we shouldn't # force honitsu with this hand tiles = self._string_to_136_array(sou='12355', pin='238', honors='22666') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # if we have a complete set with dora, we shouldn't go for honitsu tiles = self._string_to_136_array(sou='11123688', pin='123', honors='55') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # even if the set may be interpreted as two forms tiles = self._string_to_136_array(sou='1223688', pin='2334', honors='55') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # even if the set may be interpreted as two forms v2 tiles = self._string_to_136_array(sou='1223688', pin='2345', honors='55') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False) # if we have a long form with dora, we shouldn't go for honitsu tiles = self._string_to_136_array(sou='1223688', pin='2333', honors='55') player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(player.tiles), False)
def test_get_common_tempai_sandoko(): table = Table() table.add_dora_indicator(string_to_136_tile(man="1")) tiles = string_to_136_array(man="222", sou="2278", pin="222899") table.player.init_hand(tiles) tile = string_to_136_tile(sou="2") meld, _ = table.player.try_to_call_meld(tile, False) assert meld is not None assert tiles_to_string(meld.tiles) == "222s"
def test_should_activate_strategy(): table = Table() player = table.player strategy = HonitsuStrategy(BaseStrategy.HONITSU, player) table.add_dora_indicator(string_to_136_tile(pin="1")) table.add_dora_indicator(string_to_136_tile(honors="5")) tiles = string_to_136_array(sou="12355", man="12389", honors="123") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # many tiles in one suit and yakuhai pair, but still many useless winds tiles = string_to_136_array(sou="12355", man="23", pin="68", honors="2355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # many tiles in one suit and yakuhai pair and another honor pair, so # now this is honitsu tiles = string_to_136_array(sou="12355", man="238", honors="22355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is True # same conditions, but ready suit with dora in another suit, so no honitsu tiles = string_to_136_array(sou="12355", pin="234", honors="22355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # same conditions, but we have a pon of yakuhai doras, we shouldn't # force honitsu with this hand tiles = string_to_136_array(sou="12355", pin="238", honors="22666") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # if we have a complete set with dora, we shouldn't go for honitsu tiles = string_to_136_array(sou="11123688", pin="123", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # even if the set may be interpreted as two forms tiles = string_to_136_array(sou="1223688", pin="2334", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # even if the set may be interpreted as two forms v2 tiles = string_to_136_array(sou="1223688", pin="2345", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # if we have a long form with dora, we shouldn't go for honitsu tiles = string_to_136_array(sou="1223688", pin="2333", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False
def test_open_hand_and_not_go_for_atodzuke_yakuhai(): table = Table() # dora here to activate honitsu strategy table.add_dora_indicator(string_to_136_tile(sou="9")) player = table.player player.seat = 1 tiles = string_to_136_array(sou="1112345678", honors="557") player.init_hand(tiles) tile = string_to_136_array(sou="1111")[3] meld, _ = player.try_to_call_meld(tile, False) assert meld is not None assert tiles_to_string(meld.tiles) == "111s"
def _make_table(dora_indicators=None): table = Table() table.count_of_remaining_tiles = 60 table.player.scores = 25000 # with that we don't have daburi anymore table.player.round_step = 1 if dora_indicators: for x in dora_indicators: table.add_dora_indicator(x) return table
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_open_hand_when_yakuhai_already_in_the_hand(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(honors='5')) tiles = self._string_to_136_array(man='46', pin='4679', sou='1348', honors='666') player.init_hand(tiles) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) tile = self._string_to_136_tile(sou='2') meld, _ = player.try_to_call_meld(tile, True) self.assertNotEqual(meld, None)
def test_call_shouminkan_and_bad_ukeire_after_call_second_case(): table = Table() table.add_dora_indicator(string_to_136_tile(honors="5")) table.count_of_remaining_tiles = 10 player = table.player tiles = string_to_136_array(man="3455567", sou="222", honors="666") player.init_hand(tiles) player.add_called_meld(make_meld(MeldPrint.PON, man="555")) player.add_called_meld(make_meld(MeldPrint.PON, honors="666")) tile = string_to_136_array(man="5555")[3] player.draw_tile(tile) assert player.should_call_kan(tile, False) is None
def test_discard_tile_based_on_second_level_ukeire_and_cost(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='2')) table.add_discarded_tile(1, self._string_to_136_tile(man='2'), False) tiles = self._string_to_136_array(man='34678', pin='2356', sou='4467') tile = self._string_to_136_tile(sou='8') player.init_hand(tiles) player.draw_tile(tile) discarded_tile = player.discard_tile() discard_correct = self._to_string([discarded_tile]) == '2p' or self._to_string([discarded_tile]) == '3p' self.assertEqual(discard_correct, True)
def test_choose_1_shanten_with_cost_possibility_draw(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(man='557', pin='468', sou='55577', honors='66') player.init_hand(tiles) meld = self._make_meld(Meld.PON, sou='555') player.add_called_meld(meld) tile = self._string_to_136_tile(sou='7') player.draw_tile(tile) discarded_tile = player.discard_tile() self.assertNotEqual(player.ai.current_strategy, None) self.assertEqual(player.ai.current_strategy.type, BaseStrategy.YAKUHAI) self.assertEqual(self._to_string([discarded_tile]), '7m')
def test_5_pairs_yakuhai_not_chiitoitsu(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='9')) table.add_dora_indicator(self._string_to_136_tile(sou='1')) tiles = self._string_to_136_array(sou='112233', pin='16678', honors='66') player.init_hand(tiles) tile = self._string_to_136_tile(honors='6') meld, _ = player.try_to_call_meld(tile, True) self.assertNotEqual(player.ai.current_strategy.type, BaseStrategy.CHIITOITSU) self.assertEqual(player.ai.current_strategy.type, BaseStrategy.YAKUHAI) self.assertNotEqual(meld, None)
def test_keep_only_yakuhai_pon(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) table.add_dora_indicator(self._string_to_136_tile(man='3')) tiles = self._string_to_136_array(man='11144', sou='567', pin='56', honors='777') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) player.draw_tile(self._string_to_136_tile(man='4')) discarded_tile = player.discard_tile() self.assertNotEqual(self._to_string([discarded_tile]), '7z')
def test_atodzuke_dont_destroy_second_pair(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) tiles = self._string_to_136_array(man='111445', sou='468', pin='56', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) # 6 man is bad meld, we lose our second pair and so is 4 man tile = self._string_to_136_tile(man='6') meld, _ = player.try_to_call_meld(tile, True) self.assertEqual(meld, None) tile = self._string_to_136_tile(man='4') meld, _ = player.try_to_call_meld(tile, True) self.assertEqual(meld, None) # but if we have backup pair it's ok tiles = self._string_to_136_array(man='111445', sou='468', pin='88', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) # 6 man is bad meld, we lose our second pair and so is 4 man tile = self._string_to_136_tile(man='6') meld, _ = player.try_to_call_meld(tile, True) self.assertNotEqual(meld, None) tile = self._string_to_136_tile(man='4') meld, _ = player.try_to_call_meld(tile, True) self.assertNotEqual(meld, None)
def test_atodzuke_choose_hidden_syanpon(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) tiles = self._string_to_136_array(man='111678', sou='56678', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) for _ in range(0, 4): table.add_discarded_tile(1, self._string_to_136_tile(sou='9'), False) player.draw_tile(self._string_to_136_tile(man='6')) discarded_tile = player.discard_tile() self.assertNotEqual(self._to_string([discarded_tile]), '6m')
def test_discard_less_valuable_isolated_tile_first(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(sou='2456', pin='129', man='234458') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='7')) discarded_tile = player.discard_tile() # we have a choice what to discard: 9p or 8m # 9p is less valuable self.assertEqual(self._to_string([discarded_tile]), '9p') table.dora_indicators.append(self._string_to_136_tile(pin='8')) tiles = self._string_to_136_array(sou='2456', pin='129', man='234458') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(sou='7')) discarded_tile = player.discard_tile() # but if 9p is dora # let's discard 8m instead self.assertEqual(self._to_string([discarded_tile]), '8m')
def test_atodzuke_keep_yakuhai_wait(self): # make sure yakuhai strategy is activated by adding 3 doras to the hand table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(man='9')) tiles = self._string_to_136_array(man='11144', sou='567', pin='567', honors='77') player.init_hand(tiles) meld = self._make_meld(Meld.PON, man='111') player.add_called_meld(meld) # two of 4 man tiles are already out, so it would seem our wait is worse, but we know # we must keep two pairs in order to be atodzuke tempai table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False) strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player) self.assertEqual(strategy.should_activate_strategy(player.tiles), True) player.draw_tile(self._string_to_136_tile(man='2')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '2m')
def test_choose_1_shanten_with_cost_possibility_meld(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) tiles = self._string_to_136_array(man='557', pin='468', sou='55577', honors='66') player.init_hand(tiles) meld = self._make_meld(Meld.PON, sou='555') player.add_called_meld(meld) tile = self._string_to_136_tile(sou='7') meld, discard_option = player.try_to_call_meld(tile, False) self.assertNotEqual(meld, None) self.assertEqual(meld.type, Meld.PON) self.assertEqual(self._to_string(meld.tiles), '777s') self.assertNotEqual(player.ai.current_strategy, None) self.assertEqual(player.ai.current_strategy.type, BaseStrategy.YAKUHAI) discarded_tile = discard_option.find_tile_in_hand(player.closed_hand) self.assertEqual(self._to_string([discarded_tile]), '7m')
def test_choose_correct_wait_yaku_versus_dora(self): table = Table() player = table.player player.round_step = 2 table.add_dora_indicator(self._string_to_136_tile(pin='4')) tiles = self._string_to_136_array(man='23478', sou='23488', pin='235') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(pin='4')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '5p') table = Table() player = table.player player.round_step = 2 table.add_dora_indicator(self._string_to_136_tile(pin='1')) tiles = self._string_to_136_array(man='34578', sou='34588', pin='235') player.init_hand(tiles) player.draw_tile(self._string_to_136_tile(pin='4')) discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '2p')
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)
class CallRiichiTestCase(unittest.TestCase, TestMixin): def _make_table(self, dora_indicators=None): self.table = Table() self.table.count_of_remaining_tiles = 60 self.player = self.table.player self.player.scores = 25000 # with that we don't have daburi anymore self.player.round_step = 1 if dora_indicators: for x in dora_indicators: self.table.add_dora_indicator(x) def test_dont_call_riichi_with_yaku_and_central_tanki_wait(self): self._make_table() tiles = self._string_to_136_array(sou='234567', pin='234567', man='4') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(man='5')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) def test_dont_call_riichi_expensive_damaten_with_yaku(self): self._make_table(dora_indicators=[ self._string_to_136_tile(man='7'), self._string_to_136_tile(man='5'), self._string_to_136_tile(sou='1'), ]) # tanyao pinfu sanshoku dora 4 - this is damaten baiman, let's not riichi it tiles = self._string_to_136_array(man='67888', sou='678', pin='34678') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) # let's test lots of doras hand, tanyao dora 8, also damaten baiman tiles = self._string_to_136_array(man='666888', sou='22', pin='34678') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) # chuuren tiles = self._string_to_136_array(man='1112345678999') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) def test_riichi_expensive_hand_without_yaku(self): self._make_table(dora_indicators=[ self._string_to_136_tile(man='1'), self._string_to_136_tile(sou='1'), self._string_to_136_tile(pin='1') ]) tiles = self._string_to_136_array(man='222', sou='22278', pin='22789') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_riichi_tanki_honor_without_yaku(self): self._make_table( dora_indicators=[ self._string_to_136_tile(man='2'), self._string_to_136_tile(sou='6') ] ) tiles = self._string_to_136_array(man='345678', sou='789', pin='123', honors='2') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_riichi_tanki_honor_chiitoitsu(self): self._make_table() tiles = self._string_to_136_array(man='22336688', sou='99', pin='99', honors='2') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_always_call_daburi(self): self._make_table() self.player.round_step = 0 tiles = self._string_to_136_array(sou='234567', pin='234567', man='4') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(man='5')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_dont_call_karaten_tanki_riichi(self): self._make_table() tiles = self._string_to_136_array(man='22336688', sou='99', pin='99', honors='2') self.player.init_hand(tiles) for _ in range(0, 3): self.table.add_discarded_tile(1, self._string_to_136_tile(honors='2'), False) self.table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) def test_dont_call_karaten_ryanmen_riichi(self): self._make_table(dora_indicators=[ self._string_to_136_tile(man='1'), self._string_to_136_tile(sou='1'), self._string_to_136_tile(pin='1') ]) tiles = self._string_to_136_array(man='222', sou='22278', pin='22789') self.player.init_hand(tiles) for _ in range(0, 4): self.table.add_discarded_tile(1, self._string_to_136_tile(sou='6'), False) self.table.add_discarded_tile(1, self._string_to_136_tile(sou='9'), False) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False) def test_call_riichi_penchan_with_suji(self): self._make_table(dora_indicators=[ self._string_to_136_tile(pin='1'), ]) tiles = self._string_to_136_array(sou='11223', pin='234567', man='66') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(sou='6')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_call_riichi_tanki_with_kabe(self): self._make_table(dora_indicators=[ self._string_to_136_tile(pin='1'), ]) for _ in range(0, 3): self.table.add_discarded_tile(1, self._string_to_136_tile(honors='1'), False) for _ in range(0, 4): self.table.add_discarded_tile(1, self._string_to_136_tile(sou='8'), False) tiles = self._string_to_136_array(sou='1119', pin='234567', man='666') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='1')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_call_riichi_chiitoitsu_with_suji(self): self._make_table(dora_indicators=[ self._string_to_136_tile(man='1'), ]) for _ in range(0, 3): self.table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) tiles = self._string_to_136_array(man='22336688', sou='9', pin='99', honors='22') self.player.init_hand(tiles) self.player.add_discarded_tile(Tile(self._string_to_136_tile(sou='6'), True)) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), True) def test_dont_call_riichi_chiitoitsu_bad_wait(self): self._make_table(dora_indicators=[ self._string_to_136_tile(man='1'), ]) for _ in range(0, 3): self.table.add_discarded_tile(1, self._string_to_136_tile(honors='3'), False) tiles = self._string_to_136_array(man='22336688', sou='4', pin='99', honors='22') self.player.init_hand(tiles) self.player.draw_tile(self._string_to_136_tile(honors='3')) self.player.discard_tile() self.assertEqual(self.player.can_call_riichi(), False)