コード例 #1
0
    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')
コード例 #2
0
    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')
コード例 #3
0
def test_tile_danger_early_discard_early():
    enemy_seat = 1
    table = Table()
    table.has_aka_dora = True
    player = table.player

    tiles = string_to_136_array(man="11345", pin="11289", honors="5", sou="19")
    tile = string_to_136_tile(man="9")
    player.init_hand(tiles)
    player.draw_tile(tile)

    table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="2"), False)
    table.add_discarded_tile(enemy_seat, string_to_136_tile(sou="5"), False)
    table.add_discarded_tile(enemy_seat, string_to_136_tile(pin="7"), True)
    table.add_discarded_tile(enemy_seat, string_to_136_tile(pin="1"), True)

    enemy_called_riichi_helper(table, enemy_seat, string_to_136_tile(pin="4"))

    # too early to judge about early discards
    _assert_discard_not_equal(player,
                              enemy_seat,
                              TileDanger.BONUS_EARLY_28,
                              sou="1")
    _assert_discard_not_equal(player,
                              enemy_seat,
                              TileDanger.BONUS_EARLY_5,
                              sou="9")
    _assert_discard_not_equal(player,
                              enemy_seat,
                              TileDanger.BONUS_EARLY_37,
                              pin="8")
    _assert_discard_not_equal(player,
                              enemy_seat,
                              TileDanger.BONUS_EARLY_37,
                              pin="9")
コード例 #4
0
def _create_table(enemy_seat, discards, riichi_tile):
    table = Table()
    table.has_aka_dora = True
    for discard in discards:
        table.add_discarded_tile(0, discard, False)
    enemy_called_riichi_helper(table, enemy_seat, riichi_tile)
    return table
コード例 #5
0
    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')
コード例 #6
0
    def test_defence_against_honitsu_second_case(self):
        table = Table()

        tiles = self._string_to_136_array(sou='4', pin='2223456', man='678', honors='66')
        table.player.init_hand(tiles)

        table.add_called_meld(1, self._make_meld(Meld.CHI, sou='789'))
        table.add_called_meld(1, self._make_meld(Meld.PON, honors='444'))
        table.add_called_meld(1, self._make_meld(Meld.PON, 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')
コード例 #7
0
def test_open_hand_and_once_discarded_tile():
    """
    If we have valuable pair in the hand, this tile was discarded once and we have 1+ shanten
    let's open on this valuable pair
    """
    table = Table()
    strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, table.player)

    tiles = string_to_136_array(sou="678", pin="14689", man="456", honors="77")
    table.player.init_hand(tiles)

    # we don't activate strategy yet
    assert strategy.should_activate_strategy(table.player.tiles) is False

    # let's skip first yakuhai early in the game
    tile = string_to_136_tile(honors="7")
    meld, _ = table.player.try_to_call_meld(tile, True)
    assert meld is None

    # now one is out
    table.add_discarded_tile(1, tile, False)

    meld, _ = table.player.try_to_call_meld(tile, True)
    assert meld is not None
    assert tiles_to_string(meld.tiles) == "777z"

    # but we don't need to open hand for atodzuke here
    tile = string_to_136_tile(pin="7")
    meld, _ = table.player.try_to_call_meld(tile, True)
    assert meld is None
コード例 #8
0
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
コード例 #9
0
    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)
コード例 #10
0
def test_threatening_riichi_player_and_not_early_hand_bonus():
    table = Table()
    enemy_seat = 2
    discards = string_to_136_array(sou="111122")
    for discard in discards:
        table.add_discarded_tile(enemy_seat, discard, False)
    enemy_called_riichi_helper(table, enemy_seat)

    # +1 scale for riichi on 6+ turn
    threatening_player = table.player.ai.defence.get_threatening_players()[0]
    assert threatening_player.enemy.seat == enemy_seat
    assert threatening_player.get_assumed_hand_cost(
        string_to_136_tile(man="2")) == 3900
コード例 #11
0
    def _choose_tanki_with_kabe_helper(self, tiles, kabe_tiles, tile_to_draw, tile_to_discard_str):
        table = Table()
        player = table.player
        player.round_step = 2
        player.dealer_seat = 3

        for tile in kabe_tiles:
            for _ in range(0, 4):
                table.add_discarded_tile(1, tile, False)

        player.init_hand(tiles)
        player.draw_tile(tile_to_draw)
        discarded_tile = player.discard_tile()
        self.assertEqual(self._to_string([discarded_tile]), tile_to_discard_str)
コード例 #12
0
    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, pin='567'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, pin='123'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, 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')
