Skip to content

Commit

Permalink
Fixed horizontal line wrapping
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
GeorgesCoding committed Apr 7, 2024
1 parent 11e8f60 commit 901ca71
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 95 deletions.
66 changes: 37 additions & 29 deletions Controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def isCheck(end, piece, board, opposite, canPassant, text, computer):
moveList = computeAll(-piece, board, 0, opposite, canPassant, computer)
if kingCoord(-piece, board) in moveList:
inCheck = " White king in check" if -piece < 0 else " Black king in check"
addText(text, inCheck, 0, 0)
addText(text, inCheck, 0, 0, 0)


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

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

if (kingCoord(piece, tempBoard) in moveList):
if (kingCoord(piece, board) in moveList):
undo(board, piece, newPiece, newY, newX, oldY, oldX)
return False
else:
undo(board, piece, newPiece, newY, newX, oldY, oldX)
return True


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


# determines if en passant is a possible move
Expand Down Expand Up @@ -387,14 +389,14 @@ def castle(piece, board, oldY, oldX, pSize, size, moveList, text, count):
firstMove(piece, board, oldY, kingX)
firstMove(rook, board, oldY, rookX)
board[mY][tempX] = 0
count = addText(text, str(PIECE[piece]) + str(side), count, 0)
count = addText(text, str(PIECE[piece]) + str(side), count, 0, 0, 0)
return True, count
return False, count


# evaluates if a button is pressed
# also dictates pawn promotion behaviour
def button(selection, info, promotedPiece, board, text, count, length):
def button(selection, info, promotedPiece, board, text, count, length, font):
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])
pawn = "Black"

Expand All @@ -408,8 +410,8 @@ def button(selection, info, promotedPiece, board, text, count, length):
newPiece = -newPiece
pawn = "White"
board[y][x] = newPiece
addText(text, pawn + " pawn promoted to ", 0, 0)
addText(text, " " + str(PIECE[newPiece]), 0, length)
addText(text, pawn + " pawn promoted to", 0, 0, font)
addText(text, " " + str(PIECE[newPiece]), 0, length, font)

if count == 0:
return pressed
Expand All @@ -421,29 +423,35 @@ def button(selection, info, promotedPiece, board, text, count, length):
def checkmate(king, board, canPassant, opposite, computer):
canMove = False
opposite = 1 if (opposite == 0 and computer == None) else 0

moveList = specificCompute(-king, board, canPassant, computer, opposite)[0]
tempBoard = [row[:] for row in board]

# goes through each list in moveList
for i, _ in enumerate(moveList):
n = moveList[i][0]
oldY, oldX = moveList[i][1]
tempBoard = [row[:] for row in board]
tempBoard[oldY][oldX] = 0
board[oldY][oldX] = 0

# goes through each sublist
for (newY, newX) in moveList[i][2]:
movedBoard = [row[:] for row in tempBoard]
if spaceCheck(n, movedBoard, newY, newX):
movedBoard[newY][newX] = n
tempMoveList = computeAll(king, movedBoard, 0, opposite, canPassant, computer)
if kingCoord(king, movedBoard) not in tempMoveList: # king not in check after move
if spaceCheck(n, board, newY, newX):
newPiece = board[newY][newX]
board[newY][newX] = n
tempMoveList = computeAll(king, board, 0, opposite, canPassant, computer)
if kingCoord(king, board) not in tempMoveList: # king not in check after move
canMove = True
undo(board, 0, newPiece, newY, newX, oldY, oldX)
break
undo(board, 0, newPiece, newY, newX, oldY, oldX)
board[oldY][oldX] = n
return not canMove


# undos the move to prevent deep copying the list
def undo(board, oldPiece, newPiece, newY, newX, oldY, oldX):
board[newY][newX] = newPiece
board[oldY][oldX] = oldPiece


