-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgame.py
More file actions
334 lines (292 loc) · 9.26 KB
/
game.py
File metadata and controls
334 lines (292 loc) · 9.26 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
from Board import Board
from Piece import *
import easygui
import datetime
import pymongo
boardimg = pygame.image.load("C:\\Users\\tyrio\\PycharmProjects\\PythonChess\\img\\board.png")
windowicon = pygame.image.load("C:\\Users\\tyrio\\PycharmProjects\\PythonChess\\img\\icon.png")
WHITE = "w"
BLACK = "b"
width = 500
height = 500
checker = width/8
white = (255, 255, 255)
red = pygame.Color.r
origin = (0, 0)
clicked = False
game_ended = False
clickedPiece = 0
lastMoved = 0
turn = WHITE
msg = ""
PGN = ""
n_turn = 1
PGN_result = ""
board = Board()
def main():
pygame.init()
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("PyChess")
pygame.display.set_icon(windowicon)
clock = pygame.time.Clock()
done = False
board.load_board()
store_kings()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONUP:
on_click_board(event)
screen.fill(white)
screen.blit(boardimg, origin)
board.update(screen)
if not game_ended:
if isinstance(clickedPiece, Piece):
for move in clickedPiece.valid_moves(board):
highlight_square(move, screen)
else: # Draw rect
pygame.display.flip()
choice = easygui.buttonbox(msg, 'Restart?', ['Restart', 'Quit'])
if choice == 'Restart':
game_restart()
else:
done = True
pygame.display.flip()
clock.tick(50)
def store_kings():
"""
Saves both King Objects to their respective variables, in order
to quickly locate them in other functions.
"""
global b_king, w_king
kings = board.get_kings()
if kings[0].get_color() == WHITE: # Will always be true in standard games
w_king = kings[0]
b_king = kings[1]
else:
w_king = kings[1]
b_king = kings[0]
def game_restart():
"""
Starts a common game, setting pieces in place and clearing all the configurations used
in last game
"""
global game_ended, lastMoved, clicked, turn, w_king, b_king, PGN, PGN_result
for x in range(8):
for y in range(8):
board.add_blank([x, y])
board.load_board()
store_kings()
game_ended = False
lastMoved = 0
clicked = False
turn = "w"
PGN = ""
PGN_result = ""
def format_pos(pos):
"""
Return the position as letter+number coordinates, rather than two numbers, for PGN purposes
:type pos: list
:param pos: Position to be formated
:return: Position in chess notation
"""
x = chr(ord('a')+pos[0])
return x + str(pos[1]+1)
def on_click_board(event):
"""
Handler for clicks on the board
Takes care of most of the game logic, checking whether the move was valid or not,
then modifying global game variables however needed.
:param event: Click event
:return:
"""
global clicked, clickedPiece, turn, lastMoved, game_ended, msg, PGN, PGN_result
side = width / 8
# Getting the click coordinates out of the event
x = int(event.pos[0] / side)
y = int((event.pos[1] - 500) * -1 / side)
# Logic for in progress games
if not game_ended:
if not clicked: # Selecting a piece
square = board.get_square([x, y])
if square != 0:
if square.get_color() == turn:
clickedPiece = square
clicked = True
else: # Piece was selected, discard or try to move
if [x, y] in clickedPiece.valid_moves(board):
update_PGN(clickedPiece, [x, y])
clickedPiece.move([x, y], board)
if turn == WHITE:
turn = BLACK
else:
turn = WHITE
clear_passants()
lastMoved = clickedPiece
clicked = False
clickedPiece = 0
# Check whether the game ended ith last move or not
if not board.can_team_move(turn):
PGN = PGN[0:len(PGN)-1]+"#"
game_ended = True
lastMoved = 0
if turn == WHITE:
if w_king.is_checked(board):
msg = "Black Wins"
PGN_result = "0-1"
else:
msg = "Draw"
PGN_result = "1/2-1/2"
else:
if turn == BLACK:
if b_king.is_checked(board):
msg = "White Wins"
PGN_result = "1-0"
else:
msg = "Draw"
PGN_result = "1/2-1/2"
store_game() # Store the game on the database
else: # Game has ended, currently asking for restart / exit
pass
def update_PGN(piece, move):
"""
Adds current move to PGN
:type piece: Piece
:param piece: Piece that moved
:param move: Destination move
:return: Returns true when stored
"""
global n_turn, PGN
s2 = ""
s3 = ""
s4 = ""
if turn == WHITE:
s = str(n_turn)+". "
else:
n_turn += 1
s = ""
# Castling
if isinstance(piece, King):
if abs(move[0] - piece.get_pos()[0]) == 2: # Castling
if move[0] == 2:
s2 = "O-O-O"
else:
s2 = "O-O"
clickedPiece.move(move, board)
res = s + s2 + " "
if res == "":
print(PGN)
PGN += res
return True
# Normal movements
if not isinstance(piece, Pawn):
if board.is_ambiguous(piece, move):
s2 = str(piece)+format_pos(piece.get_pos())[0:1]
else:
s2 = str(piece)
if piece.is_taking(board, move):
s3 = "x"
if isinstance(piece, Pawn):
s2 = format_pos(piece.get_pos())[0:1] # Pawns taking need column
clickedPiece.move(move, board) # make the move now to be able to check for checks
# Checks
if piece.get_color() == WHITE:
if b_king.is_checked(board):
s4 = "+"
else:
if w_king.is_checked(board):
s4 = "+"
res = s+s2+s3+format_pos(move)+s4+" "
if res == "":
print(PGN)
PGN += res
return True
def store_game():
"""
Stores the game to a local mongoDB database
:return:
"""
client = pymongo.MongoClient("mongodb://localhost:27017")
db = client["pychess"]
col = db["games"]
now = datetime.datetime.now()
res = {"Event": "Friendly PyChess game",
"Site": "PyChess application",
"Date": now.strftime("%Y.%m.%d"),
"Round": "n/a",
"White": "Player1",
"Black": "Player2",
"Result": PGN_result,
"Game": PGN,
"FEN": get_FEN()}
col.insert_one(res)
def get_FEN():
"""
Generates FEN string of current board
:return: Returns a string with current board's FEN
"""
cont = 0
lines = []
line = ""
passant = "-"
for y in range(7, -1, -1):
for x in range(0, 8):
square = board.get_square([x, y])
if isinstance(square, Pawn):
if square.get_passant():
passant = format_pos(square.get_pos())
if isinstance(square, Piece):
if cont > 0:
line += str(cont)
cont = 0
line += square.get_FEN()
else:
cont += 1
if x == 7:
line += str(cont)
cont = 0
lines.append(line)
line = ""
boardfen = ""
for line in lines:
boardfen += line + "/"
castling = " "
# White castling availability
if w_king.can_castle():
w_square_kingside = board.get_square([7,0])
w_square_queenside = board.get_square([0, 0])
if isinstance(w_square_kingside, Rook):
if w_square_kingside.can_castle():
castling += "K"
if isinstance(w_square_queenside, Rook):
if w_square_queenside.can_castle():
castling += "Q"
# Black castling availability
if b_king.can_castle():
b_square_kingside = board.get_square([7, 7])
b_square_queenside = board.get_square([0, 7])
if isinstance(b_square_kingside, Rook):
if b_square_kingside.can_castle():
castling += "k"
if isinstance(b_square_queenside, Rook):
if b_square_queenside.can_castle():
castling += "q"
return boardfen + " " + turn + castling + " " + passant + " " + str(0) + " " + str(n_turn)
def highlight_square(pos, screen):
"""
Draw circles on valid destination squares for current piece
:type pos: list
:param pos: Position to highlight
:param screen: PyGame screen controller
"""
x = int(width / 8 * pos[0] + width/16)
y = int(height - height / 8 * (1 + pos[1]) + height/16)
pygame.draw.circle(screen, (255, 0, 0), [x, y], 8, 0)
def clear_passants():
"""
Clear en passant flag from last moved pawn
"""
if isinstance(lastMoved, Pawn):
lastMoved.clear_passant()
main()