-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboard.py
More file actions
414 lines (352 loc) · 16 KB
/
board.py
File metadata and controls
414 lines (352 loc) · 16 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
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
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
#!/usr/bin/python
from operator import xor
import constants
import board_analyzer
class Board(object):
def __init__(self):
"""
Initializes a new chess board.
"""
#Board is a list of lists
#To access element, write
# self._board[<col#>][<row#>]
#
#See http://i.stack.imgur.com/7KSiN.png
#for picture of board layout
################################################
self._board = [ ['r','p','','','','','*p','*r'],
['n','p','','','','','*p','*n'],
['b','p','','','','','*p','*b'],
['q','p','','','','','*p','*q'],
['k','p','','','','','*p','*k'],
['b','p','','','','','*p','*b'],
['n','p','','','','','*p','*n'],
['r','p','','','','','*p','*r'] ]
def getBoard(self):
"""
Returns a copy of the current board's state.
@return: Copy of board (as list of lists).
"""
return self._board
def pieceOwner(self, specific_move):
"""
Returns the owner of a given piece.
@param specific_move The space you are trying to check (e.g. [1, 2]).
@return: Either constants.WHITE_PLAYER or
constants.BLACK_PLAYER.
"""
if constants.BLACK_PLAYER_SYMBOL in self._board[specific_move[0]][specific_move[1]]:
return constants.BLACK_PLAYER
elif constants.EMPTY_SYMBOL == self._board[specific_move[0]][specific_move[1]]:
return constants.EMPTY_SYMBOL
else:
return constants.WHITE_PLAYER
def _moveConverter(self, move):
"""
Converts move from original string input to a list of integers:
e.g. "a2a3" => [0, 1, 0, 2].
@param move: Player's move (e.g. "a2a3").
@return: List containing indices for board (e.g. [0, 1, 0, 2]).
"""
converter = {"a":0, "b":1, "c":2, "d":3, "e":4, "f":5, "g":6, "h":7}
new_move = []
for string in move:
if string in converter:
new_move.append(converter[string])
else:
new_move.append(int(string)-1)
move = new_move
return move
def isLegalMove(self, currentPlayer, move):
"""
Determines if a move is legal or not.
A move is not legal if any of the following is true:
a) piece is not actually moved (e.g. 'a5a5')
b) move refers to empty space
c) the game piece is not owned by the current player
d) a game piece owned by the same player is at the destination
e) the move is not legal for the game piece
f) not moving into check
@precondition: Method presumes that move is well-formed.
(e.g. row and column values are correct).
@param currentPlayer: "1" for white, "2" for black.
@param move: Four character combination representing move
(e.g. [1, 2, 1, 4]).
@return: True if move is legal, False otherwise.
"""
#Tests for whether a piece is not actually moved
if move[0:2] == move[2:4]:
return False
else:
validity = True
#Tests if there is a piece in the targeted space
if self._board[move[0]][move[1]] == constants.EMPTY_SYMBOL:
return False
else:
validity = True
#Tests if the game piece is not owned by the current player
if currentPlayer != self.pieceOwner([move[0], move[1]]):
return False
else:
validity = True
#Tests if game piece owned by the current player occupies end destination
if currentPlayer == self.pieceOwner([move[2], move[3]]):
return False
else:
validity = True
#Tests whether the move is legal for a specific piece
if constants.PAWN_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForPawn(move, currentPlayer) == True:
validity = True
else:
return False
if constants.ROOK_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForRook(move) == True:
validity = True
else:
return False
if constants.KNIGHT_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForKnight(move) == True:
validity = True
else:
return False
if constants.BISHOP_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForBishop(move) == True:
validity = True
else:
return False
if constants.QUEEN_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForQueen(move) == True:
validity = True
else:
return False
if constants.KING_SYMBOL in self._board[move[0]][move[1]]:
if self._isLegalMoveForKing(move) == True:
validity = True
else:
return False
#Tests whether current player's move will move current player in check
if board_analyzer.isCheck(self._board, currentPlayer, move) == True:
return False
else:
validity = True
return validity
def _isLegalMoveForRook(self, move):
"""
Helper method for determining if move is legal for rook.
@param move: Four-character combination representing player move.
@return: True if move is legal, False otherwise.
"""
horizontal = None
vertical = None
horizontal_blocking = None
vertical_blocking = None
#Allows for horizontal movement
if move[1] == move[3]:
horizontal = True
#Tests whether piece in path of horizontal movement
if move[0] < move[2]:
blocked_spaces = 0
for space in range(move[0] + 1, move[2]):
if constants.EMPTY_SYMBOL != self._board[space][move[1]]:
blocked_spaces += 1
if blocked_spaces == 0:
horizontal_blocking = False
else:
blocked_spaces = 0
for space in range(move[2] + 1, move[0]):
if constants.EMPTY_SYMBOL != self._board[space][move[1]]:
blocked_spaces += 1
if blocked_spaces == 0:
horizontal_blocking = False
#Allows for vertical movement
if move[0] == move[2]:
vertical = True
#Tests whether piece in path of vertical movement
if move[0] < move[2]:
blocked_spaces = 0
for space in range(move[1] + 1, move[3]):
if constants.EMPTY_SYMBOL != self._board[move[0]][space]:
blocked_spaces += 1
if blocked_spaces == 0:
vertical_blocking = False
else:
blocked_spaces = 0
for space in range(move[3] + 1, move[1]):
if constants.EMPTY_SYMBOL != self._board[move[0]][space]:
blocked_spaces += 1
if blocked_spaces == 0:
vertical_blocking = False
if xor((horizontal == True and horizontal_blocking == False), \
(vertical == True and vertical_blocking == False) == True):
return True
else:
return False
def _isLegalMoveForKnight(self, move):
"""
Helper method for determining if move is legal for knight.
@param move: Four-character combination representing player move.
@return: True if move is legal, False otherwise.
"""
if (move[0] + 2 == move[2] and move[1] + 1 == move[3]) or \
(move[0] + 2 == move[2] and move[1] - 1 == move[3]) or \
(move[0] - 2 == move[2] and move[1] + 1 == move[3]) or \
(move[0] - 2 == move[2] and move[1] - 1 == move[3]) or \
(move[0] + 1 == move[2] and move[1] + 2 == move[3]) or \
(move[0] + 1 == move[2] and move[1] - 2 == move[3]) or \
(move[0] - 1 == move[2] and move[1] + 2 == move[3]) or \
(move[0] - 1 == move[2] and move[1] - 2 == move[3]):
return True
else:
return False
def _isLegalMoveForBishop(self, move):
"""
Helper method for determining if move is legal for bishop.
@param move: Four-character combination representing player move.
@return: True if move is legal, False otherwise.
"""
#Allows for diagonal movement
if abs(move[2] - move[0]) != abs(move[3] - move[1]):
return False
#Test whether piece in path of upper-right diagonal movement
if move[2] - move[0] > 0 and move[3] - move[1] > 0:
for space in range(1, abs(move[2] - move[0])):
if constants.EMPTY_SYMBOL != self._board[move[0] + space][move[1] + space]:
return False
#Test whether piece in path of upper-left diagonal movement
if move[2] - move[0] < 0 and move[3] - move[1] > 0:
for space in range(1, abs(move[2] - move[0])):
if constants.EMPTY_SYMBOL != self._board[move[0] - space][move[1] + space]:
return False
#Test whether piece in path of lower-right diagonal movement
if move[2] - move[0] > 0 and move[3] - move[1] < 0:
for space in range(1, abs(move[2] - move[0])):
if constants.EMPTY_SYMBOL != self._board[move[0] + space][move[1] - space]:
return False
#Test whether piece in path of lower-left diagonal movement
if move[2] - move[0] < 0 and move[3] - move[1] < 0:
for space in range(1, abs(move[2] - move[0])):
if constants.EMPTY_SYMBOL != self._board[move[0] - space][move[1] - space]:
return False
return True
def _isLegalMoveForQueen(self, move):
"""
Helper method for determining if move is legal for queen.
Calls isLegalMoveForRook and isLegalMoveForBishop since queen
movements are either rook-like or bishop-like.
@param move: Four-character combination representing player move.
@return: True if move is legal, False otherwise.
"""
if xor(self._isLegalMoveForRook(move), self._isLegalMoveForBishop(move)) == True:
return True
else:
return False
def _isLegalMoveForKing(self, move):
"""
Helper method for determining if move is legal for king.
@param move: Four-character combination representing player move.
@return: True if move is legal, False otherwise.
"""
#Allows for horizontal movement
if (move[0] + 1 == move[2] or move[0] - 1 == move[2]):
return True
else:
validity = False
#Allows for vertical movement
if (move[1] + 1 == move[3] or move[1] - 1 == move[3]):
return True
else:
validity = False
#Allows for diagonal movement
if (move[0] + 1 == move[2] and move[1] + 1 == move[3]) or \
(move[0] - 1 == move[2] and move[1] - 1 == move[3]) or \
(move[0] + 1 == move[2] and move[1] - 1 == move[3]) or \
(move[0] - 1 == move[2] and move[1] + 1 == move[3]):
return True
else:
validity = False
return validity
def _isLegalMoveForPawn(self, move, currentPlayer):
"""
Helper method for determining if move is legal for pawn.
@param move: Four-character combination representing player move.
@param currentPlayer: "1" for white, "2" for black.
@return: True if move is legal, False otherwise.
"""
acceptable = [0, 1, 2, 3, 4, 5, 6, 7]
if currentPlayer == constants.WHITE_PLAYER:
#Pawns can move up one space at a time
if self._board[move[0]][move[1] + 1] == constants.EMPTY_SYMBOL:
if move[1] + 1 == move[3] and move[0] == move[2]:
return True
#Pawns can move up two spaces at the start
if move[1] == 1:
if move[1] + 2 == move[3] and move[0] == move[2]:
if self._board[move[0]][move[1] + 1] == constants.EMPTY_SYMBOL and \
self._board[move[0]][move[1] + 2] == constants.EMPTY_SYMBOL:
return True
#Allows for NE attack
if move[0] + 1 in acceptable and move[1] + 1 in acceptable:
if move[0] + 1 == move[2] and move[1] + 1 == move[3]:
if self.pieceOwner([move[0] + 1, move[1] + 1]) == constants.BLACK_PLAYER:
return True
#Allows for NW attack
if move[0] - 1 in acceptable and move[1] + 1 in acceptable:
if move[0] - 1 == move[2] and move[1] + 1 == move[3]:
if self.pieceOwner([move[0] - 1, move[1] + 1]) == constants.BLACK_PLAYER:
return True
elif currentPlayer == constants.BLACK_PLAYER:
#Pawns can move up one space at a time
if self._board[move[0]][move[1] - 1] == constants.EMPTY_SYMBOL:
if move[1] - 1 == move[3] and move[0] == move[2]:
return True
#Pawns can move up two spaces at the start
if move[1] == 6:
if move[1] - 2 == move[3] and move[0] == move[2]:
if self._board[move[0]][move[1] - 1] == constants.EMPTY_SYMBOL and \
self._board[move[0]][move[1] - 2] == constants.EMPTY_SYMBOL:
return True
#Allows for NE attack
if move[0] + 1 in acceptable and move[1] - 1 in acceptable:
if move[0] + 1 == move[2] and move[1] - 1 == move[3]:
if self.pieceOwner([move[0] + 1, move[1] - 1]) == constants.BLACK_PLAYER:
return True
#Allows for NW attack
if move[0] - 1 in acceptable and move[1] - 1 in acceptable:
if move[0] - 1 == move[2] and move[1] - 1 == move[3]:
if self.pieceOwner([move[0] - 1, move[1] - 1]) == constants.BLACK_PLAYER:
return True
return False
################################################################
def movePiece(self, currentPlayer, move):
"""
Moves chess piece.
@precondition: isLegalMove() must be True.
@param currentPlayer: "1" for white, "2" for black.
@param move: Four-character combination representing player move.
"""
targetPiece = self._board[move[0]][move[1]]
self._board[move[2]][move[3]] = targetPiece
self._board[move[0]][move[1]] = constants.EMPTY_SYMBOL
################################################################
def printBoard(self):
"""
Prints the board.
"""
divider = " +----+----+----+----+----+----+----+----+"
#Print each row
row = 7
while row >= 0:
print divider
#Print each column
col = 0
text = "%s |" % str(row + 1)
while col < 8:
piece = self._board[col][row]
text += " %2s |" % piece
col = col + 1
print text
#Next row
row = row - 1
print divider
print "\n a b c d e f g h"