コード例 #13
0
    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')
コード例 #14
0
def test_calculate_our_hand_cost():
    table = Table()
    player = table.player
    enemy_seat = 2
    table.add_called_riichi_step_one(enemy_seat)
    table.add_discarded_tile(enemy_seat, string_to_136_tile(pin="9"), True)

    tiles = string_to_136_array(sou="234678", pin="23478", man="22")
    tile = string_to_136_tile(honors="1")
    player.init_hand(tiles)
    player.draw_tile(tile)

    discard_option = find_discard_option(player, honors="1")
    assert discard_option.danger.weighted_cost == 6128
コード例 #15
0
    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')
コード例 #16
0
    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])
コード例 #17
0
    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')
コード例 #18
0
    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')
コード例 #19
0
def test_dont_activate_strategy_if_we_dont_have_enough_tiles_in_the_wall():
    table = Table()
    strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, table.player)

    table.dora_indicators.append(string_to_136_tile(honors="7"))
    tiles = string_to_136_array(man="59", sou="1235", pin="12789", honors="55")
    table.player.init_hand(tiles)

    assert strategy.should_activate_strategy(table.player.tiles) is True

    table.add_discarded_tile(3, string_to_136_tile(honors="5"), False)
    table.add_discarded_tile(3, string_to_136_tile(honors="5"), False)

    # we can't complete yakuhai, because there is not enough honor tiles
    assert strategy.should_activate_strategy(table.player.tiles) is False
コード例 #20
0
def _choose_tanki_with_kabe_helper(tiles, kabe_tiles, tile_to_draw,
                                   tile_to_discard_str):
    table = Table()
    player = table.player
    player.round_step = 2
    player.dealer_seat = 3

    for tile in kabe_tiles:
        for _ in range(0, 4):
            table.add_discarded_tile(1, tile, False)

    player.init_hand(tiles)
    player.draw_tile(tile_to_draw)
    discarded_tile, _ = player.discard_tile()
    assert tiles_to_string([discarded_tile]) == tile_to_discard_str
コード例 #21
0
    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)
コード例 #22
0
    def _choose_furiten_over_karaten_helper(self, tiles, furiten_tile, karaten_tile, tile_to_draw, tile_to_discard_str):
        table = Table()
        player = table.player
        player.round_step = 2
        player.dealer_seat = 3

        player.init_hand(tiles)

        player.add_discarded_tile(Tile(furiten_tile, True))

        for _ in range(0, 3):
            table.add_discarded_tile(1, karaten_tile, False)

        player.draw_tile(tile_to_draw)
        discarded_tile = player.discard_tile()
        self.assertEqual(self._to_string([discarded_tile]), tile_to_discard_str)
コード例 #23
0
    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')
コード例 #24
0
def _choose_furiten_over_karaten_helper(tiles, furiten_tile, karaten_tile,
                                        tile_to_draw, tile_to_discard_str):
    table = Table()
    player = table.player
    player.round_step = 2
    player.dealer_seat = 3

    player.init_hand(tiles)

    player.add_discarded_tile(Tile(furiten_tile, True))

    for _ in range(0, 3):
        table.add_discarded_tile(1, karaten_tile, False)

    player.draw_tile(tile_to_draw)
    discarded_tile, _ = player.discard_tile()
    assert tiles_to_string([discarded_tile]) == tile_to_discard_str
コード例 #25
0
    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])
コード例 #26
0
def test_discard_tile_based_on_second_level_ukeire_and_cost():
    table = Table()
    player = table.player

    table.add_dora_indicator(string_to_136_tile(man="2"))
    table.add_discarded_tile(1, string_to_136_tile(man="2"), False)

    tiles = string_to_136_array(man="34678", pin="2356", sou="4467")
    tile = string_to_136_tile(sou="8")

    player.init_hand(tiles)
    player.draw_tile(tile)

    discarded_tile, _ = player.discard_tile()
    discard_correct = tiles_to_string(
        [discarded_tile]) == "2p" or tiles_to_string([discarded_tile]) == "3p"
    assert discard_correct is True
コード例 #27
0
    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')
コード例 #28
0
    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])
コード例 #29
0
    def _avoid_furiten_helper(self, tiles, furiten_tile, other_tile,
                              tile_to_draw, tile_to_discard_str):
        table = Table()
        player = table.player
        player.round_step = 2
        player.dealer_seat = 3

        player.init_hand(tiles)

        player.add_discarded_tile(Tile(furiten_tile, True))

        for _ in range(0, 2):
            table.add_discarded_tile(1, other_tile, False)

        player.draw_tile(tile_to_draw)
        discarded_tile = player.discard_tile()
        self.assertEqual(self._to_string([discarded_tile]),
                         tile_to_discard_str)
