Skip to content

Commit 901ca71

Browse files
committed
Fixed horizontal line wrapping
My first version of horizontal line wrapping wasn't exactly dynamic so it didn't work as desired. This version will do so for all screen sizes. Also added in the to check wether or not the king is in check directly after promotion, something I forgot to do before. Game is pretty much done, as any other work will be to improve upon the AI.
1 parent 11e8f60 commit 901ca71

5 files changed

Lines changed: 114 additions & 95 deletions

File tree

Controller.py

Lines changed: 37 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def isCheck(end, piece, board, opposite, canPassant, text, computer):
5757
moveList = computeAll(-piece, board, 0, opposite, canPassant, computer)
5858
if kingCoord(-piece, board) in moveList:
5959
inCheck = " White king in check" if -piece < 0 else " Black king in check"
60-
addText(text, inCheck, 0, 0)
60+
addText(text, inCheck, 0, 0, 0)
6161

6262

6363
# computes the board position of the piece
@@ -80,14 +80,16 @@ def kingCoord(piece, board):
8080

8181
# determine if move is legal during check or if it prevents check
8282
def checkMove(piece, newY, newX, oldY, oldX, board, canPassant, computer):
83-
tempBoard = [row[:] for row in board]
84-
tempBoard[oldY][oldX] = 0
85-
tempBoard[newY][newX] = piece
86-
moveList = computeAll(piece, tempBoard, 0, 0, canPassant, computer)
83+
newPiece = board[newY][newX]
84+
board[oldY][oldX] = 0
85+
board[newY][newX] = piece
86+
moveList = computeAll(piece, board, 0, 0, canPassant, computer)
8787

88-
if (kingCoord(piece, tempBoard) in moveList):
88+
if (kingCoord(piece, board) in moveList):
89+
undo(board, piece, newPiece, newY, newX, oldY, oldX)
8990
return False
9091
else:
92+
undo(board, piece, newPiece, newY, newX, oldY, oldX)
9193
return True
9294

9395

