def test_open_suit_same_shanten(): table = Table() player = table.player player.scores = 25000 table.count_of_remaining_tiles = 100 tiles = string_to_136_array(man="1134556", pin="3", sou="78", honors="777") player.init_hand(tiles) meld = make_meld(MeldPrint.CHI, man="345") player.add_called_meld(meld) strategy = HonitsuStrategy(BaseStrategy.HONITSU, player) assert strategy.should_activate_strategy(player.tiles) is True tile = string_to_136_tile(man="1") meld, _ = player.try_to_call_meld(tile, True) assert meld is not None assert tiles_to_string(meld.tiles) == "111m"
def determine_strategy(self, tiles_136, meld_tile=None): # for already opened hand we don't need to give up on selected strategy if self.player.is_open_hand and self.current_strategy: return False old_strategy = self.current_strategy self.current_strategy = None # order is important, we add strategies with the highest priority first strategies = [] if self.player.table.has_open_tanyao: strategies.append(TanyaoStrategy(BaseStrategy.TANYAO, self.player)) strategies.append(YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player)) strategies.append(HonitsuStrategy(BaseStrategy.HONITSU, self.player)) strategies.append(ChinitsuStrategy(BaseStrategy.CHINITSU, self.player)) strategies.append( FormalTempaiStrategy(BaseStrategy.FORMAL_TEMPAI, self.player)) strategies.append( CommonOpenTempaiStrategy(BaseStrategy.COMMON_OPEN_TEMPAI, self.player)) for strategy in strategies: if strategy.should_activate_strategy(tiles_136, meld_tile=meld_tile): self.current_strategy = strategy break if self.current_strategy and ( not old_strategy or self.current_strategy.type != old_strategy.type): self.player.logger.debug( log.STRATEGY_ACTIVATE, context=self.current_strategy, ) if not self.current_strategy and old_strategy: self.player.logger.debug(log.STRATEGY_DROP, context=old_strategy) return self.current_strategy and True or False
def test_suitable_tiles(): table = Table() player = table.player strategy = HonitsuStrategy(BaseStrategy.HONITSU, player) tiles = string_to_136_array(sou="12355", man="238", honors="23455") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is True tile = string_to_136_tile(man="1") assert strategy.is_tile_suitable(tile) is False tile = string_to_136_tile(pin="1") assert strategy.is_tile_suitable(tile) is False tile = string_to_136_tile(sou="1") assert strategy.is_tile_suitable(tile) is True tile = string_to_136_tile(honors="1") assert strategy.is_tile_suitable(tile) is True
def test_should_activate_strategy(): table = Table() player = table.player strategy = HonitsuStrategy(BaseStrategy.HONITSU, player) table.add_dora_indicator(string_to_136_tile(pin="1")) table.add_dora_indicator(string_to_136_tile(honors="5")) tiles = string_to_136_array(sou="12355", man="12389", honors="123") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # many tiles in one suit and yakuhai pair, but still many useless winds tiles = string_to_136_array(sou="12355", man="23", pin="68", honors="2355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # many tiles in one suit and yakuhai pair and another honor pair, so # now this is honitsu tiles = string_to_136_array(sou="12355", man="238", honors="22355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is True # same conditions, but ready suit with dora in another suit, so no honitsu tiles = string_to_136_array(sou="12355", pin="234", honors="22355") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # same conditions, but we have a pon of yakuhai doras, we shouldn't # force honitsu with this hand tiles = string_to_136_array(sou="12355", pin="238", honors="22666") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # if we have a complete set with dora, we shouldn't go for honitsu tiles = string_to_136_array(sou="11123688", pin="123", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # even if the set may be interpreted as two forms tiles = string_to_136_array(sou="1223688", pin="2334", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # even if the set may be interpreted as two forms v2 tiles = string_to_136_array(sou="1223688", pin="2345", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False # if we have a long form with dora, we shouldn't go for honitsu tiles = string_to_136_array(sou="1223688", pin="2333", honors="55") player.init_hand(tiles) assert strategy.should_activate_strategy(player.tiles) is False
def should_activate_strategy(self, tiles_136, meld_tile=None): """ We can go for chinitsu strategy if we have prevalence of one suit """ result = super(ChinitsuStrategy, self).should_activate_strategy(tiles_136) if not result: return False # when making decisions about chinitsu, we should consider # the state of our own hand, tiles_34 = TilesConverter.to_34_array(self.player.tiles) suits = count_tiles_by_suits(tiles_34) suits = [x for x in suits if x["name"] != "honor"] suits = sorted(suits, key=lambda x: x["count"], reverse=True) suit = suits[0] count_of_shuntsu_other_suits = 0 count_of_koutsu_other_suits = 0 count_of_shuntsu_other_suits += HonitsuStrategy._count_of_shuntsu( tiles_34, suits[1]["function"]) count_of_shuntsu_other_suits += HonitsuStrategy._count_of_shuntsu( tiles_34, suits[2]["function"]) count_of_koutsu_other_suits += HonitsuStrategy._count_of_koutsu( tiles_34, suits[1]["function"]) count_of_koutsu_other_suits += HonitsuStrategy._count_of_koutsu( tiles_34, suits[2]["function"]) # we need to have at least 9 tiles of one suit to fo for chinitsu if suit["count"] < 9: return False # here we only check doras in different suits, we will deal # with honors later self._initialize_chinitsu_dora_count(tiles_136, suit) # 3 non-isolated doras in other suits is too much # to even try if self.dora_count_not_suitable >= 3: return False if self.dora_count_not_suitable == 2: # 2 doras in other suits, no doras in our suit # let's not consider chinitsu if self.dora_count_suitable == 0: return False # we have 2 doras in other suits and we # are 1 shanten, let's not rush chinitsu if self.player.ai.shanten == 1: return False # too late to get rid of doras in other suits if self.player.round_step > 8: return False # we are almost tempai, chinitsu is slower if suit["count"] == 9 and self.player.ai.shanten == 1: return False # only 10 tiles by 9th turn is too slow, considering alternative if suit["count"] == 10 and self.player.ai.shanten == 1 and self.player.round_step > 8: return False # only 11 tiles or less by 12th turn is too slow, considering alternative if suit["count"] <= 11 and self.player.round_step > 11: return False # if we have a pon of honors, let's not go for chinitsu honor_pons = len( [x for x in range(0, 34) if is_honor(x) and tiles_34[x] >= 3]) if honor_pons >= 1: return False # if we have a valued pair, let's not go for chinitsu valued_pairs = len( [x for x in self.player.valued_honors if tiles_34[x] == 2]) if valued_pairs >= 1: return False # if we have a pair of honor doras, let's not go for chinitsu honor_doras_pairs = len([ x for x in range(0, 34) if is_honor(x) and tiles_34[x] == 2 and plus_dora(x * 4, self.player.table.dora_indicators) ]) if honor_doras_pairs >= 1: return False # if we have a honor pair, we will only throw them away if it's early in the game # and if we have lots of tiles in our suit honor_pairs = len( [x for x in range(0, 34) if is_honor(x) and tiles_34[x] == 2]) if honor_pairs >= 2: return False if honor_pairs == 1: if suit["count"] < 11: return False if self.player.round_step > 8: return False # if we have a complete set in other suits, we can only throw it away if it's early in the game if count_of_shuntsu_other_suits + count_of_koutsu_other_suits >= 1: # too late to throw away chi after 8 step if self.player.round_step > 8: return False # already 1 shanten, no need to throw away complete set if self.player.round_step > 5 and self.player.ai.shanten == 1: return False # dora is not isolated and we have a complete set, let's not go for chinitsu if self.dora_count_not_suitable >= 1: return False self.chosen_suit = suit["function"] return True