-
Notifications
You must be signed in to change notification settings - Fork 0
/
main_GUI.py
598 lines (528 loc) · 30.5 KB
/
main_GUI.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
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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# This is the main file to run the game. It includes all the elements for animation & user interactions.
# Citations:
# CMU 112 graphics framework. Available on: https://www.cs.cmu.edu/~112/notes/notes-animations-part1.html
# Images of chess pieces.
# Available on: https://www.kindpng.com/imgv/hxbhmb_chess-pieces-png-chess-pieces-sprite-sheet-transparent/
# Image of leaderboard symbol.
# Available on: https://thenounproject.com/term/pedestal/758425/
from cmu_112_graphics import *
import time
# Below file contains functions + attributes that reinforces chess rules, as well as chess AI
from ChessEngine import *
class NormalChessGame(Mode):
# Makes a chess board with default starting pieces
def makeBoard(self):
# empty board
board = [([None] * self.cols) for row in range(self.rows)]
# rook, knight, bishop, queen, king
board[0][0], board[0][self.cols-1] = Rook('b'), Rook('b')
board[0][1], board[0][self.cols-2] = Knight('b'), Knight('b')
board[0][2], board[0][self.cols-3] = Bishop('b'), Bishop('b')
board[0][3], board[0][4] = Queen('b'), King('b')
board[self.rows-1][0], board[self.rows-1][self.cols-1] = Rook('w'), Rook('w')
board[self.rows-1][1], board[self.rows-1][self.cols-2] = Knight('w'), Knight('w')
board[self.rows-1][2], board[self.rows-1][self.cols-3] = Bishop('w'), Bishop('w')
board[self.rows-1][3], board[self.rows-1][4] = Queen('w'), King('w')
# pawns
for col in range(self.cols):
board[1][col] = Pawn('b')
board[self.rows-2][col] = Pawn('w')
return board
def loadImages(self):
self.bK = self.loadImage("images/bK.png") # K represents king
self.bQ = self.loadImage("images/bQ.png")
self.bB = self.loadImage("images/bB.png")
self.bN = self.loadImage("images/bN.png") # N represents knight
self.bR = self.loadImage("images/bR.png")
self.bP = self.loadImage("images/bP.png")
self.wK = self.loadImage("images/wK.png")
self.wQ = self.loadImage("images/wQ.png")
self.wB = self.loadImage("images/wB.png")
self.wN = self.loadImage("images/wN.png")
self.wR = self.loadImage("images/wR.png")
self.wP = self.loadImage("images/wP.png")
# Stores arguments passed in from splash screen
def __init__(self, AIDifficulty):
super().__init__()
self.AIDifficulty = AIDifficulty
# Stores app variables
def appStarted(self):
self.loadImages()
# Loading board
self.margin = 30
self.rows = self.cols = 8
self.board = self.makeBoard()
# Game considerations
self.selectedPiece = None # (row, col)
self.whiteToMove = True
self.pawnPromotion = None
self.timerDelay = 1000 # AI checking if it is his turn yet
self.cheated = False # Player cannot just turn on AI after winning to improve leaderboard
# Function is taken from https://www.cs.cmu.edu/~112/notes/notes-animations-part1.html
def pointInGrid(self, x, y):
return ((self.margin <= x <= self.width-self.margin) and
(self.margin <= y <= self.height-self.margin))
# Function is taken and modified from https://www.cs.cmu.edu/~112/notes/notes-animations-part1.html
def getCell(self, x, y):
if not self.pointInGrid(x, y):
return (-1, -1)
cellWidth = (self.width - 2*self.margin) / self.cols
cellHeight = (self.height - self.margin - self.margin) / self.rows
row = int((y - self.margin) / cellHeight)
col = int((x - self.margin) / cellWidth)
return row, col
# Function is taken and modified from https://www.cs.cmu.edu/~112/notes/notes-animations-part1.html
def getCellBounds(self, row, col):
cellWidth = (self.width - 2*self.margin) / self.cols
cellHeight = (self.height - self.margin - self.margin) / self.rows
x0 = self.margin + col * cellWidth
x1 = self.margin + (col+1) * cellWidth
y0 = self.margin + row * cellHeight
y1 = self.margin + (row+1) * cellHeight
return (x0, y0, x1, y1)
def movePiece(self, previousRow, previousCol, row, col):
self.board[row][col] = self.board[previousRow][previousCol]
self.board[previousRow][previousCol] = None
# Tracks (row, col) input for chess move that player makes. Makes decision accordingly.
def mousePressed(self, event):
row, col = self.getCell(event.x, event.y)
if (row, col) == (-1, -1): return # input not on board
elif self.board[row][col] != None and self.selectedPiece == None: # selecting a piece
piece = self.board[row][col]
if (piece.color == 'w' and self.whiteToMove) or (piece.color == 'b' and not self.whiteToMove):
self.selectedPiece = (row, col)
elif self.selectedPiece != None: # chess piece already selected
previousRow, previousCol = self.selectedPiece
if (previousRow, previousCol) == (row, col): # unselect piece
self.selectedPiece = None
elif (self.board[previousRow][previousCol].tryMove(
self.board, previousRow, previousCol, row, col)):
# move piece
self.movePiece(previousRow, previousCol, row, col)
# Toggle hasMoved, castling, pawn promotion
ChessEngine.specialRules(self.board, row, col)
# Check if opponent king is in check, checkmate, or stalemate
ChessEngine.checkGameState(self.board, self.whiteToMove)
# Now it is the next player's turn
self.whiteToMove = not self.whiteToMove
self.selectedPiece = None
def checkCondition(self):
self.board[1][5] = self.board[6][4] = None
self.board[2][5] = Pawn('b')
self.board[4][4] = Pawn('w')
def keyPressed(self, event):
if event.key in 'aA': # toggle AI on and off
self.cheated = True
if self.AIDifficulty == None:
self.AIDifficulty = 1
else:
self.AIDifficulty = None
elif event.key in 'rR':
# Update leaderboard upon reset
wKRow, wKCol = ChessEngine.findKing(self.board, 'w')
bKRow, bKCol = ChessEngine.findKing(self.board, 'b')
if self.board[bKRow][bKCol].inCheckmate and self.AIDifficulty != None and not self.cheated:
self.app.wins[self.AIDifficulty - 1] += 1
elif self.board[wKRow][wKCol].inCheckmate and self.AIDifficulty != None and not self.cheated:
self.app.losses[self.AIDifficulty - 1] += 1
# Reset game
self.appStarted()
ChessEngine.stalemate = False
elif event.key in 'xX':
wKRow, wKCol = ChessEngine.findKing(self.board, 'w')
bKRow, bKCol = ChessEngine.findKing(self.board, 'b')
self.app.setActiveMode(self.app.TitlePage)
elif event.key in 'hH':
self.app.setActiveMode(self.app.HelpScreen1)
elif event.key == '1': # check black in 1 move
self.board = self.makeBoard()
self.checkCondition()
self.whiteToMove = True
elif event.key == '2': # checkmate black in 1 move
self.board = self.makeBoard()
self.checkCondition()
self.board[1][6], self.board[3][6] = None, Pawn('b')
self.board[6][5], self.board[4][5] = None, Pawn('w')
self.whiteToMove = True
elif event.key == '3': # AI depth testing
self.board = self.makeBoard()
# white pieces
self.board[6][3] = self.board[7][2] = self.board[7][1] = None
self.board[4][3] = Pawn('w')
self.board[4][5] = Bishop('w')
self.board[3][1] = Knight('w')
# black pieces
self.board[1][3] = self.board[1][4] = None
self.board[3][3], self.board[2][4] = Pawn('b'), Pawn('b')
self.whiteToMove = True
elif event.key == '4': # castling testing
self.board = self.makeBoard()
# white pieces
self.board[6][3] = self.board[7][2] = self.board[7][1] = self.board[7][3] = None
self.board[4][3] = Pawn('w')
self.board[4][5] = Bishop('w')
self.board[3][1] = Knight('w')
self.board[6][3] = Queen('w')
# black pieces
self.board[1][3] = self.board[1][4] = None
self.board[3][3], self.board[2][4] = Pawn('b'), Pawn('b')
self.whiteToMove = True
elif event.key == '5': # black pawn promotion
self.board = self.makeBoard()
# white pieces
self.board[6][7] = self.board[7][7] = None
self.board[5][5] = Rook('w')
# black pieces
self.board[1][6] = None
self.board[6][7] = Pawn('b')
self.whiteToMove = False
def timerFired(self):
if self.AIDifficulty != None and not self.whiteToMove:
print("AI is thinking...")
passed = time.time()
ChessEngine.AIMove(self.board, 'b', self.AIDifficulty)
self.whiteToMove = not self.whiteToMove
print("...AI has moved", time.time() - passed)
# Draws header on top: title & status of game (ie. check)
def drawHeader(self, canvas):
canvas.create_text(self.width / 2, 0, text = 'Chess :)',
anchor = 'n', font = 'Arial 24 bold')
wKRow, wKCol = ChessEngine.findKing(self.board, 'w')
bKRow, bKCol = ChessEngine.findKing(self.board, 'b')
if self.board[wKRow][wKCol].inCheckmate:
canvas.create_text(self.width / 2, self.height - self.margin, text = 'Black wins! Press "r" to reset',
anchor = 'n', font = 'Arial 20 bold')
if self.board[bKRow][bKCol].inCheckmate:
canvas.create_text(self.width / 2, self.height - self.margin, text = 'White wins! Press "r" to reset',
anchor = 'n', font = 'Arial 20 bold')
if self.board[wKRow][wKCol].inCheck:
canvas.create_text(self.margin, self.margin, text = 'White in check!',
anchor = 'sw', font = 'Arial 16')
if self.board[bKRow][bKCol].inCheck:
canvas.create_text(self.width - self.margin, self.margin, text = 'Black in check!',
anchor = 'se', font = 'Arial 16')
if ChessEngine.stalemate:
canvas.create_text(self.width - self.margin, self.margin, text = 'Stalemate!',
anchor = 'se', font = 'Arial 16')
if self.AIDifficulty != None:
canvas.create_text(self.width / 2, self.height - self.margin, text = 'Black is thinking!',
anchor = 'se', font = 'Arial 16')
# Draws empty board
def drawBoard(self, canvas):
for row in range(self.rows):
for col in range(self.cols):
x0, y0, x1, y1 = self.getCellBounds(row, col)
if (row + col) % 2 == 0: fill = 'white'
else: fill = 'saddle brown'
canvas.create_rectangle(x0, y0, x1, y1, fill = fill)
# Finds appropriate image for chess piece
def assignImage(self, chessPiece):
classes = [King, Queen, Bishop, Knight, Rook, Pawn]
if chessPiece.color == 'w':
image = [self.wK, self.wQ, self.wB, self.wN, self.wR, self.wP]
for i in range(len(classes)):
if isinstance(chessPiece, classes[i]):
return image[i]
else:
image = [self.bK, self.bQ, self.bB, self.bN, self.bR, self.bP]
for i in range(len(classes)):
if isinstance(chessPiece, classes[i]):
return image[i]
# Draws pieces on board
def drawPieces(self, canvas):
for row in range(self.rows):
for col in range(self.cols):
if self.board[row][col] != None:
x0, y0, x1, y1 = self.getCellBounds(row, col)
image = self.assignImage(self.board[row][col])
canvas.create_image((x0 + x1) / 2, (y0 + y1) / 2,
image = ImageTk.PhotoImage(image))
if self.selectedPiece != None: # draws a blue ring around selected piece
row, col = self.selectedPiece
if row < 8:
x0, y0, x1, y1 = self.getCellBounds(row, col)
canvas.create_rectangle(x0, y0, x1, y1, width = 4, outline = 'deep sky blue')
# Draws app
def redrawAll(self, canvas):
self.drawHeader(canvas)
self.drawBoard(canvas)
self.drawPieces(canvas)
class CrazyhouseGame(NormalChessGame):
def appStarted(self):
super().appStarted()
self.margin = 50
self.whitePieces = {Queen: 0, Rook: 0, Bishop: 0, Knight: 0, Pawn: 0}
self.blackPieces = {Queen: 0, Rook: 0, Bishop: 0, Knight: 0, Pawn: 0}
def movePiece(self, previousRow, previousCol, row, col):
if self.board[row][col] != None:
if self.whiteToMove: self.whitePieces[type(self.board[row][col])] += 1
else: self.blackPieces[type(self.board[row][col])] += 1
self.board[row][col] = self.board[previousRow][previousCol]
self.board[previousRow][previousCol] = None
def getCrazyCell(self, x, y, color):
pieces = [Queen, Rook, Bishop, Knight, Pawn]
if color == 'b':
x0, x1 = 5, self.width / 2 - self.margin
row = 8
else:
x0, x1 = self.width / 2 + self.margin, self.width - 5
row = 9
cellWidth = (x1 - x0) / len(pieces)
col = int((x - x0) / cellWidth)
if color == 'b' and self.blackPieces[pieces[col]] != 0 and not self.whiteToMove:
return (row, col)
elif color == 'w' and self.whitePieces[pieces[col]] != 0 and self.whiteToMove:
return (row, col)
else:
return (-1, -1)
def mousePressed(self, event):
row, col = self.getCell(event.x, event.y)
if (row, col) == (-1, -1):
# Additional crazyhouse selections, if piece not selected already
if (event.x <= self.width / 2 - self.margin and
event.y >= self.height - self.margin):
row, col = self.getCrazyCell(event.x, event.y, 'b')
elif (event.x >= self.width / 2 + self.margin and
event.y >= self.height - self.margin):
row, col = self.getCrazyCell(event.x, event.y, 'w')
if (row, col) != (-1, -1) and self.selectedPiece == None:
self.selectedPiece = (row, col)
elif self.selectedPiece == (row, col):
self.selectedPiece = None
elif self.board[row][col] != None and self.selectedPiece == None: # selecting a piece
piece = self.board[row][col]
if (piece.color == 'w' and self.whiteToMove) or (piece.color == 'b' and not self.whiteToMove):
self.selectedPiece = (row, col)
elif self.selectedPiece != None: # chess piece already selected
previousRow, previousCol = self.selectedPiece
if (previousRow, previousCol) == (row, col): # unselect piece
self.selectedPiece = None
return
elif previousRow >= 8:
if not ChessEngine.tryCrazyhouseMove(self.board, previousRow, previousCol, row, col):
return # illegal move: do nothing
elif previousRow == 8 and self.board[row][col] == None: # crazyhouse move
pieces = list(self.blackPieces)
self.board[row][col] = pieces[previousCol]('b')
self.blackPieces[pieces[previousCol]] -= 1
elif previousRow == 9 and self.board[row][col] == None: # crazyhouse move
pieces = list(self.whitePieces)
self.board[row][col] = pieces[previousCol]('w')
self.whitePieces[pieces[previousCol]] -= 1
elif (self.board[previousRow][previousCol].tryMove(
self.board, previousRow, previousCol, row, col)):
# move piece
self.movePiece(previousRow, previousCol, row, col)
else: return # no legal move made
# Toggle hasMoved, castling, pawn promotion
ChessEngine.specialRules(self.board, row, col)
# Check if opponent king is in check, checkmate, or stalemate
if self.whiteToMove: crazyhousePieces = self.blackPieces
else: crazyhousePieces = self.whitePieces
ChessEngine.checkGameState(self.board, self.whiteToMove, crazyhousePieces)
# Now it is the next player's turn
self.whiteToMove = not self.whiteToMove
self.selectedPiece = None
# if row < 8: print(self.board[row][col])
def keyPressed(self, event):
super().keyPressed(event)
if event.key in 'hH':
self.app.setActiveMode(self.app.HelpScreen2)
def drawHeader(self, canvas):
super().drawHeader(canvas)
canvas.create_text(self.width / 2, self.margin, text = 'Crazyhouse variant!',
anchor = 's', font = 'Arial 16 bold')
def drawCrazyhousePieces(self, canvas, color, x0, y0, x1, y1):
pieces = [Queen, Rook, Bishop, Knight, Pawn]
cellWidth = (x1 - x0) / len(pieces)
yMid = y1 - 15
for i in range(len(pieces)):
image = self.assignImage(pieces[i](color))
image = self.scaleImage(image, 2/3)
startX, endX = x0 + i * cellWidth, x0 + (i + 1) * cellWidth
canvas.create_image((startX + endX) / 2, (y0 + yMid) / 2,
image = ImageTk.PhotoImage(image))
if color == 'w':
d = self.whitePieces
else:
d = self.blackPieces
canvas.create_text((startX + endX) / 2, yMid, text = d[pieces[i]],
font = 'Arial 10', anchor = 'n')
if self.selectedPiece != None:
row, col = self.selectedPiece
if row == 8 and color == 'b':
canvas.create_rectangle(x0 + col * cellWidth, y0, x0 + (col + 1) * cellWidth,
yMid, width = 3, outline = 'deep sky blue')
elif row == 9 and color == 'w':
canvas.create_rectangle(x0 + col * cellWidth, y0, x0 + (col + 1) * cellWidth,
yMid, width = 3, outline = 'deep sky blue')
def redrawAll(self, canvas):
super().redrawAll(canvas)
self.drawCrazyhousePieces(canvas, 'b', 5, self.height - self.margin,
self.width / 2 - self.margin, self.height)
self.drawCrazyhousePieces(canvas, 'w', self.width / 2 + self.margin,
self.height - self.margin, self.width - 5, self.height)
class TitlePage(Mode):
def appStarted(self):
self.margin = 30
self.cy = []
for box in range(1, 5):
self.cy += [self.margin + box * self.height / 5]
self.boxWidth, self.boxHeight = 160, 40
self.leaderboard = self.loadImage("images/leaderboard.png")
def redrawAll(self, canvas):
canvas.create_rectangle(0, 0, self.width, self.height, fill = "saddle brown")
font1, font2, font3 = 'Arial 30 bold underline', 'Arial 22', 'Arial 16'
canvas.create_text(self.width / 2, 2 * self.margin, text = 'Chess!', font = font1, fill = 'white')
s = """Click to play any of the modes below\n\nPress 'h' for help and hot keys"""
canvas.create_text(self.width / 2, self.cy[0], text = s, font = font3, fill = 'white', justify = 'center')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[1] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[1] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[1], text = '2 Player Chess', font = font2, fill = 'white')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[2] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[2] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[2], text = 'Play against the Kosbie (AI)!', font = font2, fill = 'white')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[3] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[3] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[3], text = 'Crazyhouse!', font = font2, fill = 'white')
canvas.create_image(self.width - 2 * self.margin, 2 * self.margin, image=ImageTk.PhotoImage(self.leaderboard))
def mousePressed(self, event):
imageMidLength = 25
if (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[1] - self.boxHeight <= event.y <= self.cy[1] + self.boxHeight):
self.app.setActiveMode(self.app.NormalChessGame)
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[2] - self.boxHeight <= event.y <= self.cy[2] + self.boxHeight):
self.app.setActiveMode(self.app.AIDifficultyScreen)
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[3] - self.boxHeight <= event.y <= self.cy[3] + self.boxHeight):
self.app.setActiveMode(self.app.Crazyhouse)
elif (self.width - 2 * self.margin - imageMidLength <= event.x <= self.width - 2 * self.margin + imageMidLength
and 2 * self.margin - imageMidLength <= event.y <= 2 * self.margin + imageMidLength):
self.app.setActiveMode(self.app.Leaderboard)
def keyPressed(self, event):
if event.key in 'hH':
self.app.setActiveMode(self.app.HelpScreen)
elif event.key in 'xX':
self.app.setActiveMode(self.app.TitlePage)
class HelpScreen(TitlePage):
def __init__(self, previousGamemode):
super().__init__()
self.previousGamemode = previousGamemode
def redrawAll(self, canvas):
canvas.create_rectangle(0, 0, self.width, self.height, fill = "saddle brown")
font1, font2, font3 = 'Arial 30 bold underline', 'Arial 22', 'Arial 16'
canvas.create_text(self.width / 2, 2 * self.margin, text = 'Help', font = font1, fill = 'white')
s0 = ("""You can play various modes! \n Play with a friend in a normal 2 player game or in the """ +
"""whacky crazyhouse variant\n Alternatively, you can practice against Prof. Kosbie, the AI >:)""")
s1 = ("""While in game, press 'r' to restart game on same settings \n""" +
"""Press 'a' to let Kosbie (easy difficulty) take over the black pieces!""")
s2 = """If currently in game, press 'b' to return to game"""
s3 = ("""Click anywhere or press 'x' to return to main menu and try a another gamemode!\n""")
canvas.create_text(self.width / 2, self.cy[0], text = s0, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[1], text = s1, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[2], text = s2, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[2], text = s2, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[3], text = s3, font = font3, fill = 'white', justify = 'center')
def mousePressed(self, event):
self.app.setActiveMode(self.app.TitlePage)
def keyPressed(self, event):
super().keyPressed(event)
if event.key in 'bB' and self.previousGamemode == "Normal":
self.app.setActiveMode(self.app.NormalChessGame)
elif event.key in 'bB' and self.previousGamemode == "Crazyhouse":
self.app.setActiveMode(self.app.Crazyhouse)
class AIDifficultyScreen(TitlePage):
def appStarted(self):
super().appStarted()
self.difficulty = None
def redrawAll(self, canvas):
canvas.create_rectangle(0, 0, self.width, self.height, fill = "saddle brown")
font1, font2, font3 = 'Arial 30 bold underline', 'Arial 22', 'Arial 16'
canvas.create_text(self.width / 2, 2 * self.margin, text = 'Prof. Kosbie difficulty:', font = font2, fill = 'white')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[0] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[0] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[0], text = 'Easy', font = font2, fill = 'white')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[1] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[1] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[1], text = 'Medium', font = font2, fill = 'white')
canvas.create_rectangle(self.width / 2 - self.boxWidth, self.cy[2] - self.boxHeight,
self.width / 2 + self.boxWidth, self.cy[2] + self.boxHeight,
fill = 'black', outline = 'white')
canvas.create_text(self.width / 2, self.cy[2], text = '¡Hard!', font = font2, fill = 'white')
if self.difficulty != None:
s = f"Kosbie thinks {self.difficulty} moves ahead"
canvas.create_text(self.width / 2, self.cy[3], text = s, font = font3, fill = 'white')
def mousePressed(self, event):
if (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[0] - self.boxHeight <= event.y <= self.cy[0] + self.boxHeight):
self.app.setActiveMode(self.app.EasyDifficulty)
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[1] - self.boxHeight <= event.y <= self.cy[1] + self.boxHeight):
self.app.setActiveMode(self.app.NormalDifficulty)
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[2] - self.boxHeight <= event.y <= self.cy[2] + self.boxHeight):
self.app.setActiveMode(self.app.HardDifficulty)
def mouseMoved(self, event):
if (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[0] - self.boxHeight <= event.y <= self.cy[0] + self.boxHeight):
self.difficulty = 1
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[1] - self.boxHeight <= event.y <= self.cy[1] + self.boxHeight):
self.difficulty = 2
elif (self.width / 2 - self.boxWidth <= event.x <= self.width / 2 + self.boxWidth and
self.cy[2] - self.boxHeight <= event.y <= self.cy[2] + self.boxHeight):
self.difficulty = 3
else:
self.difficulty = None
class Leaderboard(TitlePage):
# Algorithm for calculating ELO rating: https://en.wikipedia.org/wiki/Elo_rating_system#Performance_rating
def redrawAll(self, canvas):
canvas.create_rectangle(0, 0, self.width, self.height, fill = "saddle brown")
font1, font2, font3 = 'Arial 30 bold underline', 'Arial 22', 'Arial 16'
canvas.create_text(self.width / 2, 2 * self.margin, text = 'Leaderboard', font = font1, fill = 'white')
subheader = ("""Play against Kosbie to improve your ELO rating!""")
s0 = (f"""Wins against Easy Kosbie (ELO ~1350): {self.app.wins[0]} \nWins against Medium Kosbie (ELO ~1420):""" +
f""" {self.app.wins[1]} \nWins against Hard Kosbie (ELO ~1500): {self.app.wins[2]}""")
s1 = (f"""Losses against Easy Kosbie (ELO ~1350): {self.app.losses[0]} \nLosses against Medium Kosbie """ +
f"""(ELO ~1420): {self.app.losses[1]} \nLosses against Hard Kosbie (ELO ~1500): {self.app.losses[2]}""")
gamesPlayed = sum(self.app.wins) + sum(self.app.losses)
if gamesPlayed == 0:
playerELO = '0 – Go play Kosbie!'
else:
playerELO, AIELO = 0, [1350, 1420, 1500]
for difficulty in range(3):
playerELO += (AIELO[difficulty] + 400) * self.app.wins[difficulty]
playerELO += (AIELO[difficulty] - 400) * self.app.losses[difficulty]
playerELO //= gamesPlayed
s2 = f"""Your ELO rating: {playerELO} \nDisclamer: all ELOs provided are rough estimates!"""
s3 = "Press 'x' or click the mouse to return to main menu!"
canvas.create_text(self.width / 2, 2 * self.margin + 36, text = subheader, font = font2, fill = 'white',
justify = 'center')
canvas.create_text(self.width / 2, self.cy[0], text = s0, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[1], text = s1, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[2], text = s2, font = font3, fill = 'white', justify = 'center')
canvas.create_text(self.width / 2, self.cy[3], text = s3, font = font3, fill = 'white', justify = 'center')
def mousePressed(self, event):
self.app.setActiveMode(self.app.TitlePage)
class MyModalApp(ModalApp):
def appStarted(app):
app.TitlePage = TitlePage()
app.NormalChessGame = NormalChessGame(None) # None = no AI
app.Crazyhouse = CrazyhouseGame(None)
app.EasyDifficulty = NormalChessGame(1) # AI difficulty
app.NormalDifficulty = NormalChessGame(2)
app.HardDifficulty = NormalChessGame(3)
app.AIDifficultyScreen = AIDifficultyScreen()
app.HelpScreen = HelpScreen(None) # help screen when called from main menu
app.HelpScreen1 = HelpScreen("Normal") # when called from a NormalChessGame
app.HelpScreen2 = HelpScreen("Crazyhouse") # when called from a CrazyhouseGame
app.Leaderboard = Leaderboard()
app.setActiveMode(app.TitlePage)
app.wins, app.losses = [0, 0, 0], [0, 0, 0]
MyModalApp(width = 650, height = 650)