コード例 #30
0
def test_discard_not_needed_winds():
    table = Table()
    player = table.player
    player.scores = 25000
    table.count_of_remaining_tiles = 100

    tiles = string_to_136_array(man="24", pin="4", sou="12344668", honors="36")
    player.init_hand(tiles)
    player.draw_tile(string_to_136_tile(sou="5"))

    table.add_discarded_tile(1, string_to_136_tile(honors="3"), False)
    table.add_discarded_tile(1, string_to_136_tile(honors="3"), False)
    table.add_discarded_tile(1, string_to_136_tile(honors="3"), False)

    tile_to_discard = player.discard_tile()

    # west was discarded three times, we don't need it
    assert tiles_to_string([tile_to_discard]) == "3z"
コード例 #31
0
    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')
コード例 #32
0
    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)
コード例 #33
0
def test_get_common_tempai_and_0_ukeire_crash():
    """
    Checks that we don't have crash anymore when bot tried to open hand with 0 ukeire
    :return:
    """
    table = Table()
    table.add_discarded_tile(1, string_to_136_tile(sou="1"), True)
    table.add_discarded_tile(1, string_to_136_tile(sou="1"), True)
    table.add_discarded_tile(1, string_to_136_tile(man="1"), True)
    table.add_discarded_tile(1, string_to_136_tile(man="1"), True)

    tiles = string_to_136_array(man="11999", sou="116", pin="99", honors="333")
    table.player.init_hand(tiles)

    tile = string_to_136_tile(pin="9")
    meld, _ = table.player.try_to_call_meld(tile, False)
    # no ukeire, no reason to open hand
    assert meld is None
コード例 #34
0
def test_threatening_riichi_player_and_not_visible_dora():
    table = Table()
    enemy_seat = 2
    table.add_dora_indicator(string_to_136_tile(sou="2"))

    discards = string_to_136_array(sou="3333222")
    for discard in discards:
        table.add_discarded_tile(enemy_seat, discard, False)

    enemy_called_riichi_helper(table, enemy_seat)

    # +1 scale for riichi on 6+ turn
    threatening_player = table.player.ai.defence.get_threatening_players()[0]
    assert threatening_player.enemy.seat == enemy_seat
    assert threatening_player.get_assumed_hand_cost(
        string_to_136_tile(man="2")) == 3900
    # on dora discard, enemy hand will be on average more expensive
    assert threatening_player.get_assumed_hand_cost(
        string_to_136_tile(sou="3")) == 5200
コード例 #35
0
    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])
コード例 #36
0
    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')
コード例 #37
0
    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')
コード例 #38
0
    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')
コード例 #39
0
    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')
