-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpit_board.py
308 lines (242 loc) · 10.3 KB
/
pit_board.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
"""backend of the logic of the game"""
from __future__ import annotations
from typing import List, Iterator, Dict
from data_types import Player, PitData, ImpactData
from link_list import LinkList
from link_list import Node
import uipit
from defs import AI, PLAYER
class PitBoard:
"""
logic of the game, turn, and move stones
"""
now_turn = PLAYER # who turn is it
player_one: Player # player one data
player_two: Player # player two data
pits_list: List[PitData] # list of all the pits, include jackpots
pits_link_list: LinkList[PitData] # link list of the pits
pits_to_node: Dict[PitData, Node[PitData]] # fast way to get the node from pit
last_play: bool # the who play the last turn
def __init__(self, pits: List[uipit.UiPit]) -> None:
"""
init the TurnManager and the pits
:param pits: list of pits first is player one jackpot. last is player two jackpot
"""
self.pits_list = []
self.pits_to_node = {}
self.pits_list.append(PitData.jackpot(0, 0))
self.size_of_pits = len(pits)
half_size_pits = self.size_of_pits//2
# feel the non jackpot pits
for index in range(1, self.size_of_pits - 1):
player_index = int(index > half_size_pits)
self.pits_list.append(PitData(player_index, index))
self.pits_list.append(PitData.jackpot(1, self.size_of_pits - 1))
self.player_one = Player(0, self.pits_list)
self.player_two = Player(1, self.pits_list)
# init the players data
# start of the link list is the player one jackpot
self.pits_link_list = LinkList(self.player_one.jackpot)
players_pits = zip(self.player_one.pits, self.player_two.pits)
self.pits_to_node[self.player_one.jackpot] = self.pits_link_list.start
# store the like list
for index, (player_one_pit, player_two_pit) in enumerate(players_pits):
# update codes
most_right = self.pits_link_list.push_right(player_one_pit)
most_left = self.pits_link_list.push_left(player_two_pit)
# this is the parallel pit
most_left.parallel_node = most_right
most_right.parallel_node = most_left
self.pits_to_node[player_one_pit] = most_right
self.pits_to_node[player_two_pit] = most_left
self.pits_link_list.push_right(self.player_two.jackpot)
self.pits_link_list.make_looped()
self.pits_to_node[self.player_two.jackpot] = self.pits_link_list.most_right
@staticmethod
def end_pit_logic(jackpot: PitData, pits: List[PitData]) -> None:
"""
then end game, points logic
:param jackpot: where to save the stones
:param pits: pits to take their stones
:return: None
"""
for pit in pits:
jackpot.stones += pit.stones
pit.stones = 0
def have_stones(self, player: bool) -> bool:
"""
if the player have any stones in his pits
:param player: which player
:return: if have any stones
"""
player = self.get_player(player)
for pit in player.pits:
if not pit.is_empty():
return True
return False
def have_win(self) -> bool:
"""
check if somebody win
:return: if somebody win
"""
return not self.have_stones(AI) or not self.have_stones(PLAYER)
def set_turn(self) -> Iterator[ImpactData]:
"""
change the turn and check if someone is win
:return: the actions the ui need to do
"""
now_turn = self.now_turn
assert sum([pit.stones for pit in self.pits_list]) == 48, "Stone Missing or adding stones in board"
for instruction in self.pit_buttons_disabled(now_turn):
yield instruction
# if somebody don't have stones
if self.have_win():
print("END GAME")
# todo: fix the player class
self.end_pit_logic(self.player_two.jackpot, self.player_one.pits)
self.end_pit_logic(self.player_one.jackpot, self.player_two.pits)
player_one_stones = self.get_player(PLAYER).jackpot.stones
player_two_stones = self.get_player(AI).jackpot.stones
if player_one_stones != player_two_stones:
new_title = "YOU" if player_one_stones < player_two_stones else "AI"
new_title += " WIN!"
yield ImpactData.change_title(new_title)
else:
new_title = "TIE!"
yield ImpactData.change_title(new_title)
enable_pits = self.disabled_player_pits(now_turn, False)
disable_pits = self.disabled_player_pits(not now_turn, not False)
# update text of the pits
for pit in self.pits_list:
yield ImpactData.update_text(pit)
# disable all the pits
for disable_pit in self.connect_generator(enable_pits, disable_pits):
yield disable_pit
@staticmethod
def connect_generator(*generators) -> Iterator:
"""
get generators and return one generator
:param generators: the generators we wont to mash together
:return: Iterator of all the enter generators
"""
for generator in generators:
for value in generator:
yield value
def pit_buttons_disabled(self, now_turn: bool) -> Iterator[ImpactData]:
"""
enable of disable the buttons by user turn
:param now_turn: which turn
:return: the actions the ui need to do
"""
enables_pits = self.disabled_player_pits(now_turn, False)
disable_pits = self.disabled_player_pits(not now_turn, not False)
return self.connect_generator(enables_pits, disable_pits)
def disabled_player_pits(self, player: bool, disable: bool) -> Iterator[ImpactData]:
"""
disabled or enable player pits buttons
:param player: which player
:param disable: disable or enable
:return: the actions the ui need to do
"""
wonted_player = self.get_player(player)
yield ImpactData.disable_pit(wonted_player.jackpot)
for pit in wonted_player.pits:
if disable or pit.stones == 0:
yield ImpactData.disable_pit(pit)
else:
yield ImpactData.enable_pit(pit)
@staticmethod
def get_next(pit_node: Node[PitData]) -> Node[PitData]:
"""
get the next pit node
:param pit_node: the start
:return: the next pit
"""
return pit_node.right_node
def get_player(self, player: bool) -> Player:
"""
get the player
:param player: which player
:return: the player class
"""
return self.player_one if player else self.player_two
def first_stones_logic(self, start_node: Node[PitData]) \
-> Iterator[ImpactData]:
"""
move first stones until he get to the last stone
:param start_node: which pit the user move
:return: the actions the ui need to do
"""
other_player = self.get_player(self.now_turn)
next_pit_node = self.get_next(start_node)
start_pit = start_node.data
while start_pit.stones > 1:
next_pit = next_pit_node.data
# skip other_player jackpot
if next_pit is not other_player.jackpot:
next_pit.stones += 1
yield ImpactData.update_text(next_pit)
start_pit.stones -= 1
next_pit_node = self.get_next(next_pit_node)
# delete last stone
start_pit.stones -= 1
yield ImpactData.update_text(start_pit)
# when getting to the end
return next_pit_node
def can_take_stones(self, pit: Node[PitData]) -> bool:
"""
if the user can take the parallel pits
:param pit: the pit he check
:return: if can or not
"""
return pit.data in self.get_player(self.now_turn).pits \
and pit.data.is_empty() and pit.parallel_node.data.stones > 0
def last_stone_logic(self, start_pit: Node[PitData], last_node: Node[PitData]) \
-> Iterator[ImpactData]:
"""
make the last stone logic
:param start_pit: which pit the user moved
:param last_node: where the move get, where to make the last stone logic
for example: if the last stone land on empty player pit and so one
:return: the actions the ui need to do
"""
my_player = self.get_player(not self.now_turn)
if last_node.data is self.get_player(self.now_turn).jackpot:
last_node = self.get_next(last_node)
next_pit = last_node
next_turn = not self.now_turn
# if have another turn
if next_pit.data is my_player.jackpot:
next_turn = self.now_turn
yield ImpactData.another_turn(start_pit.data)
if self.can_take_stones(next_pit):
# take parallel pit stones to player jackpot
parallel_pit = last_node.parallel_node.data
yield ImpactData.take_opponent_stones(parallel_pit)
my_player.jackpot.stones += parallel_pit.stones
my_player.jackpot.stones += 1 # take also the stone that land on the empty pit
parallel_pit.stones = 0
yield ImpactData.update_text(parallel_pit)
yield ImpactData.update_text(my_player.jackpot)
else:
next_pit.data.stones += 1
yield ImpactData.update_text(next_pit.data)
self.now_turn = next_turn
def make_move(self, pit_index: int) -> Iterator[ImpactData]:
"""
just move
:param pit_index: which pit as index
:return: the actions the ui need to do
"""
self.last_play = not self.now_turn
moved_pit = self.pits_list[pit_index]
moved_pit_node = self.pits_to_node[moved_pit]
first_stones = self.first_stones_logic(moved_pit_node)
while True:
try:
yield next(first_stones)
except StopIteration as return_next_pit:
# getting the last pit of the turn
for instruction in self.last_stone_logic(moved_pit_node, return_next_pit.value):
yield instruction
break