Pygame not animating sprites
Question:
I am making a chess program and I am working on the user UI. One of the things I am working on is the piece animation. I have two files: My chess Engine (irrelevant for this but I have provided just in case) and my main file. Here they are:
Main File:
import pygame as p
import ChessEngine
WIDTH = HEIGHT = 512
DIMENSION = 8
SQ_SIZE = HEIGHT//DIMENSION
MAX_FPS = 15
IMAGES = {}
def loadImages():
pieces = ['wp', 'wR', 'wN', 'wB', 'wK',
'wQ', 'bp', 'bR', 'bN', 'bB', 'bK', 'bQ']
for piece in pieces:
IMAGES[piece] = p.transform.scale(
p.image.load('Chess/Images/' + piece + '.png'), (SQ_SIZE, SQ_SIZE))
def main():
p.init()
screen = p.display.set_mode((WIDTH, HEIGHT))
clock = p.time.Clock()
screen.fill(p.Color("white"))
gs = ChessEngine.GameState()
validMoves = gs.getValidMoves()
moveMade = False
loadImages()
running = True
sqSelected = ()
playerClicks = []
while running:
for e in p.event.get():
if e.type == p.QUIT:
running = False
elif e.type == p.MOUSEBUTTONDOWN:
location = p.mouse.get_pos()
col = location[0]//SQ_SIZE
row = location[1]//SQ_SIZE
if sqSelected == (row, col):
sqSelected = ()
playerClicks = []
else:
sqSelected = (row, col)
playerClicks.append(sqSelected)
if len(playerClicks) == 2:
move = ChessEngine.Move(
playerClicks[0], playerClicks[1], gs.board)
print(move.getChessNotation())
for i in range(len(validMoves)):
if move == validMoves[i]:
gs.makeMove(validMoves[i])
moveMade = True
sqSelected = ()
playerClicks = []
if not moveMade:
playerClicks = [sqSelected]
elif e.type == p.KEYDOWN:
if e.key == p.K_z:
gs.undoMove()
moveMade = True
if moveMade:
animateMove(gs.moveLog[-1], screen, gs.board, clock)
validMoves = gs.getValidMoves()
moveMade = False
drawGameState(screen, gs, validMoves, sqSelected)
clock.tick(MAX_FPS)
p.display.flip()
def highlightSquares(screen, gs, validMoves, sqSelected):
if sqSelected != ():
r, c = sqSelected
if gs.board[r][c][0] == ('w' if gs.whitetoMove else 'b'):
s = p.Surface((SQ_SIZE, SQ_SIZE))
s.set_alpha(100)
s.fill(p.Color('blue'))
screen.blit(s, (c*SQ_SIZE, r*SQ_SIZE))
s.fill(p.Color('yellow'))
for move in validMoves:
if move.startRow == r and move.startCol == c:
screen.blit(s, (SQ_SIZE*move.endCol, SQ_SIZE*move.endRow))
def drawGameState(screen, gs, validMoves, sqSelected):
drawBoard(screen)
highlightSquares(screen, gs, validMoves, sqSelected)
drawPieces(screen, gs.board)
def drawBoard(screen):
global colors
colors = [p.Color("white"), p.Color("gray")]
for r in range(DIMENSION):
for c in range(DIMENSION):
color = colors[((r+c) % 2)]
p.draw.rect(screen, color, p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
def drawPieces(screen, board):
for r in range(DIMENSION):
for c in range(DIMENSION):
piece = board[r][c]
if piece != "--":
screen.blit(IMAGES[piece], p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
def animateMove(move, screen, board, clock):
global colors
dR = move.endRow - move.startRow
dC = move.endCol - move.startCol
framesPerSquare = 10
frameCount = (abs(dR) + abs(dC)) * framesPerSquare
for frame in range(frameCount + 1):
r, c = (move.startRow + dR * frame / frameCount,
move.startCol + dC*frame / frameCount)
drawBoard(screen)
drawPieces(screen, board)
color = colors[(move.endRow + move.endCol) % 2]
endSquare = p.Rect(move.endCol*SQ_SIZE,
move.endRow*SQ_SIZE, SQ_SIZE, SQ_SIZE)
p.draw.rect(screen, color, endSquare)
if move.pieceCaptured != '--':
screen.blit(IMAGES[move.pieceCaptured], endSquare)
screen.blit(IMAGES[move.pieceMoved], p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
p.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
and my Chess Engine:
def __init__(self):
# 8x8 2d board, each element has 2 characters.
self.board = [
["bR", "bN", "bB", "bQ", "bK", "bB", "bN", "bR"],
["bp", "bp", "bp", "bp", "bp", "bp", "bp", "bp"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["wp", "wp", "wp", "wp", "wp", "wp", "wp", "wp"],
["wR", "wN", "wB", "wQ", "wK", "wB", "wN", "wR"]]
self.moveFunctions = {'p': self.getPawnMoves,
'R': self.getRookMoves, 'N': self.getKnightMoves, 'B': self.getBishopMoves, 'Q': self.getQueenMoves, 'K': self.getKingMoves}
self.whitetoMove = True
self.moveLog = []
self.whiteKingLocation = (7, 4)
self.blackKingLocation = (0, 4)
self.checkMate = False
self.staleMate = False
self.enpassantPossible = ()
self.currentCastlingRight = CastleRights(True, True, True, True)
self.castleRightsLog = [CastleRights(
self.currentCastlingRight.wks, self.currentCastlingRight.wqs, self.currentCastlingRight.bks, self.currentCastlingRight.bqs)]
def makeMove(self, move):
self.board[move.startRow][move.startCol] = "--"
self.board[move.endRow][move.endCol] = move.pieceMoved
self.moveLog.append(move)
self.whitetoMove = not self.whitetoMove
if move.pieceMoved == 'wK':
self.whiteKingLocation = (move.endRow, move.endCol)
elif move.pieceMoved == "bK":
self.blackKingLocation = (move.endRow, move.endCol)
if move.isPawnPromotion:
self.board[move.endRow][move.endCol] = move.pieceMoved[0] + 'Q'
if move.isEnpassantMove:
self.board[move.startRow][move.endCol] = '--'
if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2:
self.enpassantPossible = (
(move.startRow + move.endRow)//2, move.startCol)
else:
self.enpassantPossible = ()
if move.isCastleMove:
if move.endCol - move.startCol == 2:
self.board[move.endRow][move.endCol -
1] = self.board[move.endRow][move.endCol+1]
self.board[move.endRow][move.endCol+1] = '--'
else:
self.board[move.endRow][move.endCol +
1] = self.board[move.endRow][move.endCol-2]
self.board[move.endRow][move.endCol-2] = '--'
self.updateCastleRights(move)
self.castleRightsLog.append(CastleRights(
self.currentCastlingRight.wks, self.currentCastlingRight.wqs, self.currentCastlingRight.bks, self.currentCastlingRight.bqs))
def undoMove(self):
if len(self.moveLog) != 0:
move = self.moveLog.pop()
self.board[move.startRow][move.startCol] = move.pieceMoved
self.board[move.endRow][move.endCol] = move.pieceCaptured
self.whitetoMove = not self.whitetoMove
if move.pieceMoved == 'wK':
self.whiteKingLocation = (move.startRow, move.startCol)
elif move.pieceMoved == "bK":
self.blackKingLocation = (move.startRow, move.startCol)
if move.isEnpassantMove:
self.board[move.endRow][move.endCol] = '--'
self.board[move.startRow][move.endCol] = move.pieceCaptured
self.enpassantPossible = (move.endRow, move.endCol)
if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2:
self.enpassantPossible = ()
self.castleRightsLog.pop()
newRights = self.castleRightsLog[-1]
self.currentCastlingRight = CastleRights(
newRights.wks, newRights.bks, newRights.wqs, newRights.bqs)
if move.isCastleMove:
if move.endCol - move.startCol == 2:
self.board[move.endRow][move.endCol +
1] = self.board[move.endRow][move.endCol-1]
self.board[move.endRow][move.endCol-1] = '--'
else:
self.board[move.endRow][move.endCol -
2] = self.board[move.endRow][move.endCol+1]
self.board[move.endRow][move.endCol+1] = '--'
def updateCastleRights(self, move):
if move.pieceMoved == 'wK':
self.currentCastlingRight.wks = False
self.currentCastlingRight.wqs = False
elif move.pieceMoved == 'bK':
self.currentCastlingRight.bks = False
self.currentCastlingRight.bqs = False
elif move.pieceMoved == 'wR':
if move.startRow == 7:
if move.startCol == 0:
self.currentCastlingRight.wqs = False
elif move.startCol == 7:
self.currentCastlingRight.wks = False
elif move.pieceMoved == 'bR':
if move.startRow == 0:
if move.startCol == 0:
self.currentCastlingRight.bqs = False
elif move.startCol == 7:
self.currentCastlingRight.bks = False
def getValidMoves(self):
tempEnpassantPossible = self.enpassantPossible
tempCastleRights = CastleRights(self.currentCastlingRight.wks, self.currentCastlingRight.bks,
self.currentCastlingRight.wqs, self.currentCastlingRight.bqs)
moves = self.getAllPossibleMoves()
if self.whitetoMove:
self.getCastleMoves(
self.whiteKingLocation[0], self.whiteKingLocation[1], moves)
else:
self.getCastleMoves(
self.blackKingLocation[0], self.blackKingLocation[1], moves)
for i in range(len(moves)-1, -1, -1):
self.makeMove(moves[i])
self.whitetoMove = not self.whitetoMove
if self.inCheck():
moves.remove(moves[i])
self.whitetoMove = not self.whitetoMove
self.undoMove()
if len(moves) == 0:
if self.inCheck():
self.checkMate = True
else:
self.staleMate = True
else:
self.checkMate = False
self.staleMate = False
self.enpassantPossible = tempEnpassantPossible
self.currentCastlingRight = tempCastleRights
return moves
def inCheck(self):
if self.whitetoMove:
return self.squareUnderAttack(self.whiteKingLocation[0], self.whiteKingLocation[1])
else:
return self.squareUnderAttack(self.blackKingLocation[0], self.blackKingLocation[1])
def squareUnderAttack(self, r, c):
self.whitetoMove = not self.whitetoMove
oppMoves = self.getAllPossibleMoves()
self.whitetoMove = not self.whitetoMove
for move in oppMoves:
if move.endRow == r and move.endCol == c:
return True
return False
def getAllPossibleMoves(self):
moves = []
for r in range(len(self.board)):
for c in range(len(self.board[r])):
turn = self.board[r][c][0]
if (turn == 'w' and self.whitetoMove) or (turn == 'b' and not self.whitetoMove):
piece = self.board[r][c][1]
self.moveFunctions[piece](r, c, moves)
return moves
def getPawnMoves(self, r, c, moves):
if self.whitetoMove:
if self.board[r-1][c] == "--":
moves.append(Move((r, c), (r-1, c), self.board))
if r == 6 and self.board[r-2][c] == "--":
moves.append(Move((r, c), (r-2, c), self.board))
if c - 1 >= 0:
if self.board[r-1][c-1][0] == 'b':
moves.append(Move((r, c), (r-1, c-1), self.board))
elif (r - 1, c - 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r-1, c-1), self.board, enpassantPossible=True))
if c + 1 <= 7:
if self.board[r-1][c+1][0] == 'b':
moves.append(Move((r, c), (r-1, c+1), self.board))
elif (r - 1, c + 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r-1, c+1), self.board, enpassantPossible=True))
else:
if self.board[r+1][c] == "--":
moves.append(Move((r, c), (r+1, c), self.board))
if r == 1 and self.board[r+2][c] == '--':
moves.append(Move((r, c), (r+2, c), self.board))
if c-1 >= 0:
if self.board[r+1][c-1][0] == 'w':
moves.append(Move((r, c), (r+1, c-1), self.board))
elif (r + 1, c - 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r+1, c-1), self.board, enpassantPossible=True))
if c+1 <= 7:
if self.board[r+1][c+1][0] == 'w':
moves.append(Move((r, c), (r+1, c+1), self.board))
elif (r + 1, c + 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r+1, c+1), self.board, enpassantPossible=True))
def getRookMoves(self, r, c, moves):
directions = ((-1, 0), (0, -1), (1, 0), (0, 1))
enemyColor = "b" if self.whitetoMove else "w"
for d in directions:
for i in range(1, 8):
endRow = r + d[0] * i
endCol = c + d[1] * i
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece == "--":
moves.append(
Move((r, c), (endRow, endCol), self.board))
elif endPiece[0] == enemyColor:
moves.append(
Move((r, c), (endRow, endCol), self.board))
break
else:
break
else:
break
def getKnightMoves(self, r, c, moves):
knightMoves = ((-2, -1), (-2, 1), (-1, -2), (-1, 2),
(1, -2), (1, 2), (2, -1), (2, 1))
allyColor = "w" if self.whitetoMove else "b"
for m in knightMoves:
endRow = r + m[0]
endCol = c + m[1]
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece[0] != allyColor:
moves.append(Move((r, c), (endRow, endCol), self.board))
def getBishopMoves(self, r, c, moves):
directions = ((-1, -1), (-1, 1), (1, -1), (1, 1))
enemyColor = "b" if self.whitetoMove else "w"
for d in directions:
for i in range(1, 8):
endRow = r + d[0] * i
endCol = c + d[1] * i
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece == "--":
moves.append(
Move((r, c), (endRow, endCol), self.board))
elif endPiece[0] == enemyColor:
moves.append(
Move((r, c), (endRow, endCol), self.board))
break
else:
break
else:
break
def getQueenMoves(self, r, c, moves):
self.getRookMoves(r, c, moves)
self.getBishopMoves(r, c, moves)
def getKingMoves(self, r, c, moves):
kingMoves = ((-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
allyColor = "w" if self.whitetoMove else "b"
for i in range(8):
endRow = r + kingMoves[i][0]
endCol = c + kingMoves[i][1]
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece[0] != allyColor:
moves.append(Move((r, c), (endRow, endCol), self.board))
def getCastleMoves(self, r, c, moves):
if self.squareUnderAttack(r, c):
return
if (self.whitetoMove and self.currentCastlingRight.wks) or (not self.whitetoMove and self.currentCastlingRight.bks):
self.getKingsideCastleMoves(r, c, moves)
if (self.whitetoMove and self.currentCastlingRight.wqs) or (not self.whitetoMove and self.currentCastlingRight.bqs):
self.getQueensideCastleMoves(r, c, moves)
def getKingsideCastleMoves(self, r, c, moves):
if self.board[r][c+1] == '--' and self.board[r][c+2] == '--':
if not self.squareUnderAttack(r, c+1) and not self.squareUnderAttack(r, c+2):
moves.append(
Move((r, c), (r, c+2), self.board, isCastleMove=True))
def getQueensideCastleMoves(self, r, c, moves):
if self.board[r][c-1] == '--' and self.board[r][c-2] == '--' and self.board[r][c-3] == '--':
if not self.squareUnderAttack(r, c-1) and not self.squareUnderAttack(r, c-2):
moves.append(
Move((r, c), (r, c-2), self.board, isCastleMove=True))
class CastleRights():
def __init__(self, wks, bks, wqs, bqs):
self.wks = wks
self.bks = bks
self.wqs = wqs
self.bqs = bqs
class Move():
ranksToRows = {"1": 7, "2": 6, "3": 5,
"4": 4, "5": 3, "6": 2, "7": 1, "8": 0}
rowsToRanks = {v: k for k, v in ranksToRows.items()}
filesToCols = {"a": 0, "b": 1, "c": 2,
"d": 3, "e": 4, "f": 5, "g": 6, "h": 7}
colsToFiles = {v: k for k, v in filesToCols.items()}
def __init__(self, startSq, endSq, board, enpassantPossible=False, isCastleMove=False):
self.startRow = startSq[0]
self.startCol = startSq[1]
self.endRow = endSq[0]
self.endCol = endSq[1]
self.pieceMoved = board[self.startRow][self.startCol]
self.pieceCaptured = board[self.endRow][self.endCol]
self.isPawnPromotion = (self.pieceMoved == 'wp' and self.endRow == 0) or (
self.pieceMoved == 'bp' and self.endRow == 7)
self.isEnpassantMove = enpassantPossible
if self.isEnpassantMove:
self.pieceCaptured = 'wp' if self.pieceMoved == 'bp' else 'bp'
self.isCastleMove = isCastleMove
self.moveID = self.startRow * 1000 +
self.startCol*100 + self.endRow*10 + self.endCol
def __eq__(self, other):
if isinstance(other, Move):
return self.moveID == other.moveID
return False
def getChessNotation(self):
return self.getRankFile(self.startRow, self.startCol) + self.getRankFile(self.endRow, self.endCol)
def getRankFile(self, r, c):
return self.colsToFiles[c] + self.rowsToRanks[r]
I am not getting an error, however I am not getting an animation either; just a delay.
I am on a Macbook Air, if that has anything to do with this.
Please help…
Answers:
You missed to handle the events in the animation loop. See pygame.event.get()
respectively pygame.event.pump()
:
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
For instance:
def animateMove(move, screen, board, clock):
# [...]
for frame in range(frameCount + 1):
pygame.event.pump()
# [...]
p.display.flip()
clock.tick(60)
I am making a chess program and I am working on the user UI. One of the things I am working on is the piece animation. I have two files: My chess Engine (irrelevant for this but I have provided just in case) and my main file. Here they are:
Main File:
import pygame as p
import ChessEngine
WIDTH = HEIGHT = 512
DIMENSION = 8
SQ_SIZE = HEIGHT//DIMENSION
MAX_FPS = 15
IMAGES = {}
def loadImages():
pieces = ['wp', 'wR', 'wN', 'wB', 'wK',
'wQ', 'bp', 'bR', 'bN', 'bB', 'bK', 'bQ']
for piece in pieces:
IMAGES[piece] = p.transform.scale(
p.image.load('Chess/Images/' + piece + '.png'), (SQ_SIZE, SQ_SIZE))
def main():
p.init()
screen = p.display.set_mode((WIDTH, HEIGHT))
clock = p.time.Clock()
screen.fill(p.Color("white"))
gs = ChessEngine.GameState()
validMoves = gs.getValidMoves()
moveMade = False
loadImages()
running = True
sqSelected = ()
playerClicks = []
while running:
for e in p.event.get():
if e.type == p.QUIT:
running = False
elif e.type == p.MOUSEBUTTONDOWN:
location = p.mouse.get_pos()
col = location[0]//SQ_SIZE
row = location[1]//SQ_SIZE
if sqSelected == (row, col):
sqSelected = ()
playerClicks = []
else:
sqSelected = (row, col)
playerClicks.append(sqSelected)
if len(playerClicks) == 2:
move = ChessEngine.Move(
playerClicks[0], playerClicks[1], gs.board)
print(move.getChessNotation())
for i in range(len(validMoves)):
if move == validMoves[i]:
gs.makeMove(validMoves[i])
moveMade = True
sqSelected = ()
playerClicks = []
if not moveMade:
playerClicks = [sqSelected]
elif e.type == p.KEYDOWN:
if e.key == p.K_z:
gs.undoMove()
moveMade = True
if moveMade:
animateMove(gs.moveLog[-1], screen, gs.board, clock)
validMoves = gs.getValidMoves()
moveMade = False
drawGameState(screen, gs, validMoves, sqSelected)
clock.tick(MAX_FPS)
p.display.flip()
def highlightSquares(screen, gs, validMoves, sqSelected):
if sqSelected != ():
r, c = sqSelected
if gs.board[r][c][0] == ('w' if gs.whitetoMove else 'b'):
s = p.Surface((SQ_SIZE, SQ_SIZE))
s.set_alpha(100)
s.fill(p.Color('blue'))
screen.blit(s, (c*SQ_SIZE, r*SQ_SIZE))
s.fill(p.Color('yellow'))
for move in validMoves:
if move.startRow == r and move.startCol == c:
screen.blit(s, (SQ_SIZE*move.endCol, SQ_SIZE*move.endRow))
def drawGameState(screen, gs, validMoves, sqSelected):
drawBoard(screen)
highlightSquares(screen, gs, validMoves, sqSelected)
drawPieces(screen, gs.board)
def drawBoard(screen):
global colors
colors = [p.Color("white"), p.Color("gray")]
for r in range(DIMENSION):
for c in range(DIMENSION):
color = colors[((r+c) % 2)]
p.draw.rect(screen, color, p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
def drawPieces(screen, board):
for r in range(DIMENSION):
for c in range(DIMENSION):
piece = board[r][c]
if piece != "--":
screen.blit(IMAGES[piece], p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
def animateMove(move, screen, board, clock):
global colors
dR = move.endRow - move.startRow
dC = move.endCol - move.startCol
framesPerSquare = 10
frameCount = (abs(dR) + abs(dC)) * framesPerSquare
for frame in range(frameCount + 1):
r, c = (move.startRow + dR * frame / frameCount,
move.startCol + dC*frame / frameCount)
drawBoard(screen)
drawPieces(screen, board)
color = colors[(move.endRow + move.endCol) % 2]
endSquare = p.Rect(move.endCol*SQ_SIZE,
move.endRow*SQ_SIZE, SQ_SIZE, SQ_SIZE)
p.draw.rect(screen, color, endSquare)
if move.pieceCaptured != '--':
screen.blit(IMAGES[move.pieceCaptured], endSquare)
screen.blit(IMAGES[move.pieceMoved], p.Rect(
c*SQ_SIZE, r*SQ_SIZE, SQ_SIZE, SQ_SIZE))
p.display.flip()
clock.tick(60)
if __name__ == '__main__':
main()
and my Chess Engine:
def __init__(self):
# 8x8 2d board, each element has 2 characters.
self.board = [
["bR", "bN", "bB", "bQ", "bK", "bB", "bN", "bR"],
["bp", "bp", "bp", "bp", "bp", "bp", "bp", "bp"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["--", "--", "--", "--", "--", "--", "--", "--"],
["wp", "wp", "wp", "wp", "wp", "wp", "wp", "wp"],
["wR", "wN", "wB", "wQ", "wK", "wB", "wN", "wR"]]
self.moveFunctions = {'p': self.getPawnMoves,
'R': self.getRookMoves, 'N': self.getKnightMoves, 'B': self.getBishopMoves, 'Q': self.getQueenMoves, 'K': self.getKingMoves}
self.whitetoMove = True
self.moveLog = []
self.whiteKingLocation = (7, 4)
self.blackKingLocation = (0, 4)
self.checkMate = False
self.staleMate = False
self.enpassantPossible = ()
self.currentCastlingRight = CastleRights(True, True, True, True)
self.castleRightsLog = [CastleRights(
self.currentCastlingRight.wks, self.currentCastlingRight.wqs, self.currentCastlingRight.bks, self.currentCastlingRight.bqs)]
def makeMove(self, move):
self.board[move.startRow][move.startCol] = "--"
self.board[move.endRow][move.endCol] = move.pieceMoved
self.moveLog.append(move)
self.whitetoMove = not self.whitetoMove
if move.pieceMoved == 'wK':
self.whiteKingLocation = (move.endRow, move.endCol)
elif move.pieceMoved == "bK":
self.blackKingLocation = (move.endRow, move.endCol)
if move.isPawnPromotion:
self.board[move.endRow][move.endCol] = move.pieceMoved[0] + 'Q'
if move.isEnpassantMove:
self.board[move.startRow][move.endCol] = '--'
if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2:
self.enpassantPossible = (
(move.startRow + move.endRow)//2, move.startCol)
else:
self.enpassantPossible = ()
if move.isCastleMove:
if move.endCol - move.startCol == 2:
self.board[move.endRow][move.endCol -
1] = self.board[move.endRow][move.endCol+1]
self.board[move.endRow][move.endCol+1] = '--'
else:
self.board[move.endRow][move.endCol +
1] = self.board[move.endRow][move.endCol-2]
self.board[move.endRow][move.endCol-2] = '--'
self.updateCastleRights(move)
self.castleRightsLog.append(CastleRights(
self.currentCastlingRight.wks, self.currentCastlingRight.wqs, self.currentCastlingRight.bks, self.currentCastlingRight.bqs))
def undoMove(self):
if len(self.moveLog) != 0:
move = self.moveLog.pop()
self.board[move.startRow][move.startCol] = move.pieceMoved
self.board[move.endRow][move.endCol] = move.pieceCaptured
self.whitetoMove = not self.whitetoMove
if move.pieceMoved == 'wK':
self.whiteKingLocation = (move.startRow, move.startCol)
elif move.pieceMoved == "bK":
self.blackKingLocation = (move.startRow, move.startCol)
if move.isEnpassantMove:
self.board[move.endRow][move.endCol] = '--'
self.board[move.startRow][move.endCol] = move.pieceCaptured
self.enpassantPossible = (move.endRow, move.endCol)
if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2:
self.enpassantPossible = ()
self.castleRightsLog.pop()
newRights = self.castleRightsLog[-1]
self.currentCastlingRight = CastleRights(
newRights.wks, newRights.bks, newRights.wqs, newRights.bqs)
if move.isCastleMove:
if move.endCol - move.startCol == 2:
self.board[move.endRow][move.endCol +
1] = self.board[move.endRow][move.endCol-1]
self.board[move.endRow][move.endCol-1] = '--'
else:
self.board[move.endRow][move.endCol -
2] = self.board[move.endRow][move.endCol+1]
self.board[move.endRow][move.endCol+1] = '--'
def updateCastleRights(self, move):
if move.pieceMoved == 'wK':
self.currentCastlingRight.wks = False
self.currentCastlingRight.wqs = False
elif move.pieceMoved == 'bK':
self.currentCastlingRight.bks = False
self.currentCastlingRight.bqs = False
elif move.pieceMoved == 'wR':
if move.startRow == 7:
if move.startCol == 0:
self.currentCastlingRight.wqs = False
elif move.startCol == 7:
self.currentCastlingRight.wks = False
elif move.pieceMoved == 'bR':
if move.startRow == 0:
if move.startCol == 0:
self.currentCastlingRight.bqs = False
elif move.startCol == 7:
self.currentCastlingRight.bks = False
def getValidMoves(self):
tempEnpassantPossible = self.enpassantPossible
tempCastleRights = CastleRights(self.currentCastlingRight.wks, self.currentCastlingRight.bks,
self.currentCastlingRight.wqs, self.currentCastlingRight.bqs)
moves = self.getAllPossibleMoves()
if self.whitetoMove:
self.getCastleMoves(
self.whiteKingLocation[0], self.whiteKingLocation[1], moves)
else:
self.getCastleMoves(
self.blackKingLocation[0], self.blackKingLocation[1], moves)
for i in range(len(moves)-1, -1, -1):
self.makeMove(moves[i])
self.whitetoMove = not self.whitetoMove
if self.inCheck():
moves.remove(moves[i])
self.whitetoMove = not self.whitetoMove
self.undoMove()
if len(moves) == 0:
if self.inCheck():
self.checkMate = True
else:
self.staleMate = True
else:
self.checkMate = False
self.staleMate = False
self.enpassantPossible = tempEnpassantPossible
self.currentCastlingRight = tempCastleRights
return moves
def inCheck(self):
if self.whitetoMove:
return self.squareUnderAttack(self.whiteKingLocation[0], self.whiteKingLocation[1])
else:
return self.squareUnderAttack(self.blackKingLocation[0], self.blackKingLocation[1])
def squareUnderAttack(self, r, c):
self.whitetoMove = not self.whitetoMove
oppMoves = self.getAllPossibleMoves()
self.whitetoMove = not self.whitetoMove
for move in oppMoves:
if move.endRow == r and move.endCol == c:
return True
return False
def getAllPossibleMoves(self):
moves = []
for r in range(len(self.board)):
for c in range(len(self.board[r])):
turn = self.board[r][c][0]
if (turn == 'w' and self.whitetoMove) or (turn == 'b' and not self.whitetoMove):
piece = self.board[r][c][1]
self.moveFunctions[piece](r, c, moves)
return moves
def getPawnMoves(self, r, c, moves):
if self.whitetoMove:
if self.board[r-1][c] == "--":
moves.append(Move((r, c), (r-1, c), self.board))
if r == 6 and self.board[r-2][c] == "--":
moves.append(Move((r, c), (r-2, c), self.board))
if c - 1 >= 0:
if self.board[r-1][c-1][0] == 'b':
moves.append(Move((r, c), (r-1, c-1), self.board))
elif (r - 1, c - 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r-1, c-1), self.board, enpassantPossible=True))
if c + 1 <= 7:
if self.board[r-1][c+1][0] == 'b':
moves.append(Move((r, c), (r-1, c+1), self.board))
elif (r - 1, c + 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r-1, c+1), self.board, enpassantPossible=True))
else:
if self.board[r+1][c] == "--":
moves.append(Move((r, c), (r+1, c), self.board))
if r == 1 and self.board[r+2][c] == '--':
moves.append(Move((r, c), (r+2, c), self.board))
if c-1 >= 0:
if self.board[r+1][c-1][0] == 'w':
moves.append(Move((r, c), (r+1, c-1), self.board))
elif (r + 1, c - 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r+1, c-1), self.board, enpassantPossible=True))
if c+1 <= 7:
if self.board[r+1][c+1][0] == 'w':
moves.append(Move((r, c), (r+1, c+1), self.board))
elif (r + 1, c + 1) == self.enpassantPossible:
moves.append(
Move((r, c), (r+1, c+1), self.board, enpassantPossible=True))
def getRookMoves(self, r, c, moves):
directions = ((-1, 0), (0, -1), (1, 0), (0, 1))
enemyColor = "b" if self.whitetoMove else "w"
for d in directions:
for i in range(1, 8):
endRow = r + d[0] * i
endCol = c + d[1] * i
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece == "--":
moves.append(
Move((r, c), (endRow, endCol), self.board))
elif endPiece[0] == enemyColor:
moves.append(
Move((r, c), (endRow, endCol), self.board))
break
else:
break
else:
break
def getKnightMoves(self, r, c, moves):
knightMoves = ((-2, -1), (-2, 1), (-1, -2), (-1, 2),
(1, -2), (1, 2), (2, -1), (2, 1))
allyColor = "w" if self.whitetoMove else "b"
for m in knightMoves:
endRow = r + m[0]
endCol = c + m[1]
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece[0] != allyColor:
moves.append(Move((r, c), (endRow, endCol), self.board))
def getBishopMoves(self, r, c, moves):
directions = ((-1, -1), (-1, 1), (1, -1), (1, 1))
enemyColor = "b" if self.whitetoMove else "w"
for d in directions:
for i in range(1, 8):
endRow = r + d[0] * i
endCol = c + d[1] * i
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece == "--":
moves.append(
Move((r, c), (endRow, endCol), self.board))
elif endPiece[0] == enemyColor:
moves.append(
Move((r, c), (endRow, endCol), self.board))
break
else:
break
else:
break
def getQueenMoves(self, r, c, moves):
self.getRookMoves(r, c, moves)
self.getBishopMoves(r, c, moves)
def getKingMoves(self, r, c, moves):
kingMoves = ((-1, -1), (-1, 0), (-1, 1),
(0, -1), (0, 1), (1, -1), (1, 0), (1, 1))
allyColor = "w" if self.whitetoMove else "b"
for i in range(8):
endRow = r + kingMoves[i][0]
endCol = c + kingMoves[i][1]
if 0 <= endRow < 8 and 0 <= endCol < 8:
endPiece = self.board[endRow][endCol]
if endPiece[0] != allyColor:
moves.append(Move((r, c), (endRow, endCol), self.board))
def getCastleMoves(self, r, c, moves):
if self.squareUnderAttack(r, c):
return
if (self.whitetoMove and self.currentCastlingRight.wks) or (not self.whitetoMove and self.currentCastlingRight.bks):
self.getKingsideCastleMoves(r, c, moves)
if (self.whitetoMove and self.currentCastlingRight.wqs) or (not self.whitetoMove and self.currentCastlingRight.bqs):
self.getQueensideCastleMoves(r, c, moves)
def getKingsideCastleMoves(self, r, c, moves):
if self.board[r][c+1] == '--' and self.board[r][c+2] == '--':
if not self.squareUnderAttack(r, c+1) and not self.squareUnderAttack(r, c+2):
moves.append(
Move((r, c), (r, c+2), self.board, isCastleMove=True))
def getQueensideCastleMoves(self, r, c, moves):
if self.board[r][c-1] == '--' and self.board[r][c-2] == '--' and self.board[r][c-3] == '--':
if not self.squareUnderAttack(r, c-1) and not self.squareUnderAttack(r, c-2):
moves.append(
Move((r, c), (r, c-2), self.board, isCastleMove=True))
class CastleRights():
def __init__(self, wks, bks, wqs, bqs):
self.wks = wks
self.bks = bks
self.wqs = wqs
self.bqs = bqs
class Move():
ranksToRows = {"1": 7, "2": 6, "3": 5,
"4": 4, "5": 3, "6": 2, "7": 1, "8": 0}
rowsToRanks = {v: k for k, v in ranksToRows.items()}
filesToCols = {"a": 0, "b": 1, "c": 2,
"d": 3, "e": 4, "f": 5, "g": 6, "h": 7}
colsToFiles = {v: k for k, v in filesToCols.items()}
def __init__(self, startSq, endSq, board, enpassantPossible=False, isCastleMove=False):
self.startRow = startSq[0]
self.startCol = startSq[1]
self.endRow = endSq[0]
self.endCol = endSq[1]
self.pieceMoved = board[self.startRow][self.startCol]
self.pieceCaptured = board[self.endRow][self.endCol]
self.isPawnPromotion = (self.pieceMoved == 'wp' and self.endRow == 0) or (
self.pieceMoved == 'bp' and self.endRow == 7)
self.isEnpassantMove = enpassantPossible
if self.isEnpassantMove:
self.pieceCaptured = 'wp' if self.pieceMoved == 'bp' else 'bp'
self.isCastleMove = isCastleMove
self.moveID = self.startRow * 1000 +
self.startCol*100 + self.endRow*10 + self.endCol
def __eq__(self, other):
if isinstance(other, Move):
return self.moveID == other.moveID
return False
def getChessNotation(self):
return self.getRankFile(self.startRow, self.startCol) + self.getRankFile(self.endRow, self.endCol)
def getRankFile(self, r, c):
return self.colsToFiles[c] + self.rowsToRanks[r]
I am not getting an error, however I am not getting an animation either; just a delay.
I am on a Macbook Air, if that has anything to do with this.
Please help…
You missed to handle the events in the animation loop. See pygame.event.get()
respectively pygame.event.pump()
:
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
For instance:
def animateMove(move, screen, board, clock):
# [...]
for frame in range(frameCount + 1):
pygame.event.pump()
# [...]
p.display.flip()
clock.tick(60)