コード例 #40
0
    def test_try_to_detect_honitsu_hand(self):
        table = Table()

        table.add_called_meld(1, self._make_meld(Meld.CHI, pin='567'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, pin='123'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, 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(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
        self.assertEqual(EnemyAnalyzer(table.get_player(1)).chosen_suit, is_pin)
コード例 #41
0
    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, pin='567'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, pin='123'))
        table.add_called_meld(1, self._make_meld(Meld.CHI, 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')
コード例 #42
0
    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')
コード例 #43
0
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)
コード例 #44
0
class YakuhaiStrategyTestCase(unittest.TestCase, TestMixin):

    def setUp(self):
        self.table = Table()
        self.player = self.table.player
        self.player.dealer_seat = 3

    def test_should_activate_strategy(self):
        strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)

        tiles = self._string_to_136_array(sou='12355689', man='89', honors='123')
        self.player.init_hand(tiles)
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), False)

        self.table.dora_indicators.append(self._string_to_136_tile(honors='7'))
        tiles = self._string_to_136_array(sou='12355689', man='899', honors='55')
        self.player.init_hand(tiles)
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), True)

        # with chitoitsu-like hand we don't need to go for yakuhai
        tiles = self._string_to_136_array(sou='1235566', man='8899', honors='66')
        self.player.init_hand(tiles)
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), False)

        # don't count tile discarded by other player as our pair
        tiles = self._string_to_136_array(sou='12355689', man='899', honors='25')
        self.player.init_hand(tiles)
        tiles = self._string_to_136_array(sou='12355689', man='899', honors='255')
        self.assertEqual(strategy.should_activate_strategy(tiles), False)

    def test_dont_activate_strategy_if_we_dont_have_enough_tiles_in_the_wall(self):
        strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)

        self.table.dora_indicators.append(self._string_to_136_tile(honors='7'))
        tiles = self._string_to_136_array(man='59', sou='1235', pin='12789', honors='55')
        self.player.init_hand(tiles)

        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), True)

        self.table.add_discarded_tile(3, self._string_to_136_tile(honors='5'), False)
        self.table.add_discarded_tile(3, self._string_to_136_tile(honors='5'), False)

        # we can't complete yakuhai, because there is not enough honor tiles
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), False)

    def test_suitable_tiles(self):
        strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)

        # for yakuhai we can use any tile
        for tile in range(0, 136):
            self.assertEqual(strategy.is_tile_suitable(tile), True)

    def test_force_yakuhai_pair_waiting_for_tempai_hand(self):
        """
        If hand shanten = 1 don't open hand except the situation where is
        we have tempai on yakuhai tile after open set
        """
        self.table.dora_indicators.append(self._string_to_136_tile(man='3'))
        tiles = self._string_to_136_array(sou='123', pin='678', man='34468', honors='66')
        self.player.init_hand(tiles)

        # we will not get tempai on yakuhai pair with this hand, so let's skip this call
        tile = self._string_to_136_tile(man='5')
        meld, _ = self.player.try_to_call_meld(tile, False)
        self.assertEqual(meld, None)

        # but here we will have atodzuke tempai
        tile = self._string_to_136_tile(man='7')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(meld.type, Meld.CHI)
        self.assertEqual(self._to_string(meld.tiles), '678m')

        self.table = Table()
        self.player = self.table.player

        # we can open hand in that case
        self.table.dora_indicators.append(self._string_to_136_tile(sou='5'))
        tiles = self._string_to_136_array(man='44556', sou='366789', honors='77')
        self.player.init_hand(tiles)

        strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), True)

        tile = self._string_to_136_tile(honors='7')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(self._to_string(meld.tiles), '777z')

    def test_tempai_without_yaku(self):
        tiles = self._string_to_136_array(sou='678', pin='12355', man='456', honors='77')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(pin='5')
        self.player.draw_tile(tile)
        meld = self._make_meld(Meld.CHI, sou='678')
        self.player.add_called_meld(meld)

        discard = self.player.discard_tile()
        self.assertNotEqual(self._to_string([discard]), '7z')
        self.assertNotEqual(self._to_string([discard]), '5p')

    def test_wrong_shanten_improvements_detection(self):
        """
        With hand 2345s1p11z bot wanted to open set on 2s,
        so after opened set we will get 25s1p11z
        it is not correct logic, because we ruined our hand
        :return:
        """
        tiles = self._string_to_136_array(sou='2345999', honors='114446')
        self.player.init_hand(tiles)

        meld = self._make_meld(Meld.PON, sou='999')
        self.player.add_called_meld(meld)
        meld = self._make_meld(Meld.PON, honors='444')
        self.player.add_called_meld(meld)

        tile = self._string_to_136_tile(sou='2')
        meld, _ = self.table.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

    def test_open_hand_with_doras_in_the_hand(self):
        """
        If we have valuable pair in the hand, and 2+ dora let's open on this
        valuable pair
        """

        tiles = self._string_to_136_array(man='59', sou='1235', pin='12789', honors='11')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # add doras to the hand
        self.table.dora_indicators.append(self._string_to_136_tile(pin='7'))
        self.table.dora_indicators.append(self._string_to_136_tile(pin='8'))
        self.player.init_hand(tiles)

        # and now we can open hand on the valuable pair
        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

        # but we don't need to open hand for atodzuke here
        tile = self._string_to_136_tile(pin='3')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

    def test_open_hand_with_doras_in_the_hand_and_atodzuke(self):
        """
        If we have valuable pair in the hand, and 2+ dora we can open hand on any tile
        but only if we have other pair in the hand
        """

        tiles = self._string_to_136_array(man='59', sou='1235', pin='12788', honors='11')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(pin='3')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # add doras to the hand
        self.table.dora_indicators.append(self._string_to_136_tile(pin='7'))
        self.player.init_hand(tiles)

        # we have other pair in the hand, so we can open atodzuke here
        tile = self._string_to_136_tile(pin='3')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

    def test_open_hand_on_fifth_round_step(self):
        """
        If we have valuable pair in the hand, 1+ dora and 5+ round step
        let's open on this valuable pair
        """

        tiles = self._string_to_136_array(man='59', sou='1235', pin='12789', honors='11')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # add doras to the hand
        self.table.dora_indicators.append(self._string_to_136_tile(pin='7'))
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # one discard == one round step
        self.player.add_discarded_tile(Tile(0, False))
        self.player.add_discarded_tile(Tile(0, False))
        self.player.add_discarded_tile(Tile(0, False))
        self.player.add_discarded_tile(Tile(0, False))
        self.player.add_discarded_tile(Tile(0, False))
        self.player.add_discarded_tile(Tile(0, False))
        self.player.init_hand(tiles)

        # after 5 round step we can open hand
        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

        # but we don't need to open hand for atodzuke here
        tile = self._string_to_136_tile(pin='3')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

    def test_open_hand_with_two_valuable_pairs(self):
        """
        If we have two valuable pairs in the hand and 1+ dora
        let's open on one of this valuable pairs
        """

        tiles = self._string_to_136_array(man='159', sou='128', pin='789', honors='5566')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='5')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # add doras to the hand
        self.table.dora_indicators.append(self._string_to_136_tile(pin='7'))
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='5')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

        tile = self._string_to_136_tile(honors='6')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

        # but we don't need to open hand for atodzuke here
        tile = self._string_to_136_tile(pin='3')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

    def test_open_hand_and_once_discarded_tile(self):
        """
        If we have valuable pair in the hand, this tile was discarded once and we have 1+ shanten
        let's open on this valuable pair
        """

        strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)

        tiles = self._string_to_136_array(sou='678', pin='14689', man='456', honors='77')
        self.player.init_hand(tiles)

        # we don't activate strategy yet
        self.assertEqual(strategy.should_activate_strategy(self.player.tiles), False)

        # let's skip first yakuhai early in the game
        tile = self._string_to_136_tile(honors='7')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # now one is out
        self.table.add_discarded_tile(1, tile, False)

        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(self._to_string(meld.tiles), '777z')

        # but we don't need to open hand for atodzuke here
        tile = self._string_to_136_tile(pin='7')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

    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_always_open_double_east_wind(self):
        tiles = self._string_to_136_array(man='59', sou='1235', pin='12788', honors='11')
        self.player.init_hand(tiles)

        # player is is not east
        self.player.dealer_seat = 2
        self.assertEqual(self.player.player_wind, WEST)

        self.player.init_hand(tiles)
        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # player is is east
        self.player.dealer_seat = 0
        self.assertEqual(self.player.player_wind, EAST)

        self.player.init_hand(tiles)
        tile = self._string_to_136_tile(honors='1')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

    def test_open_double_south_wind(self):
        tiles = self._string_to_136_array(man='59', sou='1235', pin='12788', honors='22')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='2')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # player is south and round is south
        self.table.round_wind_number = 5
        self.player.dealer_seat = 3
        self.assertEqual(self.player.player_wind, SOUTH)

        self.player.init_hand(tiles)
        tile = self._string_to_136_tile(honors='2')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertEqual(meld, None)

        # add dora in the hand and after that we can open a hand
        self.table.dora_indicators.append(self._string_to_136_tile(pin='6'))

        self.player.init_hand(tiles)
        tile = self._string_to_136_tile(honors='2')
        meld, _ = self.player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)

    def test_keep_yakuhai_in_closed_hand(self):
        tiles = self._string_to_136_array(man='14', sou='15', pin='113347', honors='777')
        self.player.init_hand(tiles)

        tile = self._string_to_136_tile(honors='3')
        self.player.draw_tile(tile)

        discard = self.player.discard_tile()
        self.assertNotEqual(self._to_string([discard]), '7z')

    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_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_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')

    # issue #98
    @unittest.expectedFailure
    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_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_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_tempai_with_open_yakuhai_meld_and_yakuhai_pair_in_the_hand(self):
        """
        there was a bug where bot didn't handle tempai properly
        with opened yakuhai pon and pair in the hand
        56m555p6678s55z + [777z]
        """
        table = Table()
        player = table.player

        tiles = self._string_to_136_array(man='56', pin='555', sou='667', honors='55777')
        player.init_hand(tiles)
        player.add_called_meld(self._make_meld(Meld.PON, honors='777'))
        player.draw_tile(self._string_to_136_tile(sou='8'))

        player.ai.current_strategy = YakuhaiStrategy(BaseStrategy.YAKUHAI, player)

        discarded_tile = player.discard_tile()
        self.assertEqual(self._to_string([discarded_tile]), '6s')
コード例 #45
0
    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])))
コード例 #46
0
    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')