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_find_dealer_tile_to_discard(self):
        dealer = 2
        dora = self._string_to_136_tile(honors='3')
        table = Table()
        table.init_round(0, 0, 0, dora, dealer, [])

        tiles = self._string_to_136_array(sou='2234678', pin='34', man='45789')
        table.player.init_hand(tiles)

        table.add_discarded_tile(1, self._string_to_136_tile(man='4'), False)
        table.add_discarded_tile(1, self._string_to_136_tile(man='5'), False)

        table.add_discarded_tile(2, self._string_to_136_tile(man='8'), False)
        table.add_discarded_tile(2, self._string_to_136_tile(man='9'), False)

        table.add_called_riichi(1)
        table.add_called_riichi(2)

        # for this test we don't need temporary_safe_tiles
        table.get_player(1).temporary_safe_tiles = []
        table.get_player(2).temporary_safe_tiles = []

        result = table.player.discard_tile()
        # second player is a dealer, let's fold against him
        self.assertEqual(self._to_string([result]), '9m')

        tiles = self._string_to_136_array(sou='234567', pin='348', man='234', honors='23')
        table.player.init_hand(tiles)

        result = table.player.discard_tile()
        # there is no safe tiles against dealer, so let's fold against other players
        self.assertEqual(table.player.ai.in_defence, True)
        self.assertEqual(self._to_string([result]), '4m')
    def test_discard_tile_force_tsumogiri(self):
        table = Table()
        table.has_aka_dora = True
        player = table.player

        tiles = self._string_to_136_array(sou='11134567', pin='456', man='45')
        # 6p
        tile = 57

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

        discarded_tile = player.discard_tile()
        self.assertEqual(discarded_tile, tile)

        # add not red five pin
        tiles = self._string_to_136_array(sou='11134567', pin='46', man='45') + [53]
        tile = FIVE_RED_PIN

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

        discarded_tile = player.discard_tile()
        # WE DON'T NEED TO DISCARD RED FIVE
        self.assertNotEqual(discarded_tile, tile)
    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_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_detect_enemy_tempai_and_riichi(self):
        table = Table()

        self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, False)
        self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, False)

        table.add_called_riichi(1)

        self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True)
        self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
    def test_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_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_slide_set_to_keep_dora_in_hand(self):
        table = Table()
        table.dora_indicators = [self._string_to_136_tile(pin='9')]
        player = table.player

        tiles = self._string_to_136_array(sou='123456', pin='23478', man='99')
        tile = self._string_to_136_tile(pin='1')
        player.init_hand(tiles)
        player.draw_tile(tile)

        # 2p is a dora, we had to keep it
        discarded_tile = player.discard_tile()
        self.assertEqual(self._to_string([discarded_tile]), '4p')
    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)
    def test_find_suji_tiles_to_discard_for_one_player(self):
        table = Table()

        tiles = self._string_to_136_array(sou='2345678', pin='12789', man='55')
        table.player.init_hand(tiles)

        table.add_discarded_tile(1, self._string_to_136_tile(man='2'), False)
        table.add_discarded_tile(1, self._string_to_136_tile(man='8'), False)

        table.add_called_riichi(1)

        result = table.player.discard_tile()

        self.assertEqual(self._to_string([result]), '5m')
    def test_keep_aka_dora_in_hand(self):
        table = Table()
        table.dora_indicators = [self._string_to_136_tile(pin='1')]
        table.has_aka_dora = True
        player = table.player

        tiles = self._string_to_136_array(sou='12346', pin='34578', man='99')
        # five sou, we can't get it from string (because from string it is always aka dora)
        tiles += [89]
        player.init_hand(tiles)
        player.draw_tile(FIVE_RED_SOU)

        # we had to keep red five and discard just 5s
        discarded_tile = player.discard_tile()
        self.assertNotEqual(discarded_tile, FIVE_RED_SOU)
    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_riichi_with_bad_wait_against_other_player_riichi(self):
        table = Table()
        table.count_of_remaining_tiles = 60
        table.player.scores = 25000

        tiles = self._string_to_136_array(sou='11223', pin='234678', man='55')
        table.player.init_hand(tiles)
        table.player.draw_tile(self._string_to_136_tile(man='9'))

        table.add_called_riichi(3)

        discard = table.player.discard_tile()
        self.assertEqual(self._to_string([discard]), '9m')
        self.assertEqual(table.player.ai.in_defence, True)
        self.assertEqual(table.player.can_call_riichi(), False)
    def test_find_common_suji_tiles_to_discard_for_multiple_players(self):
        table = Table()

        tiles = self._string_to_136_array(sou='2345678', pin='12789', man='55')
        table.player.init_hand(tiles)

        table.add_discarded_tile(1, self._string_to_136_tile(pin='4'), False)
        table.add_discarded_tile(2, self._string_to_136_tile(pin='4'), False)

        table.add_called_riichi(1)
        table.add_called_riichi(2)

        result = table.player.discard_tile()

        self.assertEqual(self._to_string([result]), '1p')
    def test_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_try_to_discard_less_valuable_tiles_first_in_defence_mode(self):
        table = Table()

        tiles = self._string_to_136_array(sou='234678', pin='6789', man='55', honors='13')
        table.player.init_hand(tiles)

        table.add_discarded_tile(1, self._string_to_136_tile(pin='7'), False)
        table.add_discarded_tile(1, self._string_to_136_tile(sou='2'), False)

        table.add_called_riichi(1)

        result = table.player.discard_tile()

        # discard of 2s will do less damage to our hand shape than 7p discard
        self.assertEqual(table.player.ai.in_defence, True)
        self.assertEqual(self._to_string([result]), '2s')
    def _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)
    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_should_go_for_defence_and_bad_hand(self):
        """
        When we have 13 tiles in hand and someone declared a riichi
        """
        table = Table()

        tiles = self._string_to_136_array(sou='1259', pin='12348', honors='3456')
        table.player.init_hand(tiles)
        table.player.draw_tile(self._string_to_136_tile(man='6'))
        # discard here to reinit shanten number in AI
        table.player.discard_tile()

        self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False)

        table.add_called_riichi(3)

        # our hand is pretty bad, there is no sense to push it against riichi
        self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), True)
    def test_add_safe_tile_after_discard(self):
        table = Table()
        table.add_called_riichi(3)

        table.add_discarded_tile(1, self._string_to_136_tile(man='3'), False)
        table.add_discarded_tile(0, self._string_to_136_tile(man='4'), False)

        self.assertEqual(len(table.get_player(1).discards), 1)
        self.assertEqual(len(table.get_player(2).discards), 0)
        self.assertEqual(len(table.get_player(3).discards), 0)

        self.assertEqual(len(table.get_player(1).safe_tiles), 1)
        self.assertEqual(len(table.get_player(2).safe_tiles), 0)
        self.assertEqual(len(table.get_player(3).safe_tiles), 2)

        # "2" is 3 man
        self.assertEqual(table.get_player(1).safe_tiles, [2])
        self.assertEqual(table.get_player(3).safe_tiles, [2, 3])
    def test_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_choose_best_option_with_melds(self):
        table = Table()
        player = table.player
        table.has_aka_dora = False

        tiles = self._string_to_136_array(sou='245666789', honors='2266')
        player.init_hand(tiles)

        meld = self._make_meld(Meld.PON, sou='666')
        player.add_called_meld(meld)
        meld = self._make_meld(Meld.CHI, sou='789')
        player.add_called_meld(meld)

        player.draw_tile(self._string_to_136_tile(sou='5'))

        discarded_tile = player.discard_tile()
        # we should discard best ukeire option here - 2s
        self.assertEqual(self._to_string([discarded_tile]), '2s')
    def test_priority_of_players_safe_tiles(self):
        table = Table()

        tiles = self._string_to_136_array(man='789', pin='2789', sou='23789', honors='1')
        table.player.init_hand(tiles)
        table.player.draw_tile(self._string_to_136_tile(sou='1'))

        table.add_discarded_tile(1, self._string_to_136_tile(sou='7'), False)
        table.add_discarded_tile(1, self._string_to_136_tile(honors='1'), False)
        table.add_called_riichi(1)
        table.add_discarded_tile(2, self._string_to_136_tile(honors='1'), False)

        result = table.player.discard_tile()

        self.assertEqual(self._to_string([result]), '1z')
    def test_should_go_for_defence_and_good_hand_with_drawn_tile(self):
        """
        When we have 14 tiles in hand and someone declared a riichi
        """
        table = Table()
        table.has_aka_dora = True

        tiles = self._string_to_136_array(sou='2223457899', honors='666')
        table.player.init_hand(tiles)
        table.player.draw_tile(self._string_to_136_tile(man='8'))
        table.player.add_called_meld(self._make_meld(Meld.PON, sou='222'))
        table.player.add_called_meld(self._make_meld(Meld.PON, honors='666'))

        self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(), False)

        table.add_called_riichi(3)

        result = table.player.discard_tile()
        self.assertEqual(self._to_string([result]), '8m')
    def test_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_set_scores_and_uma(self):
        table = Table()
        table.init_round(0, 0, 0, 0, 0, [])
        scores = [230, 110, 55, 405]
        uma = [-17, 3, 48, -34]

        table.set_players_scores(scores, uma)

        self.assertEqual(table.get_player(0).scores, 23000)
        self.assertEqual(table.get_player(0).uma, -17)
        self.assertEqual(table.get_player(1).scores, 11000)
        self.assertEqual(table.get_player(1).uma, 3)
        self.assertEqual(table.get_player(2).scores, 5500)
        self.assertEqual(table.get_player(2).uma, 48)
        self.assertEqual(table.get_player(3).scores, 40500)
        self.assertEqual(table.get_player(3).uma, -34)
    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)