# determines if the game has ended through checkmate or stalemate
def gameEnd(board, turn, pieceMoving, start, outline, canPassant, opposite, text, computer):
if outline or not start or pieceMoving:
Expand Down Expand Up @@ -471,29 +479,29 @@ def gameEnd(board, turn, pieceMoving, start, outline, canPassant, opposite, text

# stalemate cases
if emptySpace == 62: # king vs king
addText(text, "Stalemate: Insufficient material.", 0, 0)
addText(text, "Press restart or exit the game.", 0, 0)
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
addText(text, "Press restart or exit the game.", 0, 0, 0)
return True
elif emptySpace == 61 and ((len(bishop) == 1 and knight == 0) or (knight == 1 and len(bishop) == 0)): # 1 bishop/knight vs king
addText(text, "Stalemate: Insufficient material.", 0, 0)
addText(text, "Press restart or exit the game.", 0, 0)
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
addText(text, "Press restart or exit the game.", 0, 0, 0)
return True
elif emptySpace == 60 and len(bishop) == 2: # 1 bishop vs 1 bishop, same colour
b1, b2 = bishop[0][0], bishop[1][0]
i1, i2 = (bishop[0][1] * 8) + bishop[0][2], (bishop[1][1] * 8) + bishop[1][2]
y1, y2, = bishop[0][1], bishop[1][1]
if b1 != b2 and y1 % 2 == i1 % 2 and y2 % 2 == i2 % 2:
addText(text, "Stalemate: Insufficient material.", 0, 0)
addText(text, "Press restart or exit the game.", 0, 0)
addText(text, "Stalemate: Insufficient material.", 0, 0, 0)
addText(text, "Press restart or exit the game.", 0, 0, 0)
return True

if checkmate(king, board, canPassant, opposite, computer): # cannot move
winner = {-9: "Black", -99: "Black", 99: "White", 9: "White"}
if kingCoord(king, board) in moveList: # king in check
addText(text, "Checkmate: " + str(winner[king]) + " won!", 0, 0)
addText(text, "Checkmate: " + str(winner[king]) + " won!", 0, 0, 0)
else: # no possible moves, stalemate
addText(text, "Stalemate: " + str(winner[-king]) + " cannot move.", 0, 0)
addText(text, "Press restart or exit the game.", 0, 0)
addText(text, "Stalemate: " + str(winner[-king]) + " cannot move.", 0, 0, 0)
addText(text, "Press restart or exit the game.", 0, 0, 0)
return True
else:
return False
Expand Down
42 changes: 20 additions & 22 deletions Engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ def minimax(board, canPassant, computer, depth, turn, isPawn, alpha, beta):
opposite = 1 if computer == turn else 0
moves, i = Controller.specificCompute(turn, board, canPassant, computer, opposite) # all possible moves
moves = Controller.computerCastle(turn, board, moves, i, canPassant, computer) # includes castle
tempBoard = [row[:] for row in board]
bestMove = None

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

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

alpha = max(alpha, eval)
if beta <= alpha:
tempBoard = [row[:] for row in board]
break

tempBoard = [row[:] for row in board]
return maxEval, bestMove

else: # black turn
Expand All @@ -181,21 +179,20 @@ def minimax(board, canPassant, computer, depth, turn, isPawn, alpha, beta):
if subList[0] != 10 and Controller.checkMove(subList[0], newMove[0], newMove[1], subList[1][0], subList[1][1], board, canPassant, computer):
oldY, oldX = subList[1]
newY, newX = newMove
tempBoard[oldY][oldX] = 0
tempBoard[newY][newX] = subList[0]
newPiece = board[newY][newX]
board[oldY][oldX] = 0
board[newY][newX] = subList[0]
piece = subList[0]
Controller.enPassantCapture(subList[0], tempBoard, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)
Controller.enPassantCapture(subList[0], board, newY, newX, oldY, oldX, isPawn, canPassant, [], computer, turn, True)

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

beta = min(beta, eval)
if beta <= alpha:
tempBoard = [row[:] for row in board]
break

tempBoard = [row[:] for row in board]
return minEval, bestMove


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

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

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

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


# defines the end game state
# defines the end game state, when most pieces are gone
# used to determine which piece square table to use
def endGameState(board):
# define end game here
return True
empty = 0
for row in board:
empty += row.count(0)

return True if empty < 11 else False
35 changes: 23 additions & 12 deletions GUI.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pygame
import math

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

# adds additional dialouge into the list
# recursively calls itself after deleting the last text dialouge
def addText(array, text, count, length):
def addText(array, text, count, length, font):
concateText = ""
index = 999
reverseArray = reversed(tuple(enumerate(array)))

for i, string in reverseArray: # goes through dialouge array in reverse, finding the most recently added line that contains one of the target values
if string in {"Black pawn promoted to", "White pawn promoted to", "Invalid move:"}:
index = i
changedText = text[3:]
changedText = changedText.lower() if array[index] != "Invalid move:" else changedText
concateText = array[index] + changedText
break

try:
index = array.index("")
if length >= 850:
for string in {"Black pawn promoted to ", "White pawn promoted to ", "Invalid move:"}:
if string in array:
index = array.index(string)
text = text[3:] if array[0] == "Invalid move:" else text
array[index] = array[index] + text
return count
textLength = math.inf if (concateText == "" or font == 0) else font.size(concateText)[0]
index = array.index("") if (index == 999 or textLength + 25 >= length) else index

if textLength + 25 < length:
array[index] = concateText
return count
elif count == 0: # two line text
array[index] = text
return count
Expand All @@ -135,7 +146,7 @@ def addText(array, text, count, length):
except ValueError:
array.pop(0)
array.append("")
return addText(array, text, count, length)
return addText(array, text, count, length, font)


# clears the dialouge list of unecessary dialouge
Expand All @@ -147,7 +158,7 @@ def clearText(array, num):
else:
newArray = []
for n in array:
if n not in {"Invalid move:", " White king in check", " Black king in check", "Invalid Move"}:
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"}:
newArray.append(n)
while len(newArray) != num:
newArray.append("")
Expand Down Expand Up @@ -180,7 +191,7 @@ def dialouge(size, text):
height = 130 + 2 * buttonHeight + buttonHeight/3 + ((40/27)*num) * i
dialougeSurf.blit(render, (size + 30, height))

return dialougeSurf
return dialougeSurf, font


# side window and button control
Expand Down
Loading

0 comments on commit 901ca71

Please sign in to comment.