@@ -231,7 +233,7 @@ def enPassantCapture(piece, board, newY, newX, oldY, oldX, isPawn, canPassant, t
231233
if isPawn and coord in canPassant and piece in {-1, 11, -11, 1} and spaceCheck(piece, board, newY, newX) and newCoord == canPassant[0]:
232234
board[newY + add][newX] = 0
233235
if not noPrint:
234-
addText(text, PIECE[piece] + " en passant capture", 0, 0)
236+
addText(text, PIECE[piece] + " en passant capture", 0, 0, 0)
235237

236238

237239
# determines if en passant is a possible move
@@ -387,14 +389,14 @@ def castle(piece, board, oldY, oldX, pSize, size, moveList, text, count):
387389
firstMove(piece, board, oldY, kingX)
388390
firstMove(rook, board, oldY, rookX)
389391
board[mY][tempX] = 0
390-
count = addText(text, str(PIECE[piece]) + str(side), count, 0)
392+
count = addText(text, str(PIECE[piece]) + str(side), count, 0, 0, 0)
391393
return True, count
392394
return False, count
393395

394396

395397
# evaluates if a button is pressed
396398
# also dictates pawn promotion behaviour
397-
def button(selection, info, promotedPiece, board, text, count, length):
399+
def button(selection, info, promotedPiece, board, text, count, length, font):
398400
pressed = pygame.mouse.get_pos()[0] in range(info[0], info[2] + info[0]) and pygame.mouse.get_pos()[1] in range(info[1], info[3] + info[1])
399401
pawn = "Black"
400402

@@ -408,8 +410,8 @@ def button(selection, info, promotedPiece, board, text, count, length):
408410
newPiece = -newPiece
409411
pawn = "White"
410412
board[y][x] = newPiece
411-
addText(text, pawn + " pawn promoted to ", 0, 0)
412-
addText(text, " " + str(PIECE[newPiece]), 0, length)
413+
addText(text, pawn + " pawn promoted to", 0, 0, font)
414+
addText(text, " " + str(PIECE[newPiece]), 0, length, font)
413415

414416
if count == 0:
415417
return pressed
@@ -421,29 +423,35 @@ def button(selection, info, promotedPiece, board, text, count, length):
421423
def checkmate(king, board, canPassant, opposite, computer):
422424
canMove = False
423425
opposite = 1 if (opposite == 0 and computer == None) else 0
424-
425426
moveList = specificCompute(-king, board, canPassant, computer, opposite)[0]
426-
tempBoard = [row[:] for row in board]
427427

428428
# goes through each list in moveList
429429
for i, _ in enumerate(moveList):
430430
n = moveList[i][0]
431431
oldY, oldX = moveList[i][1]
432-
tempBoard = [row[:] for row in board]
433-
tempBoard[oldY][oldX] = 0
432+
board[oldY][oldX] = 0
434433

435434
# goes through each sublist
436435
for (newY, newX) in moveList[i][2]:
437-
movedBoard = [row[:] for row in tempBoard]
438-
if spaceCheck(n, movedBoard, newY, newX):
439-
movedBoard[newY][newX] = n
440-
tempMoveList = computeAll(king, movedBoard, 0, opposite, canPassant, computer)
441-
if kingCoord(king, movedBoard) not in tempMoveList: # king not in check after move
436+
if spaceCheck(n, board, newY, newX):
437+
newPiece = board[newY][newX]
438+
board[newY][newX] = n
439+
tempMoveList = computeAll(king, board, 0, opposite, canPassant, computer)
440+
if kingCoord(king, board) not in tempMoveList: # king not in check after move
442441
canMove = True
442+
undo(board, 0, newPiece, newY, newX, oldY, oldX)
443443
break
444+
undo(board, 0, newPiece, newY, newX, oldY, oldX)
445+
board[oldY][oldX] = n
444446
return not canMove
445447

446448

449+
# undos the move to prevent deep copying the list
450+
def undo(board, oldPiece, newPiece, newY, newX, oldY, oldX):
451+
board[newY][newX] = newPiece
452+
board[oldY][oldX] = oldPiece
453+
454+
447455
# determines if the game has ended through checkmate or stalemate
448456
def gameEnd(board, turn, pieceMoving, start, outline, canPassant, opposite, text, computer):
449457
if outline or not start or pieceMoving:
@@ -471,29 +479,29 @@ def gameEnd(board, turn, pieceMoving, start, outline, canPassant, opposite, text
471479

472480
# stalemate cases
473481
if emptySpace == 62: # king vs king
474-
addText(text, "Stalemate: Insufficient material.", 0, 0)
475-
addText(text, "Press restart or exit the game.", 0, 0)
482+
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
483+
addText(text, "Press restart or exit the game.", 0, 0, 0)
476484
return True
477485
elif emptySpace == 61 and ((len(bishop) == 1 and knight == 0) or (knight == 1 and len(bishop) == 0)): # 1 bishop/knight vs king
478-
addText(text, "Stalemate: Insufficient material.", 0, 0)
479-
addText(text, "Press restart or exit the game.", 0, 0)
486+
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
487+
addText(text, "Press restart or exit the game.", 0, 0, 0)
480488
return True
481489
elif emptySpace == 60 and len(bishop) == 2: # 1 bishop vs 1 bishop, same colour
482490
b1, b2 = bishop[0][0], bishop[1][0]
483491
i1, i2 = (bishop[0][1] * 8) + bishop[0][2], (bishop[1][1] * 8) + bishop[1][2]
484492
y1, y2, = bishop[0][1], bishop[1][1]
485493
if b1 != b2 and y1 % 2 == i1 % 2 and y2 % 2 == i2 % 2:
486-
addText(text, "Stalemate: Insufficient material.", 0, 0)
487-
addText(text, "Press restart or exit the game.", 0, 0)
494+
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
495+
addText(text, "Press restart or exit the game.", 0, 0, 0)
488496
return True
489497

490498
if checkmate(king, board, canPassant, opposite, computer): # cannot move
491499
winner = {-9: "Black", -99: "Black", 99: "White", 9: "White"}
492500
if kingCoord(king, board) in moveList: # king in check
493-
addText(text, "Checkmate: " + str(winner[king]) + " won!", 0, 0)
501+
addText(text, "Checkmate: " + str(winner[king]) + " won!", 0, 0, 0)
494502
else: # no possible moves, stalemate
495-
addText(text, "Stalemate: " + str(winner[-king]) + " cannot move.", 0, 0)
496-
addText(text, "Press restart or exit the game.", 0, 0)
503+
addText(text, "Stalemate: " + str(winner[-king]) + " cannot move.", 0, 0, 0)
504+
addText(text, "Press restart or exit the game.", 0, 0, 0)
497505
return True
498506
else:
499507
return False

Engine.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ def minimax(board, canPassant, computer, depth, turn, isPawn, alpha, beta):
144144
opposite = 1 if computer == turn else 0
145145
moves, i = Controller.specificCompute(turn, board, canPassant, computer, opposite) # all possible moves
146146
moves = Controller.computerCastle(turn, board, moves, i, canPassant, computer) # includes castle
147-
tempBoard = [row[:] for row in board]
148147
bestMove = None
149148

150149
if depth == 0 or Controller.gameOver(computer, turn, board, canPassant): # reached end of tree or game ended
@@ -157,21 +156,20 @@ def minimax(board, canPassant, computer, depth, turn, isPawn, alpha, beta):
157156
if subList[0] != 10 and Controller.checkMove(subList[0], newMove[0], newMove[1], subList[1][0], subList[1][1], board, canPassant, computer):
158157
oldY, oldX = subList[1]
159158
newY, newX = newMove
160-
tempBoard[oldY][oldX] = 0
161-
tempBoard[newY][newX] = subList[0]
159+
newPiece = board[newY][newX]
160+
board[oldY][oldX] = 0
161+
board[newY][newX] = subList[0]
162162
piece = subList[0]
163-
Controller.enPassantCapture(subList[0], tempBoard, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)
163+
Controller.enPassantCapture(subList[0], board, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)
164164

165-
eval = minimax(tempBoard, canPassant, computer, depth - 1, -turn, isPawn, alpha, beta)[0]
165+
eval = minimax(board, canPassant, computer, depth - 1, -turn, isPawn, alpha, beta)[0]
166166
maxEval = max(maxEval, eval)
167167
bestMove = (oldY, oldX, newY, newX, piece) if maxEval == eval else bestMove
168+
Controller.undo(board, subList[0], newPiece, newY, newX, oldY, oldX)
168169

169170
alpha = max(alpha, eval)
170171
if beta <= alpha:
171-
tempBoard = [row[:] for row in board]
172172
break
173-
174-
tempBoard = [row[:] for row in board]
175173
return maxEval, bestMove
176174

177175
else: # black turn
@@ -181,21 +179,20 @@ def minimax(board, canPassant, computer, depth, turn, isPawn, alpha, beta):
181179
if subList[0] != 10 and Controller.checkMove(subList[0], newMove[0], newMove[1], subList[1][0], subList[1][1], board, canPassant, computer):
182180
oldY, oldX = subList[1]
183181
newY, newX = newMove
184-
tempBoard[oldY][oldX] = 0
185-
tempBoard[newY][newX] = subList[0]
182+
newPiece = board[newY][newX]
183+
board[oldY][oldX] = 0
184+
board[newY][newX] = subList[0]
186185
piece = subList[0]
187-
Controller.enPassantCapture(subList[0], tempBoard, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)
186+
Controller.enPassantCapture(subList[0], board, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)
188187

189-
eval = minimax(tempBoard, canPassant, computer, depth - 1, -turn, isPawn, alpha, beta)[0]
188+
eval = minimax(board, canPassant, computer, depth - 1, -turn, isPawn, alpha, beta)[0]
190189
minEval = min(minEval, eval)
191190
bestMove = (oldY, oldX, newY, newX, piece) if minEval == eval else bestMove
191+
Controller.undo(board, subList[0], newPiece, newY, newX, oldY, oldX)
192192

193193
beta = min(beta, eval)
194194
if beta <= alpha:
195-
tempBoard = [row[:] for row in board]
196195
break
197-
198-
tempBoard = [row[:] for row in board]
199196
return minEval, bestMove
200197

201198

@@ -256,7 +253,6 @@ def pieceSquare(piece, y, x, flip, board):
256253
# incporporates the values of the remaining pieces on the board, their positioning and if the king is in check
257254
def boardEvaluation(board, computer, canPassant):
258255
eval = 0
259-
tempBoard = [row[:] for row in board]
260256

261257
for y, row in enumerate(board):
262258
for x, n in enumerate(row): # evaluates the number of pieces on each side
@@ -265,10 +261,9 @@ def boardEvaluation(board, computer, canPassant):
265261
table = pieceSquare(n, y, x, flip, board) if n < 0 else -pieceSquare(n, y, x, flip, board)
266262
eval += temp + table
267263

268-
# computeAll gave an index error in pawnMove
269264
opposite = 1 if computer == BLACK else 0
270-
blackList = Controller.computeAll(1, tempBoard, 0, opposite + 1, canPassant, computer)
271-
whiteList = Controller.computeAll(-1, tempBoard, 0, opposite, canPassant, computer)
265+
blackList = Controller.computeAll(1, board, 0, opposite + 1, canPassant, computer)
266+
whiteList = Controller.computeAll(-1, board, 0, opposite, canPassant, computer)
272267

273268
if Controller.kingCoord(1, board) in blackList:
274269
eval -= 100 # black king in check
@@ -278,8 +273,11 @@ def boardEvaluation(board, computer, canPassant):
278273
return eval
279274

280275

281-
# defines the end game state
276+
# defines the end game state, when most pieces are gone
282277
# used to determine which piece square table to use
283278
def endGameState(board):
284-
# define end game here
285-
return True
279+
empty = 0
280+
for row in board:
281+
empty += row.count(0)
282+
283+
return True if empty < 11 else False

GUI.py

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pygame
2+
import math
23

34
# dictionary constant of the paths of the image of the pieces
45
IMAGEPATH = {
@@ -116,16 +117,26 @@ def promoOutline(screen, size, toggle):
116117

117118
# adds additional dialouge into the list
118119
# recursively calls itself after deleting the last text dialouge
119-
def addText(array, text, count, length):
120+
def addText(array, text, count, length, font):
121+
concateText = ""
122+
index = 999
123+
reverseArray = reversed(tuple(enumerate(array)))
124+
125+
for i, string in reverseArray: # goes through dialouge array in reverse, finding the most recently added line that contains one of the target values
126+
if string in {"Black pawn promoted to", "White pawn promoted to", "Invalid move:"}:
127+
index = i
128+
changedText = text[3:]
129+
changedText = changedText.lower() if array[index] != "Invalid move:" else changedText
130+
concateText = array[index] + changedText
131+
break
132+
120133
try:
121-
index = array.index("")
122-
if length >= 850:
123-
for string in {"Black pawn promoted to ", "White pawn promoted to ", "Invalid move:"}:
124-
if string in array:
125-
index = array.index(string)
126-
text = text[3:] if array[0] == "Invalid move:" else text
127-
array[index] = array[index] + text
128-
return count
134+
textLength = math.inf if (concateText == "" or font == 0) else font.size(concateText)[0]
135+
index = array.index("") if (index == 999 or textLength + 25 >= length) else index
136+
137+
if textLength + 25 < length:
138+
array[index] = concateText
139+
return count
129140
elif count == 0: # two line text
130141
array[index] = text
131142
return count
@@ -135,7 +146,7 @@ def addText(array, text, count, length):
135146
except ValueError:
136147
array.pop(0)
137148
array.append("")
138-
return addText(array, text, count, length)
149+
return addText(array, text, count, length, font)
139150

140151

141152
# clears the dialouge list of unecessary dialouge
@@ -147,7 +158,7 @@ def clearText(array, num):
147158
else:
148159
newArray = []
149160
for n in array:
150-
if n not in {"Invalid move:", " White king in check", " Black king in check", "Invalid Move"}:
161+
if n not in {"Invalid move:", " White king in check", " Black king in check", "Invalid Move", "Invalid move: White king in check", "Invalid move: Black king in check"}:
151162
newArray.append(n)
152163
while len(newArray) != num:
153164
newArray.append("")
@@ -180,7 +191,7 @@ def dialouge(size, text):
180191
height = 130 + 2 * buttonHeight + buttonHeight/3 + ((40/27)*num) * i
181192
dialougeSurf.blit(render, (size + 30, height))
182193

183-
return dialougeSurf
194+
return dialougeSurf, font
184195

185196

186197
# side window and button control

0 commit comments

Comments
 (0)