예제 #29
0
def test_tempai_without_yaku():
    table = Table()
    tiles = string_to_136_array(sou="678", pin="12355", man="456", honors="77")
    table.player.init_hand(tiles)

    tile = string_to_136_tile(pin="5")
    table.player.draw_tile(tile)
    meld = make_meld(MeldPrint.CHI, sou="678")
    table.player.add_called_meld(meld)

    discard = table.player.discard_tile()
    assert tiles_to_string([discard]) != "7z"
예제 #30
0
    def test_player_called_meld_and_closed_hand(self):
        table = Table()
        player = table.player

        tiles = self._string_to_136_array(sou='123678', pin='3599', honors='555')
        player.init_hand(tiles)

        self.assertEqual(len(player.closed_hand), 13)

        player.add_called_meld(self._make_meld(Meld.PON, honors='555'))

        self.assertEqual(len(player.closed_hand), 10)
예제 #31
0
def test_discard_not_valuable_honor_first():
    table = Table()
    player = table.player

    tiles = string_to_136_array(sou="123456",
                                pin="123455",
                                man="9",
                                honors="2")
    player.init_hand(tiles)

    discarded_tile = player.discard_tile()
    assert tiles_to_string([discarded_tile]) == "2z"
    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_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])
예제 #34
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')
예제 #35
0
    def test_dont_call_riichi_with_tanki_wait(self):
        table = Table()
        table.count_of_remaining_tiles = 60
        player = table.player
        player.scores = 25000

        tiles = self._string_to_136_array(sou='123456', pin='123456', man='3')
        player.init_hand(tiles)

        player.draw_tile(self._string_to_136_tile(man='4'))
        player.discard_tile()

        self.assertEqual(player.can_call_riichi(), False)

        table = Table()
        table.count_of_remaining_tiles = 60
        player = table.player
        player.scores = 25000

        tiles = self._string_to_136_array(sou='1133557799', pin='113')
        tile = self._string_to_136_tile(pin='6')
        player.init_hand(tiles)
        player.draw_tile(tile)
        player.discard_tile()

        # for chitoitsu it is ok to have a pair wait
        self.assertEqual(player.can_call_riichi(), True)
