Hide keyboard shortcuts

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.strategies.honitsu import HonitsuStrategy 

2from game.ai.strategies.main import BaseStrategy 

3from mahjong.tile import TilesConverter 

4from mahjong.utils import count_tiles_by_suits, is_honor, is_man, is_pin, is_sou, is_tile_strictly_isolated, plus_dora 

5 

6 

7class ChinitsuStrategy(BaseStrategy): 

8 min_shanten = 4 

9 

10 chosen_suit = None 

11 

12 dora_count_suitable = 0 

13 dora_count_not_suitable = 0 

14 

15 def get_open_hand_han(self): 

16 return 5 

17 

18 def should_activate_strategy(self, tiles_136, meld_tile=None): 

19 """ 

20 We can go for chinitsu strategy if we have prevalence of one suit 

21 """ 

22 

23 result = super(ChinitsuStrategy, self).should_activate_strategy(tiles_136) 

24 if not result: 

25 return False 

26 

27 # when making decisions about chinitsu, we should consider 

28 # the state of our own hand, 

29 tiles_34 = TilesConverter.to_34_array(self.player.tiles) 

30 suits = count_tiles_by_suits(tiles_34) 

31 

32 suits = [x for x in suits if x["name"] != "honor"] 

33 suits = sorted(suits, key=lambda x: x["count"], reverse=True) 

34 suit = suits[0] 

35 

36 count_of_shuntsu_other_suits = 0 

37 count_of_koutsu_other_suits = 0 

38 

39 count_of_shuntsu_other_suits += HonitsuStrategy._count_of_shuntsu(tiles_34, suits[1]["function"]) 

40 count_of_shuntsu_other_suits += HonitsuStrategy._count_of_shuntsu(tiles_34, suits[2]["function"]) 

41 

42 count_of_koutsu_other_suits += HonitsuStrategy._count_of_koutsu(tiles_34, suits[1]["function"]) 

43 count_of_koutsu_other_suits += HonitsuStrategy._count_of_koutsu(tiles_34, suits[2]["function"]) 

44 

45 # we need to have at least 9 tiles of one suit to fo for chinitsu 

46 if suit["count"] < 9: 

47 return False 

48 

49 # here we only check doras in different suits, we will deal 

50 # with honors later 

51 self._initialize_chinitsu_dora_count(tiles_136, suit) 

52 

53 # 3 non-isolated doras in other suits is too much 

54 # to even try 

55 if self.dora_count_not_suitable >= 3: 

56 return False 

57 

58 if self.dora_count_not_suitable == 2: 

59 # 2 doras in other suits, no doras in our suit 

60 # let's not consider chinitsu 

61 if self.dora_count_suitable == 0: 

62 return False 

63 

64 # we have 2 doras in other suits and we 

65 # are 1 shanten, let's not rush chinitsu 

66 if self.player.ai.shanten == 1: 

67 return False 

68 

69 # too late to get rid of doras in other suits 

70 if self.player.round_step > 8: 

71 return False 

72 

73 # we are almost tempai, chinitsu is slower 

74 if suit["count"] == 9 and self.player.ai.shanten == 1: 

75 return False 

76 

77 # only 10 tiles by 9th turn is too slow, considering alternative 

78 if suit["count"] == 10 and self.player.ai.shanten == 1 and self.player.round_step > 8: 

79 return False 

80 

81 # only 11 tiles or less by 12th turn is too slow, considering alternative 

82 if suit["count"] <= 11 and self.player.round_step > 11: 

83 return False 

84 

85 # if we have a pon of honors, let's not go for chinitsu 

86 honor_pons = len([x for x in range(0, 34) if is_honor(x) and tiles_34[x] >= 3]) 

87 if honor_pons >= 1: 

88 return False 

89 

90 # if we have a valued pair, let's not go for chinitsu 

91 valued_pairs = len([x for x in self.player.valued_honors if tiles_34[x] == 2]) 

92 if valued_pairs >= 1: 

93 return False 

94 

95 # if we have a pair of honor doras, let's not go for chinitsu 

96 honor_doras_pairs = len( 

97 [ 

98 x 

99 for x in range(0, 34) 

100 if is_honor(x) and tiles_34[x] == 2 and plus_dora(x * 4, self.player.table.dora_indicators) 

101 ] 

102 ) 

103 if honor_doras_pairs >= 1: 

104 return False 

105 

106 # if we have a honor pair, we will only throw them away if it's early in the game 

107 # and if we have lots of tiles in our suit 

108 honor_pairs = len([x for x in range(0, 34) if is_honor(x) and tiles_34[x] == 2]) 

109 if honor_pairs >= 2: 

110 return False 

111 if honor_pairs == 1: 

112 if suit["count"] < 11: 

113 return False 

114 if self.player.round_step > 8: 

115 return False 

116 

117 # if we have a complete set in other suits, we can only throw it away if it's early in the game 

118 if count_of_shuntsu_other_suits + count_of_koutsu_other_suits >= 1: 

119 # too late to throw away chi after 8 step 

120 if self.player.round_step > 8: 

121 return False 

122 

123 # already 1 shanten, no need to throw away complete set 

124 if self.player.round_step > 5 and self.player.ai.shanten == 1: 

125 return False 

126 

127 # dora is not isolated and we have a complete set, let's not go for chinitsu 

128 if self.dora_count_not_suitable >= 1: 

129 return False 

130 

131 self.chosen_suit = suit["function"] 

132 

133 return True 

134 

135 def is_tile_suitable(self, tile): 

136 """ 

137 We can use only tiles of chosen suit and honor tiles 

138 :param tile: 136 tiles format 

139 :return: True 

140 """ 

141 tile //= 4 

142 return self.chosen_suit(tile) 

143 

144 def _initialize_chinitsu_dora_count(self, tiles_136, suit): 

145 tiles_34 = TilesConverter.to_34_array(tiles_136) 

146 

147 dora_count_man = 0 

148 dora_count_man_not_isolated = 0 

149 dora_count_pin = 0 

150 dora_count_pin_not_isolated = 0 

151 dora_count_sou = 0 

152 dora_count_sou_not_isolated = 0 

153 

154 for tile_136 in tiles_136: 

155 tile_34 = tile_136 // 4 

156 

157 dora_count = plus_dora( 

158 tile_136, self.player.table.dora_indicators, add_aka_dora=self.player.table.has_aka_dora 

159 ) 

160 

161 if is_man(tile_34): 

162 dora_count_man += dora_count 

163 if not is_tile_strictly_isolated(tiles_34, tile_34): 

164 dora_count_man_not_isolated += dora_count 

165 

166 if is_pin(tile_34): 

167 dora_count_pin += dora_count 

168 if not is_tile_strictly_isolated(tiles_34, tile_34): 

169 dora_count_pin_not_isolated += dora_count 

170 

171 if is_sou(tile_34): 

172 dora_count_sou += dora_count 

173 if not is_tile_strictly_isolated(tiles_34, tile_34): 

174 dora_count_sou_not_isolated += dora_count 

175 

176 if suit["name"] == "pin": 

177 self.dora_count_suitable = dora_count_pin 

178 self.dora_count_not_suitable = dora_count_man_not_isolated + dora_count_sou_not_isolated 

179 elif suit["name"] == "sou": 

180 self.dora_count_suitable = dora_count_sou 

181 self.dora_count_not_suitable = dora_count_man_not_isolated + dora_count_pin_not_isolated 

182 elif suit["name"] == "man": 

183 self.dora_count_suitable = dora_count_man 

184 self.dora_count_not_suitable = dora_count_sou_not_isolated + dora_count_pin_not_isolated