-
Notifications
You must be signed in to change notification settings - Fork 0
/
board.py
295 lines (262 loc) · 11.7 KB
/
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
import pygame
from pieces import *
from setting import Config, sounds
from tools import Position, OnBoard
import math
from Fen import *
class Board:
def __init__(self):
# 0 -> white , 1 -> Black
self.player = 0
self.historic = []
self.moveIndex = 1
self.font = pygame.font.SysFont("Consolas", 18, bold=True)
self.grid = FEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR")
self.WhiteKing = None
self.BlackKing = None
for pieces in self.grid:
for piece in pieces:
if piece != None:
if piece.color == 0 and piece.code == "k":
self.WhiteKing = piece
elif piece.color == 1 and piece.code == "k":
self.BlackKing = piece
# place pieces on the chess board
# -- place all the pawns
# for x in range(Config.boardSize):
# for y in range(Config.boardSize):
# if y == 1:
# # place black pawns
# self.grid[x][y] = Pawn(Position(x, y), 1)
#
# elif y == 6:
# # place white pawns
# self.grid[x][y] = Pawn(Position(x, y), 0)
# self.WhiteKing = King(Position(4, 7), 0)
# self.BlackKing = King(Position(4, 0), 1)
self.checkWhiteKing = False
self.checkBlackKing = False
# -- black pieces
# self.grid[0][0] = Rook(Position(0, 0), 1)
# self.grid[7][0] = Rook(Position(7, 0), 1)
# self.grid[1][0] = Knight(Position(1, 0), 1)
# self.grid[6][0] = Knight(Position(6, 0), 1)
# self.grid[2][0] = Bishop(Position(2, 0), 1)
# self.grid[5][0] = Bishop(Position(5, 0), 1)
# self.grid[3][0] = Queen(Position(3, 0), 1)
# self.grid[4][0] = self.BlackKing
# -- white pieces
# self.grid[0][7] = Rook(Position(0, 7), 0)
# self.grid[7][7] = Rook(Position(7, 7), 0)
# self.grid[1][7] = Knight(Position(1, 7), 0)
# self.grid[6][7] = Knight(Position(6, 7), 0)
# self.grid[2][7] = Bishop(Position(2, 7), 0)
# self.grid[5][7] = Bishop(Position(5, 7), 0)
# self.grid[3][7] = Queen(Position(3, 7), 0)
# self.grid[4][7] = self.WhiteKing
self.winner = None
self.pieceToPromote = None
self.whitePromotions = [Queen(Position(0, 0), 0), Bishop(Position(0, 1), 0), Knight(Position(0, 2), 0), Rook(Position(0, 3), 0)]
self.blackPromotions = [Rook(Position(0, 7), 1), Knight(Position(0, 6), 1), Bishop(Position(0, 5), 1), Queen(Position(0, 4), 1)]
def Forfeit(self):
# resign
pass
def GetPiece(self, coord):
return self.grid[coord.x][coord.y]
def SetPiece(self, position, piece):
self.grid[position.x][position.y] = piece
def SwitchTurn(self):
# switch between 0 and 1
# (0 + 1) * -1 + 2 = 1
# (1 + 1) * -1 + 2 = 0
self.player = (self.player + 1 ) * -1 + 2
# CHECK IF THE PLAYER LOST OR NOT
self.IsCheckmate()
def RecentMove(self):
return None if not self.historic else self.historic[-1]
def RecentMovePositions(self):
if not self.historic or len(self.historic) <= 1:
return None, None
pos = self.historic[-1][3]
oldPos = self.historic[-1][4]
return pos.GetCopy(), oldPos.GetCopy()
def AllowedMoveList(self, piece, moves, isAI):
allowed_moves = []
for move in moves:
if self.VerifyMove(piece, move.GetCopy(), isAI):
allowed_moves.append(move.GetCopy())
return allowed_moves
def GetAllowedMoves(self, piece, isAI=False):
moves, captures = piece.GetMoves(self)
allowed_moves = self.AllowedMoveList(piece, moves.copy(), isAI)
allowed_captures = self.AllowedMoveList(piece, captures.copy(), isAI)
return allowed_moves, allowed_captures
def Move(self, piece, position):
if position != None:
position = position.GetCopy()
# print(position)
if self.isCastling(piece, position.GetCopy()):
self.CastleKing(piece, position.GetCopy())
elif self.isEnPassant(piece, position.GetCopy()):
self.grid[position.x][piece.position.y] = None
self.MovePiece(piece, position)
self.historic[-1][2] = piece.code + " EP"
else:
self.MovePiece(piece, position)
# check for promotion
if type(piece) == Pawn and (piece.position.y == 0 or piece.position.y == 7):
self.pieceToPromote = piece
else:
self.SwitchTurn()
self.Check()
def MovePiece(self, piece, position):
position = position.GetCopy()
self.grid[piece.position.x][piece.position.y] = None
old_position = piece.position.GetCopy()
piece.updatePosition(position)
self.grid[position.x][position.y] = piece
self.historic.append([self.moveIndex, piece.color, piece.code, old_position, piece.position, piece])
piece.previousMove = self.moveIndex
self.moveIndex += 1
self.checkBlackKing = False
self.checkWhiteKing = False
def VerifyMove(self, piece, move, isAI):
# verify the move by going through all the possible outcomes
# This function will return False if the opponent will reply by capturing the king
position = move.GetCopy()
oldPosition = piece.position.GetCopy()
captureEnPassant = None
# print(f"new: {move}, old: {oldPosition}")
capturedPiece = self.grid[position.x][position.y]
if self.isEnPassant(piece, position):
captureEnPassant = self.grid[position.x][oldPosition.y]
self.grid[position.x][oldPosition.y] = None
self.grid[oldPosition.x][oldPosition.y] = None
self.grid[position.x][position.y] = piece
# print(f"pos: {position}, old: {oldPosition}")
piece.updatePosition(move)
EnemyCaptures = self.GetEnemyCaptures(self.player)
if self.isCastling(piece, oldPosition):
if math.fabs(position.x - oldPosition.x) == 2 and not self.VerifyMove(piece, Position(5, position.y), isAI) \
or math.fabs(position.x - oldPosition.x) == 3 and not self.VerifyMove(piece, Position(3, position.y), isAI) \
or self.IsInCheck(piece):
self.UndoMove(piece, capturedPiece, oldPosition, position)
return False
for pos in EnemyCaptures:
if (self.WhiteKing.position == pos and piece.color == 0) \
or (self.BlackKing.position == pos and piece.color == 1):
self.UndoMove(piece, capturedPiece, oldPosition, position)
if captureEnPassant != None:
self.grid[position.x][oldPosition.y] = captureEnPassant
return False
self.UndoMove(piece, capturedPiece, oldPosition, position)
if captureEnPassant != None:
self.grid[position.x][oldPosition.y] = captureEnPassant
return True
def UndoMove(self, piece, captured, oldPos, pos):
self.grid[oldPos.x][oldPos.y] = piece
self.grid[pos.x][pos.y] = captured
piece.updatePosition(oldPos)
def GetEnemyCaptures(self, player):
captures = []
for pieces in self.grid:
for piece in pieces:
if piece != None and piece.color != player:
moves, piececaptures = piece.GetMoves(self)
captures = captures + piececaptures
return captures
def isCastling(self,king, position):
return type(king) == King and abs(king.position.x - position.x) > 1
def isEnPassant(self, piece, newPos):
if type(piece) != Pawn:
return False
moves = None
if piece.color == 0:
moves = piece.EnPassant(self, -1)
else:
moves = piece.EnPassant(self, 1)
return newPos in moves
def IsInCheck(self, piece):
return type(piece) == King and \
((piece.color == 0 and self.checkWhiteKing) or (piece.color == 1 and self.checkBlackKing))
def CastleKing(self, king, position):
position = position.GetCopy()
# print("castled")
# print(position)
if position.x == 2 or position.x == 6:
if position.x == 2:
rook = self.grid[0][king.position.y]
self.MovePiece(king, position)
self.grid[0][rook.position.y] = None
rook.position.x = 3
# print("black castled")
else:
rook = self.grid[7][king.position.y]
self.MovePiece(king, position)
self.grid[7][rook.position.y] = None
rook.position.x = 5
# print("white castled")
rook.previousMove = self.moveIndex - 1
self.grid[rook.position.x][rook.position.y] = rook
self.historic[-1][2] = king.code + " C"
sounds.castle_sound.play()
def PromotePawn(self, pawn, choice):
if choice == 0:
self.grid[pawn.position.x][pawn.position.y] = Queen(pawn.position.GetCopy(), pawn.color)
elif choice == 1:
self.grid[pawn.position.x][pawn.position.y] = Bishop(pawn.position.GetCopy(), pawn.color)
elif choice == 2:
self.grid[pawn.position.x][pawn.position.y] = Knight(pawn.position.GetCopy(), pawn.color)
elif choice == 3:
self.grid[pawn.position.x][pawn.position.y] = Rook(pawn.position.GetCopy(), pawn.color)
self.SwitchTurn()
self.Check()
self.pieceToPromote = None
def MoveSimulation(self, piece, next_pos):
if self.grid[next_pos.x][next_pos.y] == None:
self.grid[piece.position.x][piece.position.y] = None
piece.position = next_pos.GetCopy()
self.grid[next_pos.x][next_pos.y] = piece
return None
else:
prev_piece = self.grid[next_pos.x][next_pos.y]
self.grid[piece.position.x][piece.position.y] = None
piece.position = next_pos.GetCopy()
self.grid[next_pos.x][next_pos.y] = piece
return prev_piece
def Check(self):
if self.player == 0:
king = self.WhiteKing
else:
king = self.BlackKing
for pieces in self.grid:
for piece in pieces:
if piece != None and piece.color != self.player:
moves, captures = self.GetAllowedMoves(piece)
if king.position in captures:
if self.player == 1:
self.checkBlackKing = True
return
else:
self.checkWhiteKing = True
return
def IsCheckmate(self):
for pieces in self.grid:
for piece in pieces:
if piece != None and piece.color == self.player:
moves, captures = self.GetAllowedMoves(piece)
# if there's any legal move left
# then it's not checkmate
if moves or captures:
return False
self.Check()
if self.checkWhiteKing:
# black won
self.winner = 1
elif self.checkBlackKing:
# white won
self.winner = 0
else:
# it's a draw
self.winner = -1
return True