Coverage for project/game/ai/statistics_collector.py : 100%

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
1from mahjong.utils import is_honor, plus_dora, simplify
2from utils.decisions_logger import MeldPrint
5class StatisticsCollector:
6 @staticmethod
7 def collect_stat_for_enemy_riichi_hand_cost(tile_136, enemy, main_player):
8 tile_34 = tile_136 // 4
10 riichi_discard = [x for x in enemy.discards if x.riichi_discard]
11 if riichi_discard:
12 assert len(riichi_discard) == 1
13 riichi_discard = riichi_discard[0]
14 else:
15 # FIXME: it happens when user called riichi and we are trying to decide to we need to open hand on
16 # riichi tile or not. We need to process this situation correctly.
17 riichi_discard = enemy.discards[-1]
19 riichi_called_on_step = enemy.discards.index(riichi_discard) + 1
21 total_dora_in_game = len(enemy.table.dora_indicators) * 4 + (3 * int(enemy.table.has_aka_dora))
22 visible_tiles = enemy.table.revealed_tiles_136 + main_player.closed_hand
23 visible_dora_tiles = sum(
24 [plus_dora(x, enemy.table.dora_indicators, add_aka_dora=enemy.table.has_aka_dora) for x in visible_tiles]
25 )
26 live_dora_tiles = total_dora_in_game - visible_dora_tiles
27 assert live_dora_tiles >= 0, "Live dora tiles can't be less than 0"
29 number_of_kan_in_enemy_hand = 0
30 number_of_dora_in_enemy_kan_sets = 0
31 number_of_yakuhai_enemy_kan_sets = 0
32 for meld in enemy.melds:
33 # if he is in riichi he can only have closed kan
34 assert meld.type == MeldPrint.KAN and not meld.opened
36 number_of_kan_in_enemy_hand += 1
38 for tile in meld.tiles:
39 number_of_dora_in_enemy_kan_sets += plus_dora(
40 tile, enemy.table.dora_indicators, add_aka_dora=enemy.table.has_aka_dora
41 )
43 tile_meld_34 = meld.tiles[0] // 4
44 if tile_meld_34 in enemy.valued_honors:
45 number_of_yakuhai_enemy_kan_sets += 1
47 number_of_other_player_kan_sets = 0
48 for other_player in enemy.table.players:
49 if other_player.seat == enemy.seat:
50 continue
52 for meld in other_player.melds:
53 if meld.type == MeldPrint.KAN or meld.type == MeldPrint.SHOUMINKAN:
54 number_of_other_player_kan_sets += 1
56 tile_category = ""
57 # additional danger for tiles that could be used for tanyao
58 if not is_honor(tile_34):
59 # +1 here to make it more readable
60 simplified_tile = simplify(tile_34) + 1
62 if simplified_tile in [4, 5, 6]:
63 tile_category = "middle"
65 if simplified_tile in [2, 3, 7, 8]:
66 tile_category = "edge"
68 if simplified_tile in [1, 9]:
69 tile_category = "terminal"
70 else:
71 tile_category = "honor"
72 if tile_34 in enemy.valued_honors:
73 tile_category = "valuable_honor"
75 return {
76 "is_dealer": enemy.is_dealer and 1 or 0,
77 "riichi_called_on_step": riichi_called_on_step,
78 "current_enemy_step": len(enemy.discards),
79 "wind_number": main_player.table.round_wind_number,
80 "scores": enemy.scores,
81 "is_tsumogiri_riichi": riichi_discard.is_tsumogiri and 1 or 0,
82 "is_oikake_riichi": enemy.is_oikake_riichi and 1 or 0,
83 "is_oikake_riichi_against_dealer_riichi_threat": enemy.is_oikake_riichi_against_dealer_riichi_threat
84 and 1
85 or 0,
86 "is_riichi_against_open_hand_threat": enemy.is_riichi_against_open_hand_threat and 1 or 0,
87 "number_of_kan_in_enemy_hand": number_of_kan_in_enemy_hand,
88 "number_of_dora_in_enemy_kan_sets": number_of_dora_in_enemy_kan_sets,
89 "number_of_yakuhai_enemy_kan_sets": number_of_yakuhai_enemy_kan_sets,
90 "number_of_other_player_kan_sets": number_of_other_player_kan_sets,
91 "live_dora_tiles": live_dora_tiles,
92 "tile_plus_dora": plus_dora(tile_136, enemy.table.dora_indicators, add_aka_dora=enemy.table.has_aka_dora),
93 "tile_category": tile_category,
94 "discards_before_riichi_34": ";".join([str(x.value // 4) for x in enemy.discards[:riichi_called_on_step]]),
95 }