Coverage for project/game/ai/helpers/possible_forms.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 game.ai.helpers.defence import TileDanger
2from mahjong.constants import EAST
3from mahjong.tile import TilesConverter
4from utils.general import revealed_suits_tiles
7class PossibleFormsAnalyzer:
8 POSSIBLE_TANKI = 1
9 POSSIBLE_SYANPON = 2
10 POSSIBLE_PENCHAN = 3
11 POSSIBLE_KANCHAN = 4
12 POSSIBLE_RYANMEN = 5
13 POSSIBLE_RYANMEN_SIDES = 6
15 def __init__(self, player):
16 self.player = player
18 def calculate_possible_forms(self, safe_tiles):
19 possible_forms_34 = [None] * 34
21 closed_hand_34 = TilesConverter.to_34_array(self.player.closed_hand)
23 # first of all let's find suji for suits tiles
24 suits = revealed_suits_tiles(self.player, closed_hand_34)
25 for x in range(0, 3):
26 suit = suits[x]
28 for y in range(0, 9):
29 tile_34_index = y + x * 9
31 # we are only interested in tiles that we can discard
32 if closed_hand_34[tile_34_index] == 0:
33 continue
35 forms_count = self._init_zero_forms_count()
36 possible_forms_34[tile_34_index] = forms_count
38 # that means there are no possible forms for him to wait (we don't consider furiten here,
39 # because we are defending from enemy taking ron)
40 if tile_34_index in safe_tiles:
41 continue
43 # tanki
44 forms_count[self.POSSIBLE_TANKI] = 4 - suit[y]
46 # syanpon
47 if suit[y] == 1:
48 forms_count[self.POSSIBLE_SYANPON] = 3
49 if suit[y] == 2:
50 forms_count[self.POSSIBLE_SYANPON] = 1
51 else:
52 forms_count[self.POSSIBLE_SYANPON] = 0
54 # penchan
55 if y == 2:
56 forms_count[self.POSSIBLE_PENCHAN] = (4 - suit[0]) * (4 - suit[1])
57 elif y == 6:
58 forms_count[self.POSSIBLE_PENCHAN] = (4 - suit[8]) * (4 - suit[7])
60 # kanchan
61 if 1 <= y <= 7:
62 tiles_cnt_left = 4 - suit[y - 1]
63 tiles_cnt_right = 4 - suit[y + 1]
64 forms_count[self.POSSIBLE_KANCHAN] = tiles_cnt_left * tiles_cnt_right
66 # ryanmen
67 if 0 <= y <= 2:
68 if not (tile_34_index + 3) in safe_tiles:
69 forms_right = (4 - suit[y + 1]) * (4 - suit[y + 2])
70 if forms_right != 0:
71 forms_count[self.POSSIBLE_RYANMEN_SIDES] = 1
72 forms_count[self.POSSIBLE_RYANMEN] = (4 - suit[y + 1]) * (4 - suit[y + 2])
73 elif 3 <= y <= 5:
74 if not (tile_34_index - 3) in safe_tiles:
75 forms_left = (4 - suit[y - 1]) * (4 - suit[y - 2])
76 if forms_left != 0:
77 forms_count[self.POSSIBLE_RYANMEN_SIDES] += 1
78 forms_count[self.POSSIBLE_RYANMEN] += forms_left
79 if not (tile_34_index + 3) in safe_tiles:
80 forms_right = (4 - suit[y + 1]) * (4 - suit[y + 2])
81 if forms_right != 0:
82 forms_count[self.POSSIBLE_RYANMEN_SIDES] += 1
83 forms_count[self.POSSIBLE_RYANMEN] += forms_right
84 else:
85 if not (tile_34_index - 3) in safe_tiles:
86 forms_left = (4 - suit[y - 1]) * (4 - suit[y - 2])
87 if forms_left != 0:
88 forms_count[self.POSSIBLE_RYANMEN] = (4 - suit[y - 1]) * (4 - suit[y - 2])
89 forms_count[self.POSSIBLE_RYANMEN_SIDES] = 1
91 for tile_34_index in range(EAST, 34):
92 if closed_hand_34[tile_34_index] == 0:
93 continue
95 forms_count = self._init_zero_forms_count()
96 possible_forms_34[tile_34_index] = forms_count
98 total_tiles = self.player.number_of_revealed_tiles(tile_34_index, closed_hand_34)
100 # tanki
101 forms_count[self.POSSIBLE_TANKI] = 4 - total_tiles
103 # syanpon
104 forms_count[self.POSSIBLE_SYANPON] = 3 - total_tiles if total_tiles < 3 else 0
106 return possible_forms_34
108 @staticmethod
109 def calculate_possible_forms_total(forms_count):
110 total = 0
111 total += forms_count[PossibleFormsAnalyzer.POSSIBLE_TANKI]
112 total += forms_count[PossibleFormsAnalyzer.POSSIBLE_SYANPON]
113 total += forms_count[PossibleFormsAnalyzer.POSSIBLE_PENCHAN]
114 total += forms_count[PossibleFormsAnalyzer.POSSIBLE_KANCHAN]
115 total += forms_count[PossibleFormsAnalyzer.POSSIBLE_RYANMEN]
116 return total
118 @staticmethod
119 def calculate_possible_forms_danger(forms_count):
120 danger = 0
121 danger += forms_count[PossibleFormsAnalyzer.POSSIBLE_TANKI] * TileDanger.FORM_BONUS_TANKI
122 danger += forms_count[PossibleFormsAnalyzer.POSSIBLE_SYANPON] * TileDanger.FORM_BONUS_SYANPON
123 danger += forms_count[PossibleFormsAnalyzer.POSSIBLE_PENCHAN] * TileDanger.FORM_BONUS_PENCHAN
124 danger += forms_count[PossibleFormsAnalyzer.POSSIBLE_KANCHAN] * TileDanger.FORM_BONUS_KANCHAN
125 danger += forms_count[PossibleFormsAnalyzer.POSSIBLE_RYANMEN] * TileDanger.FORM_BONUS_RYANMEN
126 return danger
128 def _init_zero_forms_count(self):
129 forms_count = dict()
130 forms_count[self.POSSIBLE_TANKI] = 0
131 forms_count[self.POSSIBLE_SYANPON] = 0
132 forms_count[self.POSSIBLE_PENCHAN] = 0
133 forms_count[self.POSSIBLE_KANCHAN] = 0
134 forms_count[self.POSSIBLE_RYANMEN] = 0
135 forms_count[self.POSSIBLE_RYANMEN_SIDES] = 0
136 return forms_count