예제 #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_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')
예제 #38
0
    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
        """
        table = Table()
        player = table.player

        tiles = self._string_to_136_array(sou='123',
                                          pin='678',
                                          man='34468',
                                          honors='66')
        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, _ = 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, _ = 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')

        table = Table()
        player = table.player

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

        tile = self._string_to_136_tile(honors='7')
        meld, _ = player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(self._to_string(meld.tiles), '777z')
예제 #39
0
    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')
예제 #40
0
    def chiitoitsu_tanyao_tempai(self):
        table = Table()
        player = table.player

        tiles = self._string_to_136_array(sou='223344', pin='788', man='4577')
        player.init_hand(tiles)

        player.draw_tile(self._string_to_136_tile(man='4'))

        discard = player.discard_tile()
        discard_correct = self._to_string(
            [discard]) == '7p' or self._to_string([discard]) == '5m'
        self.assertEqual(discard_correct, True)
예제 #41
0
def test_discard_less_valuable_isolated_tile_first():
    table = Table()
    player = table.player
    table.add_dora_indicator(string_to_136_tile(sou="4"))

    tiles = string_to_136_array(sou="2456", pin="129", man="234458")
    player.init_hand(tiles)
    player.draw_tile(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
    assert tiles_to_string([discarded_tile]) == "9p"

    table.dora_indicators.append(string_to_136_tile(pin="8"))
    tiles = string_to_136_array(sou="2456", pin="129", man="234458")
    player.init_hand(tiles)
    player.draw_tile(string_to_136_tile(sou="7"))
    discarded_tile, _ = player.discard_tile()
    # but if 9p is dora
    # let's discard 8m instead
    assert tiles_to_string([discarded_tile]) == "8m"
예제 #42
0
def test_calculate_honor_tiles_value():
    table = Table()
    player = table.player
    player.dealer_seat = 3
    table.has_aka_dora = False

    # valuable honor, wind of the round
    option = DiscardOption(player, EAST * 4, 0, [], 0)
    assert option.valuation == 120

    # valuable honor, wind of the player
    option = DiscardOption(player, SOUTH * 4, 0, [], 0)
    assert option.valuation == 120

    # not valuable wind
    option = DiscardOption(player, WEST * 4, 0, [], 0)
    assert option.valuation == 100

    # not valuable wind
    option = DiscardOption(player, NORTH * 4, 0, [], 0)
    assert option.valuation == 100

    # valuable dragon
    option = DiscardOption(player, HAKU * 4, 0, [], 0)
    assert option.valuation == 120

    # valuable dragon
    option = DiscardOption(player, HATSU * 4, 0, [], 0)
    assert option.valuation == 120

    # valuable dragon
    option = DiscardOption(player, CHUN * 4, 0, [], 0)
    assert option.valuation == 120

    player.dealer_seat = 0

    # double wind
    option = DiscardOption(player, EAST * 4, 0, [], 0)
    assert option.valuation == 140
예제 #43
0
def chiitoitsu_tanyao_tempai():
    table = Table()
    player = table.player

    tiles = string_to_136_array(sou="223344", pin="788", man="4577")
    player.init_hand(tiles)

    player.draw_tile(string_to_136_tile(man="4"))

    discard = player.discard_tile()
    discard_correct = tiles_to_string([discard]) == "7p" or tiles_to_string(
        [discard]) == "5m"
    assert discard_correct is True
예제 #44
0
    def test_should_go_for_defence_and_bad_hand(self):
        """
        When we have 13 tiles in hand and someone declared a riichi
        """
        table = Table()

        tiles = self._string_to_136_array(sou='1259',
                                          pin='12348',
                                          honors='3456')
        table.player.init_hand(tiles)
        table.player.draw_tile(self._string_to_136_tile(man='6'))
        # discard here to reinit shanten number in AI
        table.player.discard_tile()

        self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(),
                         False)

        table.add_called_riichi(3)

        # our hand is pretty bad, there is no sense to push it against riichi
        self.assertEqual(table.player.ai.defence.should_go_to_defence_mode(),
                         True)
예제 #45
0
def test_placement_evaluation():
    table = Table()
    player = table.player

    # very comfortable first
    player.scores = 82000
    for enemy in table.players:
        if enemy != player:
            enemy.scores = 6000

    placement = player.ai.placement._get_placement_evaluation(
        player.ai.placement.get_current_placement())
    assert placement == Placement.VERY_COMFORTABLE_FIRST
예제 #46
0
def test_closed_kan_and_not_necessary_call():
    """
    Bot tried to call closed kan with 568m669p1478999s + 9s hand
    """
    table = Table()
    player = table.player

    tiles = string_to_136_array(man="568", sou="1478999", pin="669")
    player.init_hand(tiles)
    tile = string_to_136_tile(sou="9")
    player.draw_tile(tile)

    assert player.should_call_kan(tile, False) is None
예제 #47
0
    def test_not_open_hand_in_riichi(self):
        table = Table()
        player = table.player

        player.in_riichi = True

        tiles = self._string_to_136_array(sou='12368',
                                          pin='2358',
                                          honors='4455')
        tile = self._string_to_136_tile(honors='5')
        player.init_hand(tiles)
        meld, _ = player.try_to_call_meld(tile, False)
        self.assertEqual(meld, None)
예제 #48
0
    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')
예제 #49
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')
예제 #50
0
def test_threatening_riichi_player_and_not_visible_dora():
    table = Table()
    enemy_seat = 2
    table.add_called_riichi_step_one(enemy_seat)
    table.add_dora_indicator(string_to_136_tile(sou="2"))
    table.has_aka_dora = True

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

    # +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
예제 #51
0
def test_can_call_riichi_and_tempai():
    table = Table()
    player = table.player

    player.in_tempai = False
    player.in_riichi = False
    player.scores = 2000
    player.table.count_of_remaining_tiles = 40

    assert player.formal_riichi_conditions() is False

    player.in_tempai = True

    assert player.formal_riichi_conditions() is True
예제 #52
0
def test_correct_discard_agari_no_yaku():
    table = Table()
    player = table.player

    tiles = string_to_136_array(man="111234677889", sou="1", pin="")
    player.init_hand(tiles)

    meld = make_meld(MeldPrint.CHI, man="789")
    player.add_called_meld(meld)

    tile = string_to_136_tile(sou="1")
    player.draw_tile(tile)
    discard, _ = player.discard_tile()
    assert tiles_to_string([discard]) == "1s"
예제 #53
0
    def test_discard_tile_force_tsumogiri(self):
        table = Table()
        table.has_aka_dora = True
        player = table.player

        tiles = self._string_to_136_array(sou='11134567', pin='456', man='45')
        tile = 57 # 6p

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

        discarded_tile = player.discard_tile()
        self.assertEqual(discarded_tile, tile)

        tiles = self._string_to_136_array(sou='11134567', pin='46', man='45') + [53] # simple five pin
        tile = FIVE_RED_PIN

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

        discarded_tile = player.discard_tile()
        # WE DON'T NEED TO DISCARD RED FIVE
        self.assertNotEqual(discarded_tile, tile)
예제 #54
0
    def test_correct_discard_agari_no_yaku(self):
        table = Table()
        player = table.player

        tiles = self._string_to_136_array(man='111234677889', sou='1', pin='')
        player.init_hand(tiles)

        meld = self._make_meld(Meld.CHI, man='789')
        player.add_called_meld(meld)

        tile = self._string_to_136_tile(sou='1')
        player.draw_tile(tile)
        discard = player.discard_tile()
        self.assertEqual(self._to_string([discard]), '1s')
예제 #55
0
def test_is_threatening_and_riichi():
    table = Table()

    threatening_players = table.player.ai.defence.get_threatening_players()
    assert len(threatening_players) == 0

    enemy_seat = 2
    enemy_called_riichi_helper(table, enemy_seat)

    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_RIICHI["id"]
예제 #56
0
def test_threatening_riichi_player_with_dora_kan():
    table = Table()
    enemy_seat = 2
    table.add_called_riichi_step_one(enemy_seat)

    table.add_dora_indicator(string_to_136_tile(man="2"))

    table.add_called_meld(enemy_seat,
                          make_meld(MeldPrint.KAN, is_open=False, man="3333"))
    # we have to do it manually in test
    # normally tenhou client would do that
    table._add_revealed_tile(string_to_136_tile(man="3"))

    # non dealer
    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")) == 12000

    # dealer
    threatening_player.enemy.dealer_seat = enemy_seat
    assert threatening_player.get_assumed_hand_cost(
        string_to_136_tile(man="2")) == 18000
예제 #57
0
    def test_can_call_riichi_and_remaining_tiles(self):
        table = Table()
        player = table.player

        player.in_tempai = True
        player.in_riichi = False
        player.scores = 2000
        player.table.count_of_remaining_tiles = 3

        self.assertEqual(player.formal_riichi_conditions(), False)

        player.table.count_of_remaining_tiles = 5

        self.assertEqual(player.formal_riichi_conditions(), True)
예제 #58
0
    def test_player_wind(self):
        table = Table()

        player = table.player
        self.assertEqual(player.player_wind, EAST)

        player = Player(table, 0, 1)
        self.assertEqual(player.player_wind, NORTH)

        player = Player(table, 0, 2)
        self.assertEqual(player.player_wind, WEST)

        player = Player(table, 0, 3)
        self.assertEqual(player.player_wind, SOUTH)
예제 #59
0
def test_choose_1_shanten_with_cost_possibility_meld():
    table = Table()
    player = table.player
    table.add_dora_indicator(string_to_136_tile(sou="4"))

    tiles = string_to_136_array(man="557", pin="468", sou="55577", honors="66")
    player.init_hand(tiles)

    meld = make_meld(MeldPrint.PON, sou="555")
    player.add_called_meld(meld)

    tile = string_to_136_tile(sou="7")
    meld, discard_option = player.try_to_call_meld(tile, False)
    assert meld is not None
    assert meld.type == MeldPrint.PON
    assert tiles_to_string(meld.tiles) == "777s"

    assert player.ai.current_strategy is not None
    assert player.ai.current_strategy.type == BaseStrategy.YAKUHAI

    discarded_tile = discard_option.tile_to_discard_136

    assert tiles_to_string([discarded_tile]) == "7m"
예제 #60
0
    def test_chose_right_set_to_open_hand(self):
        """
        Different test cases to open hand and chose correct set to open hand.
        Based on real examples of incorrect opened hands
        """
        table = Table()
        table.has_open_tanyao = True
        player = table.player

        tiles = self._string_to_136_array(man='23455',
                                          pin='3445678',
                                          honors='1')
        tile = self._string_to_136_tile(man='5')
        player.init_hand(tiles)

        meld, _ = player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(meld.type, Meld.PON)
        self.assertEqual(self._to_string(meld.tiles), '555m')

        table = Table()
        player = table.player
        tiles = self._string_to_136_array(man='335666',
                                          pin='22',
                                          sou='345',
                                          honors='55')
        player.init_hand(tiles)

        tile = self._string_to_136_tile(man='4')
        meld, _ = player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(meld.type, Meld.CHI)
        self.assertEqual(self._to_string(meld.tiles), '345m')

        table = Table()
        table.has_open_tanyao = True
        player = table.player
        tiles = self._string_to_136_array(man='23557',
                                          pin='556788',
                                          honors='22')
        player.init_hand(tiles)

        tile = self._string_to_136_tile(pin='5')
        meld, _ = player.try_to_call_meld(tile, True)
        self.assertNotEqual(meld, None)
        self.assertEqual(meld.type, Meld.PON)
        self.assertEqual(self._to_string(meld.tiles), '555p')