Skip to content

Commit fbb4764

Browse files
committed
More refactoring inboard.py and pieces.py
1 parent 01d0c4c commit fbb4764

File tree

2 files changed

+92
-70
lines changed

2 files changed

+92
-70
lines changed

chesslib/board.py

Lines changed: 90 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# -*- encoding: utf-8 -*-
2-
from copy import copy, deepcopy
32
from itertools import groupby
43

54
import pieces
@@ -13,7 +12,10 @@ class CheckMate(ChessError): pass
1312
class Draw(ChessError): pass
1413
class NotYourTurn(ChessError): pass
1514

16-
class Board(object):
15+
FEN_STARTING = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
16+
RANK_REGEX = re.compile(r"^[A-Z][1-8]$")
17+
18+
class Board(dict):
1719
'''
1820
Board
1921
@@ -38,7 +40,7 @@ class Board(object):
3840
en_passant = '-'
3941
halfmove_clock = 0
4042
fullmove_number = 1
41-
table = {}
43+
4244
history = []
4345

4446
unicode_pieces = {
@@ -48,41 +50,54 @@ class Board(object):
4850
None: ' '
4951
}
5052

51-
FEN_starting = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
52-
5353
def __init__(self, fen = None):
54-
if fen is None:
55-
self.load(self.FEN_starting)
56-
else:
57-
self.load(fen)
54+
if fen is None: self.load(FEN_STARTING)
55+
else: self.load(fen)
5856

5957
def __getitem__(self, coord):
6058
if isinstance(coord, str):
6159
coord = coord.upper()
62-
regex = re.compile(r"^[A-Z][1-8]$")
63-
if not re.match(regex, coord.upper()): raise KeyError
60+
if not re.match(RANK_REGEX, coord.upper()): raise KeyError
6461
elif isinstance(coord, tuple):
6562
coord = self.letter_notation(coord)
66-
6763
try:
68-
return self.table[coord]
64+
return super(Board, self).__getitem__(coord)
6965
except KeyError:
7066
return None
7167

68+
def check_for_check_after_move(self, p):
69+
# Create a temporary board
70+
p1,p2 = p
71+
tmp = Board(self.export())
72+
tmp._do_move(p1,p2)
73+
return self.is_in_check(self[p1].color)
74+
7275
def move(self, p1, p2):
7376
p1, p2 = p1.upper(), p2.upper()
7477
piece = self[p1]
7578
dest = self[p2]
7679

7780
if self.player_turn != piece.color:
78-
raise NotYourTurn("Not " + piece.get_color() + "'s turn!")
81+
raise NotYourTurn("Not " + piece.color + "'s turn!")
7982

8083
enemy = self.get_enemy(piece.color)
84+
possible_moves = piece.possible_moves(p1)
8185
# 0. Check if p2 is in the possible moves
82-
if p2 not in piece.possible_moves(p1):
83-
raise InvalidMove
86+
if p2 not in possible_moves:
87+
raise InvalidMove
88+
89+
# If enemy has any moves look for check
90+
if self.all_possible_moves(enemy):
91+
filter(self.check_for_check_after_move, map(lambda p2: (p1,p2), possible_moves))
92+
93+
if not possible_moves and self.is_in_check(piece.color):
94+
raise CheckMate
95+
elif not possible_moves:
96+
raise Draw
97+
else:
98+
self._do_move(p1,p2)
99+
self._finish_move(piece, dest, p1,p2)
84100

85-
self._do_move(p1, p2)
86101
'''
87102
# 1. Filter possible moves: remove the ones that will make you in check
88103
# if no possible moves and not in check -- Draw
@@ -111,90 +126,102 @@ def move(self, p1, p2):
111126
'''
112127

113128
def get_enemy(self, color):
114-
if color == "white":
115-
return "black"
116-
else:
117-
return "white"
129+
if color == "white": return "black"
130+
else: return "white"
118131

119132
def _do_move(self, p1, p2):
120-
''' Move a piece without validation '''
133+
'''
134+
Move a piece without validation
135+
'''
121136
piece = self[p1]
122137
dest = self[p2]
123-
p1c = number_notation(p1)
124-
p2c = number_notation(p2)
125138
del self[p1]
126139
self[p2] = piece
127140

128141
def _finish_move(self, piece, dest,p1,p2):
129-
''' Set next player turn, count moves, log moves '''
130-
global player_turn, fullmove_number, history, halfmove_clock
131-
enemy = get_enemy(piece.get_color())
132-
player_turn = pieces.COLORS[enemy]
133-
if piece.get_color() == 'black':
134-
fullmove_number += 1
135-
halfmove_clock +=1
136-
137-
abbr = piece.abbriviation.upper()
142+
'''
143+
Set next player turn, count moves, log moves, etc.
144+
'''
145+
enemy = self.get_enemy(piece.color)
146+
if piece.color == 'black':
147+
self.fullmove_number += 1
148+
self.halfmove_clock +=1
149+
self.player_turn = enemy
150+
abbr = piece.abbriviation
138151
if abbr == 'P':
139152
# Pawn has no letter
140153
abbr = ''
141154
# Pawn resets halfmove_clock
142-
halfmove_clock = 0
155+
self.halfmove_clock = 0
143156
if dest is None:
144157
# No capturing
145158
movetext = abbr + p2.lower()
146159
else:
147160
# Capturing
148161
movetext = abbr + 'x' + p2.lower()
149162
# Capturing resets halfmove_clock
150-
halfmove_clock = 0
163+
self.halfmove_clock = 0
151164

152-
history.append(movetext)
165+
self.history.append(movetext)
153166

154167

155168
def all_possible_moves(self, color):
169+
'''
170+
Return a list of `color`'s possible moves.
171+
Does not check for check.
172+
'''
156173
if(color not in ("black", "white")): raise InvalidColor
157174
result = []
158-
for x in range(0,len(table)):
159-
for y in range(0,len(table[x])):
160-
if (table[x][y] is not None) and table[x][y].get_color() == color:
161-
moves = table[x][y].possible_moves(letter_notation((x,y)))
162-
if moves: result += moves
175+
for coord in self.keys():
176+
if (self[coord] is not None) and self[coord].color == color:
177+
moves = self[coord].possible_moves(coord)
178+
if moves: result += moves
163179
return result
164180

165181
def occupied(self, color):
182+
'''
183+
Return a list of coordinates occupied by `color`
184+
'''
166185
result = []
167186
if(color not in ("black", "white")): raise InvalidColor
168187

169-
for x in range(0,len(table)):
170-
for y in range(0,len(table[x])):
171-
if (table[x][y] is not None) and (table[x][y].color == color):
172-
result.append((x, y))
188+
for coord in self:
189+
if self[coord].color == color: result.append(coord)
173190
return result
174191

192+
def is_king(self, piece):
193+
return isinstance(piece, pieces.King)
194+
195+
175196
def get_king_position(self, color):
176-
for x in range(0,len(table)):
177-
for y in range(0,len(table[x])):
178-
piece = table[x][y]
179-
if (piece is not None) and\
180-
isinstance(piece, pieces.King) and\
181-
(piece.color == pieces.COLORS[color]):
182-
return letter_notation((x,y))
197+
for pos in self.keys():
198+
if self.is_king(self[pos]) and self[pos].color == color:
199+
return pos
183200

184201
def get_king(self, color):
185202
if(color not in ("black", "white")): raise InvalidColor
186-
return get(get_king_position(color))
203+
return self[self.get_king_position(color)]
187204

188205
def is_in_check(self, color):
189206
if(color not in ("black", "white")): raise InvalidColor
190207
king = self.get_king(color)
191208
enemy = self.get_enemy(color)
192-
return king in map(get, all_possible_moves(enemy))
209+
return king in map(self.__getitem__, self.all_possible_moves(enemy))
210+
211+
def letter_notation(self,coord):
212+
if not self.is_in_bounds(coord): raise InvalidCoord
213+
try:
214+
return self.axis_y[coord[1]] + str(self.axis_x[coord[0]])
215+
except IndexError:
216+
raise InvalidCoord
193217

194218
def number_notation(self, coord):
195-
return int(coord[1])-1, axis_y.index(coord[0])
219+
return int(coord[1])-1, self.axis_y.index(coord[0])
196220

197221
def unicode_representation(self):
222+
'''
223+
Print a text-mode chessboard using the unicode chess pieces
224+
'''
198225
for number in self.axis_x[::-1]:
199226
print " " + str(number) + " ",
200227
for letter in self.axis_y:
@@ -212,7 +239,9 @@ def is_in_bounds(self, coord):
212239
else: return True
213240

214241
def load(self, fen):
215-
''' Import state from FEN notation '''
242+
'''
243+
Import state from FEN notation
244+
'''
216245
# Split data
217246
fen = fen.split(' ')
218247
# Expand blanks
@@ -224,8 +253,8 @@ def expand(match): return ' ' * int(match.group(0))
224253
for y, letter in enumerate(row):
225254
if letter == ' ': continue
226255
coord = self.letter_notation((7-x,y))
227-
self.table[coord] = pieces.piece(letter)
228-
self.table[coord].place(self)
256+
self[coord] = pieces.piece(letter)
257+
self[coord].place(self)
229258

230259
if fen[1] == 'w': self.player_turn = 'white'
231260
else: self.player_turn = 'black'
@@ -236,7 +265,9 @@ def expand(match): return ' ' * int(match.group(0))
236265
self.fullmove_number = int(fen[5])
237266

238267
def export(self):
239-
''' Export state to FEN notation '''
268+
'''
269+
Export state to FEN notation
270+
'''
240271
def join(k, g):
241272
if k == ' ': return str(len(g))
242273
else: return "".join(g)
@@ -264,12 +295,3 @@ def replace_spaces(row):
264295
str(self.halfmove_clock),
265296
str(self.fullmove_number)]))
266297
return result
267-
268-
def letter_notation(self,coord):
269-
if not self.is_in_bounds(coord):
270-
raise InvalidCoord
271-
272-
try:
273-
return self.axis_y[coord[1]] + str(self.axis_x[coord[0]])
274-
except IndexError:
275-
raise InvalidCoord

chesslib/pieces.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def possible_moves(self, position, orthogonal, diagonal, distance):
6666
dest = from_[0]+step*x, from_[1]+step*y
6767
if dest not in board.occupied('white') + board.occupied('black'):
6868
legal_moves.append(dest)
69-
elif dest in board.occupied(piece.get_color()):
69+
elif dest in board.occupied(piece.color):
7070
collision = True
7171
else:
7272
legal_moves.append(dest)
@@ -132,7 +132,7 @@ def possible_moves(self,position):
132132

133133
for x,y in deltas:
134134
dest = from_[0]+x, from_[1]+y
135-
if(dest not in board.occupied(piece.get_color())):
135+
if(dest not in board.occupied(piece.color)):
136136
legal_moves.append(dest)
137137

138138
legal_moves = filter(board.is_in_bounds, legal_moves)

0 commit comments

Comments
 (0)