-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame.py
More file actions
273 lines (224 loc) · 8.85 KB
/
game.py
File metadata and controls
273 lines (224 loc) · 8.85 KB
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
from collections import namedtuple, deque
import random
from .painter import PaintBoard
Pawn = namedtuple("Pawn", "index colour id")
class Player():
def __init__(self, colour, name=None, choose_pawn_delegate=None):
self.colour = colour
self.choose_pawn_delegate = choose_pawn_delegate
self.name = name
if self.name is None and self.choose_pawn_delegate is None:
self.name = "computer"
self.finished = False
# initialize four pawns with
# id (first leter from colour and index (from 1 to 4))
self.pawns = [Pawn(i, colour, colour[0].upper() + str(i))
for i in range(1, 5)]
def __str__(self):
return "{}({})".format(self.name, self.colour)
def choose_pawn(self, pawns):
if len(pawns) == 1:
index = 0
elif len(pawns) > 1:
if self.choose_pawn_delegate is None:
index = random.randint(0, len(pawns) - 1)
else:
index = self.choose_pawn_delegate()
return index
class Board():
# common (shared) squares for all pawns
BOARD_SIZE = 56
# save (private) positions (squares) for each colour
# This is squares just before pawn finished
BOARD_COLOUR_SIZE = 7
COLOUR_ORDER = ['yellow', 'blue', 'red', 'green']
# distance between two neighbour colours
# (The distance from start square of one colour
# to start square of next colour)
COLOUR_DISTANCE = 14
def __init__(self):
#fn1353c
# get dict of start position for every colour
Board.COLOUR_START = {
colour: 1 + index * Board.COLOUR_DISTANCE for
index, colour in enumerate(Board.COLOUR_ORDER)}
# get dict of end position for every colour
Board.COLOUR_END = {
colour: index * Board.COLOUR_DISTANCE
for index, colour in enumerate(Board.COLOUR_ORDER)}
Board.COLOUR_END['yellow'] = Board.BOARD_SIZE
# dict where key is pawn and
# value is two size tuple holds position
# Position is combination of
# common (share) square and coloured (private) square.
self.pawns_possiotion = {}
# painter is used to visually represent
# the board and position of the pawns
self.painter = PaintBoard()
# pool means before start1353
self.board_pool_position = (0, 0)
def set_pawn(self, pawn, position):
'''save position'''
self.pawns_possiotion[pawn] = position
def put_pawn_on_board_pool(self, pawn):
self.set_pawn(pawn, self.board_pool_position)
def is_pawn_on_board_pool(self, pawn):
'''return True of False'''
return self.pawns_possiotion[pawn] == self.board_pool_position
def put_pawn_on_starting_square(self, pawn):
start = Board.COLOUR_START[pawn.colour.lower()]
position = (start, 0)
self.set_pawn(pawn, position)
def can_pawn_move(self, pawn, rolled_value):
'''check if pawn can outside board colour size'''
common_poss, private_poss = self.pawns_possiotion[pawn]
if private_poss + rolled_value > self.BOARD_COLOUR_SIZE:
return False
return True
def move_pawn(self, pawn, rolled_value):
'''change pawn position, check
if pawn reach his color square
'''
common_poss, private_poss = self.pawns_possiotion[pawn]
end = self.COLOUR_END[pawn.colour.lower()]
if private_poss > 0:
# pawn is already reached own final squares
private_poss += rolled_value
elif common_poss <= end and common_poss + rolled_value > end:
# pawn is entering in own squares
private_poss += rolled_value - (end - common_poss)
common_poss = end
else:
# pawn will be still in common square
common_poss += rolled_value
if common_poss > self.BOARD_SIZE:
common_poss = common_poss - self.BOARD_SIZE
position = common_poss, private_poss
self.set_pawn(pawn, position)
def does_pawn_reach_end(self, pawn):
'''if pawn must leave game'''
common_poss, private_poss = self.pawns_possiotion[pawn]
if private_poss == self.BOARD_COLOUR_SIZE:
return True
return False
def get_pawns_on_same_postion(self, pawn):
'''return list of pawns on same position'''
position = self.pawns_possiotion[pawn]
return [curr_pawn for curr_pawn, curr_postion in
self.pawns_possiotion.items()
if position == curr_postion]
def paint_board(self):
'''painter object expect dict of
key - occupied positions and
value - list of pawns on that position
'''
positions = {}
for pawn, position in self.pawns_possiotion.items():
common, private = position
if not private == Board.BOARD_COLOUR_SIZE:
positions.setdefault(position, []).append(pawn)
return self.painter.paint(positions)
class Die():
MIN = 1
MAX = 6
@staticmethod
def throw():
return random.randint(Die.MIN, Die.MAX)
class Game():
'''Knows the rules of the game.
Knows for example what to do when
one pawn reach another
or pawn reach end or
player roll six and so on
'''
def __init__(self):
self.players = deque()
self.standing = []
self.board = Board()
# is game finished
self.finished = False
# last rolled value from die (dice)
self.rolled_value = None
# player who last rolled die
self.curr_player = None
# curr_player's possible pawn to move
self.allowed_pawns = []
# curr_player's chosen pawn to move
self.picked_pawn = None
# chosen index from allowed pawn
self.index = None
# jog pawn if any
self.jog_pawns = []
def add_palyer(self, player):
self.players.append(player)
for pawn in player.pawns:
self.board.put_pawn_on_board_pool(pawn)
def get_available_colours(self):
used = [player.colour for player in self.players]
available = set(self.board.COLOUR_ORDER) - set(used)
return sorted(available)
def _get_next_turn(self):
if not self.rolled_value == Die.MAX:
self.players.rotate(-1)
return self.players[0]
def get_pawn_from_board_pool(self, player):
'''when pawn must start'''
for pawn in player.pawns:
if self.board.is_pawn_on_board_pool(pawn):
return pawn
def get_allowed_pawns_to_move(self, player, rolled_value):
allowed_pawns = []
if rolled_value == Die.MAX:
pawn = self.get_pawn_from_board_pool(player)
if pawn:
allowed_pawns.append(pawn)
for pawn in player.pawns:
if not self.board.is_pawn_on_board_pool(pawn) and\
self.board.can_pawn_move(pawn, rolled_value):
allowed_pawns.append(pawn)
return sorted(allowed_pawns, key=lambda pawn: pawn.index)
def get_board_pic(self):
return self.board.paint_board()
def _jog_foreign_pawn(self, pawn):
pawns = self.board.get_pawns_on_same_postion(pawn)
for p in pawns:
if p.colour != pawn.colour:
self.board.put_pawn_on_board_pool(p)
self.jog_pawns.append(p)
def _make_move(self, player, pawn):
if self.rolled_value == Die.MAX and\
self.board.is_pawn_on_board_pool(pawn):
self.board.put_pawn_on_starting_square(pawn)
self._jog_foreign_pawn(pawn)
return
self.board.move_pawn(pawn, self.rolled_value)
if self.board.does_pawn_reach_end(pawn):
player.pawns.remove(pawn)
if not player.pawns:
self.standing.append(player)
self.players.remove(player)
if len(self.players) == 1:
self.standing.extend(self.players)
self.finished = True
else:
self._jog_foreign_pawn(pawn)
def play_turn(self, ind=None, rolled_val=None):
self.jog_pawns = []
self.curr_player = self._get_next_turn()
if rolled_val is None:
self.rolled_value = Die.throw()
else:
self.rolled_value = rolled_val
self.allowed_pawns = self.get_allowed_pawns_to_move(
self.curr_player, self.rolled_value)
if self.allowed_pawns:
if ind is None:
self.index = self.curr_player.choose_pawn(
self.allowed_pawns)
else:
self.index = ind
self.picked_pawn = self.allowed_pawns[self.index]
self._make_move(self.curr_player, self.picked_pawn)
else:
self.index = -1
self.picked_pawn = None