Coverage for project/game/ai/open_hand.py : 98%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import utils.decisions_constants as log
2from game.ai.strategies.chinitsu import ChinitsuStrategy
3from game.ai.strategies.common_open_tempai import CommonOpenTempaiStrategy
4from game.ai.strategies.formal_tempai import FormalTempaiStrategy
5from game.ai.strategies.honitsu import HonitsuStrategy
6from game.ai.strategies.main import BaseStrategy
7from game.ai.strategies.tanyao import TanyaoStrategy
8from game.ai.strategies.yakuhai import YakuhaiStrategy
9from mahjong.shanten import Shanten
10from mahjong.tile import TilesConverter
13class OpenHandHandler:
14 player = None
15 current_strategy = None
16 last_discard_option = None
18 def __init__(self, player):
19 self.player = player
21 def determine_strategy(self, tiles_136, meld_tile=None):
22 # for already opened hand we don't need to give up on selected strategy
23 if self.player.is_open_hand and self.current_strategy:
24 return False
26 old_strategy = self.current_strategy
27 self.current_strategy = None
29 # order is important, we add strategies with the highest priority first
30 strategies = []
32 if self.player.table.has_open_tanyao:
33 strategies.append(TanyaoStrategy(BaseStrategy.TANYAO, self.player))
35 strategies.append(YakuhaiStrategy(BaseStrategy.YAKUHAI, self.player))
36 strategies.append(HonitsuStrategy(BaseStrategy.HONITSU, self.player))
37 strategies.append(ChinitsuStrategy(BaseStrategy.CHINITSU, self.player))
39 strategies.append(FormalTempaiStrategy(BaseStrategy.FORMAL_TEMPAI, self.player))
40 strategies.append(CommonOpenTempaiStrategy(BaseStrategy.COMMON_OPEN_TEMPAI, self.player))
42 for strategy in strategies:
43 if strategy.should_activate_strategy(tiles_136, meld_tile=meld_tile):
44 self.current_strategy = strategy
45 break
47 if self.current_strategy and (not old_strategy or self.current_strategy.type != old_strategy.type):
48 self.player.logger.debug(
49 log.STRATEGY_ACTIVATE,
50 context=self.current_strategy,
51 )
53 if not self.current_strategy and old_strategy:
54 self.player.logger.debug(log.STRATEGY_DROP, context=old_strategy)
56 return self.current_strategy and True or False
58 def try_to_call_meld(self, tile_136, is_kamicha_discard):
59 tiles_136_previous = self.player.tiles[:]
60 closed_hand_136_previous = self.player.closed_hand[:]
61 tiles_136 = tiles_136_previous + [tile_136]
62 self.determine_strategy(tiles_136, meld_tile=tile_136)
64 if not self.current_strategy:
65 self.player.logger.debug(log.MELD_DEBUG, "We don't have active strategy. Abort melding.")
66 return None, None
68 closed_hand_34_previous = TilesConverter.to_34_array(closed_hand_136_previous)
69 previous_shanten, _ = self.player.ai.hand_builder.calculate_shanten_and_decide_hand_structure(
70 closed_hand_34_previous
71 )
73 if previous_shanten == Shanten.AGARI_STATE and not self.current_strategy.can_meld_into_agari():
74 return None, None
76 meld, discard_option = self.current_strategy.try_to_call_meld(tile_136, is_kamicha_discard, tiles_136)
77 if discard_option:
78 self.last_discard_option = discard_option
80 self.player.logger.debug(
81 log.MELD_CALL,
82 "We decided to open hand",
83 context=[
84 f"Hand: {self.player.format_hand_for_print(tile_136)}",
85 f"Meld: {meld.serialize()}",
86 f"Discard after meld: {discard_option.serialize()}",
87 ],
88 )
90 return meld, discard_option