def canCastle(self, row, col): # the king cannot castle if it has already moved or if it's in check if self.hasMoved or self.isInCheck(self.row, self.col): return False # short castle if row == self.row and col == 6: # a lot of conditions have to be met: the king and the rook must not have moved, # there has to be no piece between the king and the rook and every square between the king and the rook has to be safe from # checks from the enemy pieces return (board.isEmpty(self.row, 5) and board.isEmpty(self.row, 6) and not board.isEmpty(self.row, 7) and board.getPiece(self.row, 7).hasMoved == False and not self.isInCheck(self.row, 5) and not self.isInCheck(self.row, 6)) # long castle elif row == self.row and col == 2: return (board.isEmpty(self.row, 3) and board.isEmpty(self.row, 2) and board.isEmpty(self.row, 1) and not board.isEmpty(self.row, 0) and board.getPiece(self.row, 0).hasMoved == False and not self.isInCheck(self.row, 3) and not self.isInCheck(self.row, 2)) return False
def castle(self, row, col): if col == 6: board.getPiece(self.row, 7).updateBoard(self.row, 5) else: board.getPiece(self.row, 0).updateBoard(self.row, 3) self.updateBoard(row, col)
def promote(pos, row, col, color): # calculate the position of the rects rectDim = 150 rectRow = None rectCol = None if pos[0] + rectDim >= height: rectRow = pos[0] - rectDim - 1 else: rectRow = pos[0] if pos[1] + rectDim >= width: rectCol = pos[1] - rectDim - 1 else: rectCol = pos[1] rects = [] imgs = [] imgRects = [] # display the rects for x in [rectRow, rectRow + int(rectDim / 2) + 1]: for y in [rectCol, rectCol + int(rectDim / 2) + 1]: rects.append( pygame.draw.rect(screen, (255, 255, 255), (x, y, int(rectDim / 2), int(rectDim / 2)))) pygame.draw.line(screen, (50, 50, 50), (rectRow + int(rectDim / 2), rectCol), (rectRow + int(rectDim / 2), rectCol + rectDim)) pygame.draw.line(screen, (50, 50, 50), (rectRow, rectCol + int(rectDim / 2)), (rectRow + rectDim, rectCol + int(rectDim / 2))) # display the imgs for piece in ["q", "r", "n", "b"]: imgs.append(pygame.image.load("imgs/" + color[0] + piece + ".png")) imgRects.append(imgs[len(imgs) - 1].get_rect()) imgRects[len(imgs) - 1].center = rects[len(imgs) - 1].center screen.blit(imgs[len(imgs) - 1], imgRects[len(imgs) - 1]) pygame.display.update() # transform the pawn into another piece while True: for event in pygame.event.get(): if event.type == pygame.MOUSEBUTTONUP: if rects[0].collidepoint(event.pos): pieces.Queen(row, col, color) board.getPiece(row, col).draw(screen, width, height) return if rects[1].collidepoint(event.pos): pieces.Rook(row, col, color) board.getPiece(row, col).draw(screen, width, height) return if rects[2].collidepoint(event.pos): pieces.Knight(row, col, color) board.getPiece(row, col).draw(screen, width, height) return if rects[3].collidepoint(event.pos): pieces.Bishop(row, col, color) board.getPiece(row, col).draw(screen, width, height) return
def isLegalMove(self, newRow, newCol): # if the move is out of bounds, it is not legal if newRow < 0 or newRow > 7 or newCol < 0 or newCol > 7: return False currentRow = self.row currentCol = self.col piece = board.getPiece(newRow, newCol) board.board[newRow][newCol] = self board.board[self.row][self.col] = None self.row = newRow self.col = newCol answer = True if (self.notation[0] == "w" and whiteKing.isInCheck(whiteKing.row, whiteKing.col) or self.notation[0] == "b" and blackKing.isInCheck(blackKing.row, blackKing.col)): answer = False board.board[currentRow][currentCol] = self board.board[self.row][self.col] = piece self.row = currentRow self.col = currentCol return answer
def canMove(self, newRow, newCol): # it can't move there if it's not in the moves set if not (newRow - self.row, newCol - self.col) in self.moves: return False # finding out the direction the piece will go, on the x and y axes signRow = 0 signCol = 0 if self.row != newRow: signRow = int(abs(newRow - self.row) / (newRow - self.row)) if self.col != newCol: signCol = int(abs(newCol - self.col) / (newCol - self.col)) # it can't move if there is a piece between its current and new position # we check every single suare between the current and next position, in the direction that we found out earlier diff = 0 while self.row + signRow * diff != newRow or self.col + signCol * diff != newCol: diff += 1 # if there is a piece between the current and next position if not board.isEmpty(self.row + signRow * diff, self.col + signCol * diff): # if the piece is exactly on the next position and its color is opposite to this piece's, then the piece can capture it if (self.row + signRow * diff == newRow and self.col + signCol * diff == newCol and board.getPiece( newRow, newCol).notation[0] != self.notation[0]): return True # otherwise, it can't move there return False return True
def canTakeEnPassant(self, row, col): if not board.isEmpty(row, col): return False # check the position of the pawn and the next position if self.notation[0] == "w": if row != self.row - 1 or abs(col - self.col) != 1: return False if board.isEmpty(self.row, col) or board.getPiece( self.row, col).notation != "bp" or not board.getPiece( self.row, col).hasJustMovedTwo: return False else: if row != self.row + 1 or abs(col - self.col) != 1: return False if board.isEmpty(self.row, col) or board.getPiece( self.row, col).notation != "wp" or not board.getPiece( self.row, col).hasJustMovedTwo: return False otherPawn = board.getPiece(self.row, col) currentRow = self.row currentCol = self.col board.board[self.row][col] = None board.board[row][col] = self self.row = row self.col = col valid = True # if the move puts the pawn's king in check, it's not valid if (self.notation[0] == "w" and whiteKing.isInCheck(whiteKing.row, whiteKing.col) or self.notation[0] == "b" and blackKing.isInCheck(blackKing.row, blackKing.col)): valid = False self.row = currentRow self.col = currentCol board.board[self.row][self.col] = self board.board[row][col] = None board.board[self.row][col] = otherPawn return valid
def canMove(self, newRow, newCol): if (newRow - self.row, newCol - self.col) not in self.moves: return False if (not board.isEmpty(newRow, newCol)) and board.getPiece( newRow, newCol).notation[0] != self.notation[0]: return True if board.isEmpty(newRow, newCol): return True return False
def canMove(self, newRow, newCol): # if it doesn't move in an L shape, the move isn't valid if (newRow - self.row, newCol - self.col) not in self.moves: return False #if there is a piece on that square, it can capture it if it's of a different color, or otherwise it can't move there if not board.isEmpty(newRow, newCol): if board.getPiece(newRow, newCol).notation[0] != self.notation[0]: return True else: return False return True
def updateBoard(self, newRow, newCol): global fiftyMove self.hasMoved = True # increment the fifty-move rule counter or reset it if a pawn moves or a piece is captured if board.getPiece(newRow, newCol) is not None or self.notation[1] == "p": fiftyMove = 0 board.positionsDict.clear() else: fiftyMove += 1 board.board[newRow][newCol] = self board.board[self.row][self.col] = None self.row = newRow self.col = newCol return True
def canMove(self, newRow, newCol): # if the piece can't attack nor move there, it is not valid if ((newRow - self.row, newCol - self.col) not in self.moves) and ( (newRow - self.row, newCol - self.col) not in self.attackMoves): return False # if that square is not empty if not board.isEmpty(newRow, newCol): # the pawn can capture the piece on that square if the square is in the attackMoves set # and the piece is not of the same color if board.getPiece(newRow, newCol).notation[0] != self.notation[0]: if (newRow - self.row, newCol - self.col) in self.attackMoves: return True # otherwise, the piece is of the same color as this pawn or the pawn is just not allowed to go there return False # if it is in the moves set if (newRow - self.row, newCol - self.col) in self.moves: # after the first move, the 2-square move is not valid anymore if self.notation[0] == "w" and self.col == 6: self.moves = {(-1, 0)} elif self.notation[0] == "b" and self.col == 1: self.moves = {(1, 0)} # if the pawn wants to move 2 squares, there have to be no pieces between those squares if abs(self.row - newRow) == 2: if (self.notation[0] == "w" and not board.isEmpty(self.row - 1, self.col) or self.notation[0] == "b" and not board.isEmpty(self.row + 1, self.col)): return False else: self.hasJustMovedTwo = True return True return False
def gameLoop(): global done global turn done = False turn = "w" pieces.fiftyMove = 0 pieces.initGame() currentBoard = board.getString(turn) board.positionsDict[currentBoard] = 1 while not done: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() # if the user presses the mouse if event.type == pygame.MOUSEBUTTONDOWN: # get the row and column the mouse is located in currentRow = math.floor(event.pos[1] / int(height / 8)) currentCol = math.floor(event.pos[0] / int(width / 8)) # if there is a piece that the user clicks on and it is its turn, make it the current piece piece = board.getPiece(currentRow, currentCol) if piece is not None and turn == piece.notation[0]: pieces.currentPiece = piece # if the user releases the mouse, try to move the piece and switch the turn if the move is successful if event.type == pygame.MOUSEBUTTONUP and pieces.currentPiece is not None: nextRow = math.floor(event.pos[1] / int(height / 8)) nextCol = math.floor(event.pos[0] / int(width / 8)) if pieces.currentPiece.move(nextRow, nextCol): # promotion of a pawn if pieces.currentPiece.notation[ 1] == "p" and pieces.currentPiece.row in [0, 7]: promote(event.pos, pieces.currentPiece.row, pieces.currentPiece.col, turn) if turn == "w": turn = "b" else: turn = "w" # remove the hasJustMovedTwo attribute if the enemy turn passed and they can't capture the pawn en passant anymore for r in board.board: for piece in r: if piece is None or piece.notation != (turn + "p"): continue piece.hasJustMovedTwo = False # update the positions dictionary currentBoard = board.getString(turn) if currentBoard in board.positionsDict: board.positionsDict[currentBoard] += 1 else: board.positionsDict[currentBoard] = 1 # the current piece should be none if the mouse is released pieces.currentPiece = None # draw the squares of the board colors = [(232, 235, 239), (125, 135, 150)] for i in range(0, 8): for j in range(0, 8): # change the color based on the col+row mod 2 pygame.draw.rect(screen, colors[(i + j) % 2], (i * int(width / 8), j * int(height / 8), int(width / 8), int(height / 8))) # highlight the square of the king with red if it is in check for king in [pieces.whiteKing, pieces.blackKing]: if king.isInCheck(king.row, king.col): pygame.draw.rect( screen, (255, 0, 0), (king.col * int(width / 8), king.row * int(height / 8), int(width / 8), int(height / 8))) # display the pieces in their respective squares for row in board.board: for piece in row: if piece is None: continue if pieces.currentPiece != piece: piece.draw(screen, width, height) # display the current piece at the mouse location if pieces.currentPiece is not None: screen.blit(pieces.currentPiece.img, pieces.currentPiece.imgRect) pieces.currentPiece.imgRect.center = pygame.mouse.get_pos() checkEndOfGame(currentBoard) pygame.display.flip() pygame.display.update()