-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwinningtest.py
376 lines (328 loc) · 12.7 KB
/
winningtest.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
import random, copy
class Tile(object):
RANKS = (1, 2, 3, 4, 5, 6, 7, 8, 9)
SUITS = ('M', 'P', 'S') # Sou (bamboo), Pin (dots), Man (characters)
def __init__(self, rank, suit):
self.rank = rank
self.suit= suit
def __str__(self):
return str(self.rank)
def __eq__ (self, other):
return (self.rank == other.rank) and (self.suit == other.suit)
def __ne__ (self, other):
return (self.rank != other.rank) and (self.suit == other.suit)
def __lt__ (self, other):
return (self.rank < other.rank)
def __le__ (self, other):
return (self.rank <= other.rank)
def __gt__ (self, other):
return (self.rank > other.rank)
def __ge__ (self, other):
return (self.rank >= other.rank)
def is_Same_Suit(self, other):
return (self.suit == other.suit)
# checks if a given tile is higher than this tile by one and is same suit
# used in run checks
def is_Higher_Tile(self, other):
if self.rank + 1 == other.rank and self.is_Same_Suit(other):
return True
else:
return False
class Hand(object):
def __init__(self):
self.hand = []
def add_Tile(self, tile: Tile):
self.hand.append(tile)
# sorts by rank and suit
def sort_Hand(self):
print('sorting suits')
self.hand = sorted(self.hand, key=lambda tile: tile.suit)
# sort within groups of suits then merge back into main hand
# this way the hand is grouped into suits, which are then ranked in order
sorted_Hand = []
current_Suit = self.hand[0].suit
suit_Group = []
for tile in self.hand:
if current_Suit == tile.suit:
suit_Group.append(tile)
else:
suit_Group = sorted(suit_Group, key=lambda tile: tile.rank)
sorted_Hand.extend(suit_Group)
suit_Group =[]
current_Suit = tile.suit
suit_Group.append(tile)
if len(suit_Group) > 0:
suit_Group = sorted(suit_Group, key=lambda tile: tile.rank)
sorted_Hand.extend(suit_Group)
self.hand = sorted_Hand
'''converts hand into a readable format, somewhat following riichi convention but
accounting for Chinese mahjong variant tiles so honors are not collapsed into one notation (Z in riichi)'''
def hand_To_String(self) -> str:
if len(self.hand) == 0:
print('Nothing in hand to print!')
hand_String = ''
self.sort_Hand()
current_Suit = self.hand[0].suit # only print suit at end of a group of same suit
for tile in self.hand:
if current_Suit != tile.suit:
hand_String = hand_String + current_Suit + ' '
hand_String = hand_String + str(tile)
current_Suit = tile.suit
else:
hand_String = hand_String + str(tile)
if current_Suit == self.hand[-1].suit:
hand_String = hand_String + self.hand[-1].suit
hand_String = 'Hand: ' + hand_String
return hand_String
def check_Seven_Pairs(self) -> bool:
if len(self.hand) % 2 != 0:
return False
half_hand = len(self.hand)/2
num_pairs = int(half_hand)
for i in range(num_pairs):
if self.hand[2*i] != self.hand[2*i+1]:
return False
return True
@staticmethod
def is_Set(demelded_hand):
if demelded_hand[0] == demelded_hand[1] == demelded_hand[2]:
# print('set detected, removing set')
# print(demelded_hand[0])
# print(demelded_hand[1])
# print(demelded_hand[2])
Hand.remove_Set(demelded_hand)
return True
@staticmethod
def remove_Set(demelded_hand):
del demelded_hand[0:3]
@staticmethod
# removes three tiles in a run given the indexes
def remove_Run(demelded_hand, first_tile_loc, second_tile_loc, third_tile_loc):
# print('run removal')
# print(demelded_hand[first_tile_loc])
# print(demelded_hand[second_tile_loc])
# print(demelded_hand[third_tile_loc])
demelded_hand.pop(third_tile_loc)
demelded_hand.pop(second_tile_loc)
demelded_hand.pop(first_tile_loc)
@staticmethod
# detects a run and removes it
def is_Run(demelded_hand):
for i in range(1, len(demelded_hand)):
if demelded_hand[0].is_Higher_Tile(demelded_hand[i]):
for j in range(i + 1, len(demelded_hand)):
if demelded_hand[i].is_Higher_Tile(demelded_hand[j]):
Hand.remove_Run(demelded_hand, 0, i, j)
return True
else:
# print('no valid meld found!')
return False
# yup, recursion time
@staticmethod
def remove_Melds(demelded_hand):
# print('length left: ' + str(len(demelded_hand)))
# if hand has been removed to the point of nothing left, then all
# tiles could form into valid melds and hand is won
if len(demelded_hand) == 0:
# print('melds are cleared!')
return True
else:
# is a set?
# can just check consecutive sets because they're sorted
if Hand.is_Set(demelded_hand):
return Hand.remove_Melds(demelded_hand)
# is a run?
# runs cannot be checked in consective order because a same tile might get in the way
# is_Run also removes a run if detected
elif Hand.is_Run(demelded_hand):
return Hand.remove_Melds(demelded_hand)
''' finds all pairs in hand, removes them and starts removing melds'''
def remove_Pairs(self) -> bool:
checked_tiles = [] # check skips tiles already checked
for i in range(len(self.hand)):
# ends before the last tile
if i == len(self.hand) - 1:
return False # no pairs were found
elif not (self.hand[i] in checked_tiles):
if self.hand[i] == self.hand[i+1]:
depaired_hand = copy.deepcopy(self.hand)
del depaired_hand[i : i + 2]
# start removing melds with the hand without a pair
if Hand.remove_Melds(depaired_hand):
# print('hand all removed!')
return True
else:
checked_tiles.append(self.hand[i])
# checks if hand is a complete winning hand
def is_Win(self) -> bool:
print('Starting to check win')
sus_hand = copy.deepcopy(self)
# ensure hand is sorted properly
sus_hand.sort_Hand()
# check seven_pairs
if self.check_Seven_Pairs():
print('This is seven pairs!')
return True
print('Starting checking normal hand')
# begin the pain
# if the hand is winnable, returns true
return Hand.remove_Pairs(sus_hand)
class Wall (object):
def __init__(self):
self.wall = []
for i in range(4):
for suit in Tile.SUITS:
for rank in Tile.RANKS:
tile = Tile(rank, suit)
self.wall.append(tile)
def shuffle(self):
random.shuffle(self.wall)
def deal(self) -> Tile:
if len(self.wall) == 0:
return None
else:
return self.wall.pop(0)
class Game (object):
def __init__ (self, numHands):
self.wall = Wall()
self.wall.shuffle()
self.hands = []
numTiles_in_Hand = 14
# create and sort hands
for i in range(numHands):
hand = Hand()
for j in range(numTiles_in_Hand):
hand.add_Tile(self.wall.deal())
hand.sort_Hand()
self.hands.append(hand)
def test(hand: str, win_Status: bool, if_Seven_Pairs: bool): #hand: in the format of 1m2m3s4p, etc., num must be even
half_Hand = int(len(hand)/2)
sus_Hand = Hand()
for x in range(half_Hand):
sus_Hand.add_Tile(Tile(int(hand[2*x]), hand[2*x+1]))
print('Judging ' + sus_Hand.hand_To_String())
winning = sus_Hand.is_Win()
print(winning)
if winning and win_Status:
print('The hand is correctly judged to be winning!')
elif winning == True and win_Status == False:
print('The hand is incorrectly judged to be winning!')
elif winning == False and win_Status == True:
print('The hand is incorrectly judged to be losing!')
elif winning == False and win_Status == False:
print('The hand is correctly judged to be losing!')
@staticmethod
# testing
def make_Winning_Hand():
hand = Hand()
hand.add_Tile(Tile(1, 'P'))
hand.add_Tile(Tile(2, 'P'))
hand.add_Tile(Tile(3, 'P'))
hand.add_Tile(Tile(1, 'M'))
hand.add_Tile(Tile(2, 'M'))
hand.add_Tile(Tile(3, 'M'))
hand.add_Tile(Tile(4, 'M'))
hand.add_Tile(Tile(5, 'M'))
hand.add_Tile(Tile(6, 'M'))
hand.add_Tile(Tile(1, 'S'))
hand.add_Tile(Tile(2, 'S'))
hand.add_Tile(Tile(3, 'S'))
hand.add_Tile(Tile(7, 'S'))
hand.add_Tile(Tile(7, 'S'))
print ('1 Test: ' + hand.hand_To_String())
if hand.is_Win():
print('1. Tsumo!')
else:
print('1. Something went wrong, should have been a win')
# hand = []
# hand.append(Tile(1, 'B'))
# hand.append(Tile(2, 'B'))
# hand.append(Tile(3, 'B'))
# hand.append(Tile(2, 'B'))
# hand.append(Tile(3, 'B'))
# hand.append(Tile(4, 'B'))
# hand.append(Tile(5, 'B'))
# hand.append(Tile(6, 'B'))
# hand.append(Tile(7, 'B'))
# hand.append(Tile(7, 'B'))
# hand.append(Tile(8, 'B'))
# hand.append(Tile(9, 'B'))
# hand.append(Tile(9, 'B'))
# hand.append(Tile(9, 'B'))
# if Game.is_Win(hand):
# print('2. Tsumo!')
# else:
# print('2. Something went wrong, should have been a win')
@staticmethod
# testing
def make_Wrong_Hand():
hand = Hand()
hand.add_Tile(Tile(1, 'M'))
hand.add_Tile(Tile(2, 'M'))
hand.add_Tile(Tile(3, 'S'))
hand.add_Tile(Tile(3, 'S'))
hand.add_Tile(Tile(3, 'S'))
hand.add_Tile(Tile(3, 'M'))
hand.add_Tile(Tile(4, 'S'))
hand.add_Tile(Tile(4, 'S'))
hand.add_Tile(Tile(5, 'S'))
hand.add_Tile(Tile(8, 'P'))
hand.add_Tile(Tile(7, 'M'))
hand.add_Tile(Tile(7, 'P'))
hand.add_Tile(Tile(6, 'P'))
hand.add_Tile(Tile(9, 'P'))
print ('2 Test: ' + hand.hand_To_String())
if hand.is_Win():
print('This shouldn\'t be a win!')
else:
print('Correctly not a win')
@staticmethod
def make_Seven_Pairs_Hand():
hand = Hand()
hand.add_Tile(Tile(4, 'M'))
hand.add_Tile(Tile(1, 'S'))
hand.add_Tile(Tile(1, 'M'))
hand.add_Tile(Tile(2, 'P'))
hand.add_Tile(Tile(2, 'P'))
hand.add_Tile(Tile(3, 'P'))
hand.add_Tile(Tile(3, 'P'))
hand.add_Tile(Tile(1, 'M'))
hand.add_Tile(Tile(4, 'M'))
hand.add_Tile(Tile(6, 'S'))
hand.add_Tile(Tile(1, 'S'))
hand.add_Tile(Tile(6, 'M'))
hand.add_Tile(Tile(6, 'M'))
hand.add_Tile(Tile(6, 'S'))
print ('3 Test: ' + hand.hand_To_String())
if hand.is_Win():
print('This is a win!')
else:
print('Correctly not a win')
def play(self):
for hand in self.hands:
print(Game.hand_To_String(hand))
if Game.is_Win(hand):
print('Tsumo!')
class main():
for i in range(1):
game = Game(1)
#game.play()
def test(hand: str, winstatus: bool, ifsevenpairs: bool): #hand: in the format of 1m2m3s4p, etc., num must be even
halfhand = int(len(hand)/2)
for x in range(halfhand):
print('Testing Hand: ' + hand)
sus_hand = Hand()
sus_hand.append(Tile(hand[2*x], hand[2*x+1]))
if Game.is_Win(sus_hand) and winstatus:
print('The hand is correctly judged to be winning!')
elif Game.is_Win(sus_hand) and winstatus == False:
print('The hand is incorrectly judged to be winning!')
elif Game.is_Win(sus_hand) == False and winstatus == True:
print('The hand is incorrectly judged to be losing!')
elif Game.is_Win(sus_hand) == False and winstatus == False:
print('The hand is correctly judged to be losing!')
print('Finish')
Game.make_Winning_Hand()
Game.make_Wrong_Hand()
Game.make_seven_pairs_Hand()
test('1M2M3M1S2S3S1P2P3P4M6M5M7S7S', True, False)