diff --git a/Python/Two Player Chess/Images/Highlight.jpg b/Python/Two Player Chess/Images/Highlight.jpg new file mode 100644 index 0000000..6b920cf Binary files /dev/null and b/Python/Two Player Chess/Images/Highlight.jpg differ diff --git a/Python/Two Player Chess/Images/PromotionMenu.jpg b/Python/Two Player Chess/Images/PromotionMenu.jpg new file mode 100644 index 0000000..f021a05 Binary files /dev/null and b/Python/Two Player Chess/Images/PromotionMenu.jpg differ diff --git a/Python/Two Player Chess/Images/bB.png b/Python/Two Player Chess/Images/bB.png new file mode 100644 index 0000000..453cb32 Binary files /dev/null and b/Python/Two Player Chess/Images/bB.png differ diff --git a/Python/Two Player Chess/Images/bK.png b/Python/Two Player Chess/Images/bK.png new file mode 100644 index 0000000..225f869 Binary files /dev/null and b/Python/Two Player Chess/Images/bK.png differ diff --git a/Python/Two Player Chess/Images/bN.png b/Python/Two Player Chess/Images/bN.png new file mode 100644 index 0000000..8e3d04e Binary files /dev/null and b/Python/Two Player Chess/Images/bN.png differ diff --git a/Python/Two Player Chess/Images/bQ.png b/Python/Two Player Chess/Images/bQ.png new file mode 100644 index 0000000..0d94a1c Binary files /dev/null and b/Python/Two Player Chess/Images/bQ.png differ diff --git a/Python/Two Player Chess/Images/bR.png b/Python/Two Player Chess/Images/bR.png new file mode 100644 index 0000000..b9748e8 Binary files /dev/null and b/Python/Two Player Chess/Images/bR.png differ diff --git a/Python/Two Player Chess/Images/bp.png b/Python/Two Player Chess/Images/bp.png new file mode 100644 index 0000000..c432d38 Binary files /dev/null and b/Python/Two Player Chess/Images/bp.png differ diff --git a/Python/Two Player Chess/Images/chess.jpg b/Python/Two Player Chess/Images/chess.jpg new file mode 100644 index 0000000..026f345 Binary files /dev/null and b/Python/Two Player Chess/Images/chess.jpg differ diff --git a/Python/Two Player Chess/Images/chess1.jpg b/Python/Two Player Chess/Images/chess1.jpg new file mode 100644 index 0000000..2c55305 Binary files /dev/null and b/Python/Two Player Chess/Images/chess1.jpg differ diff --git a/Python/Two Player Chess/Images/dtb.jpg b/Python/Two Player Chess/Images/dtb.jpg new file mode 100644 index 0000000..28cf524 Binary files /dev/null and b/Python/Two Player Chess/Images/dtb.jpg differ diff --git a/Python/Two Player Chess/Images/icon.png b/Python/Two Player Chess/Images/icon.png new file mode 100644 index 0000000..49a1748 Binary files /dev/null and b/Python/Two Player Chess/Images/icon.png differ diff --git a/Python/Two Player Chess/Images/logo1.jpg b/Python/Two Player Chess/Images/logo1.jpg new file mode 100644 index 0000000..dc8e890 Binary files /dev/null and b/Python/Two Player Chess/Images/logo1.jpg differ diff --git a/Python/Two Player Chess/Images/ltb.jpg b/Python/Two Player Chess/Images/ltb.jpg new file mode 100644 index 0000000..843c3b9 Binary files /dev/null and b/Python/Two Player Chess/Images/ltb.jpg differ diff --git a/Python/Two Player Chess/Images/royal.jpg b/Python/Two Player Chess/Images/royal.jpg new file mode 100644 index 0000000..164245b Binary files /dev/null and b/Python/Two Player Chess/Images/royal.jpg differ diff --git a/Python/Two Player Chess/Images/wB.png b/Python/Two Player Chess/Images/wB.png new file mode 100644 index 0000000..26dae01 Binary files /dev/null and b/Python/Two Player Chess/Images/wB.png differ diff --git a/Python/Two Player Chess/Images/wK.png b/Python/Two Player Chess/Images/wK.png new file mode 100644 index 0000000..d734164 Binary files /dev/null and b/Python/Two Player Chess/Images/wK.png differ diff --git a/Python/Two Player Chess/Images/wN.png b/Python/Two Player Chess/Images/wN.png new file mode 100644 index 0000000..2d716b1 Binary files /dev/null and b/Python/Two Player Chess/Images/wN.png differ diff --git a/Python/Two Player Chess/Images/wQ.png b/Python/Two Player Chess/Images/wQ.png new file mode 100644 index 0000000..a4fe68c Binary files /dev/null and b/Python/Two Player Chess/Images/wQ.png differ diff --git a/Python/Two Player Chess/Images/wR.png b/Python/Two Player Chess/Images/wR.png new file mode 100644 index 0000000..a805de4 Binary files /dev/null and b/Python/Two Player Chess/Images/wR.png differ diff --git a/Python/Two Player Chess/Images/wp.png b/Python/Two Player Chess/Images/wp.png new file mode 100644 index 0000000..e98fae2 Binary files /dev/null and b/Python/Two Player Chess/Images/wp.png differ diff --git a/Python/Two Player Chess/README.md b/Python/Two Player Chess/README.md new file mode 100644 index 0000000..15fca86 --- /dev/null +++ b/Python/Two Player Chess/README.md @@ -0,0 +1,19 @@ +# Two Player Chess Game +## Description +This is a two player chess game that can be played in the pygame window. The game is played by clicking on the piece you want to move and then clicking on the square you want to move it to. The game will not allow you to make an illegal move. The game will also not allow you to make a move that will put your king in check. The game will also not allow you to make a move that will put your king in checkmate. The game will also not allow you to make a move that will put your king in stalemate. + +## How to Run +To run the game, you must have python 3.7 or higher installed on your computer. You must also have pygame installed. To install pygame, run the following command in your terminal: +```bash +pip install pygame +``` +Once you have python and pygame installed, you can run the game by running the following command in your terminal: +```bash +python main.py +``` +## How to Play +To play the game, you must have two players. One player will be white and the other will be black. The white player will go first. To make a move, click on the piece you want to move and then click on the square you want to move it to. The game will not allow you to make an illegal move. The game will also not allow you to make a move that will put your king in check. The game will also not allow you to make a move that will put your king in checkmate. The game will also not allow you to make a move that will put your king in stalemate. The game will end when one player has put the other player in checkmate or stalemate. The game will also end if the players agree to a draw. To agree to a draw, both players must click on the draw button. The game will also end if one player resigns. To resign, click on the resign button. Once the game has ended, you can click on the reset button to reset the game. You can also click on the quit button to quit the game. + +# Requirements: +- Python 3.7 or higher +- Pygame diff --git a/Python/Two Player Chess/__pycache__/engine.cpython-310.pyc b/Python/Two Player Chess/__pycache__/engine.cpython-310.pyc new file mode 100644 index 0000000..ce3d250 Binary files /dev/null and b/Python/Two Player Chess/__pycache__/engine.cpython-310.pyc differ diff --git a/Python/Two Player Chess/engine.py b/Python/Two Player Chess/engine.py new file mode 100644 index 0000000..fba0bdf --- /dev/null +++ b/Python/Two Player Chess/engine.py @@ -0,0 +1,599 @@ + + +class gamestate(): + def __init__(self): + # 2D 8x8 list, each element has 2 characters. + # The first character represents the color of the piece + # The second character represents the type of the piece + # "--" represents an empty space with no piece + # "wp" represents a white pawn + # "bR" represents a black rook + # "bK" represents a black king + # "wQ" represents a white queen + # and so on + 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"]] + + # dictionary to map pieces to their respective move functions + # so that proper function is called for each piece + + self.moveFunctions = {'p':self.getPawnMoves,'R':self.getRookMoves,'N':self.getKnightMoves, + 'B':self.getBishopMoves,'Q':self.getQueenMoves,'K':self.getKingMoves} + + + # we dont have to write 6 different functions for each piece + + + + + self.whitemove=True + self.moveLog = [] + self.whiteKingLocation = (7,4) + self.blackKingLocation = (0,4) + self.checkmate = False + self.stalemate = False + self.enpassantPossible = () # coordinates for the square where en passant capture is possible + self.currentCastlingRights = castleRights(True,True,True,True) + self.castleRightsLog = [castleRights(self.currentCastlingRights.wks,self.currentCastlingRights.bks,self.currentCastlingRights.wqs,self.currentCastlingRights.bqs)] + + # pawn promotion is if white pawn reaches row 0 + # or if a black pawn reaches row 7 + + def makePawnPromotion(self,move,user_choice): + if move.pawn_promotion: + # place queen of same color at pawn's place + self.board[move.endRow][move.endCol] = move.pieceMoved[0] + user_choice + + + + + def makeMove(self,move): + # make the move and update the board + self.board[move.startRow][move.startCol] = "--" + self.board[move.endRow][move.endCol] = move.pieceMoved + self.moveLog.append(move) + # swap turns after move + self.whitemove = not self.whitemove + if move.pieceMoved == 'wK': + self.whiteKingLocation = (move.endRow,move.endCol) + if move.pieceMoved == 'bK': + self.blackKingLocation = (move.endRow,move.endCol) + + if move.enpassantPossible: + self.board[move.startRow][move.endCol] = "--" + # capturing the pawn + #update enpassantPossible variable + + # only if the pawn moves two squares ahead + # used abs so that it works for both white and black pawns + # both up the board and down the board + if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2: + self.enpassantPossible = ((move.startRow + move.endRow)//2,move.startCol) + else: + # reset enpassantPossible + self.enpassantPossible = () + + # updateCastleRights(move) + + + if move.isCastleMove: + if move.endCol - move.startCol == 2: + # king side castle move + self.board[move.endRow][move.endCol-1] = self.board[move.endRow][move.endCol+1] + self.board[move.endRow][move.endCol+1] = "--" + else: + # queen side castle move + self.board[move.endRow][move.endCol+1] = self.board[move.endRow][move.endCol-2] + self.board[move.endRow][move.endCol-2] = "--" + + + # castling + # if king moves two squares to the right + # then rook moves one square to the left + # and vice versa + self.updateCastleRights(move) + self.castleRightsLog.append(castleRights(self.currentCastlingRights.wks,self.currentCastlingRights.bks,self.currentCastlingRights.wqs,self.currentCastlingRights.bqs)) + + # update casting rights whenever it is a rook or a king move + # if a rook or a king moves from its starting position + # then we have to update the castling rights + + + + + + + + + # pawn promotion + + + + + def undoMove(self): + # to make sure that there is a move to undo + if len(self.moveLog) != 0: + # pop returns and removes the last element from the list + move = self.moveLog.pop() + + self.board[move.startRow][move.startCol] = move.pieceMoved + # undoing the move + self.board[move.endRow][move.endCol] = move.pieceCaptured + # to make sure the piece captured is not empty + # switch turns back + self.whitemove = not self.whitemove + if move.pieceMoved == 'wK': + self.whiteKingLocation = (move.startRow,move.startCol) + if move.pieceMoved == 'bK': + self.blackKingLocation = (move.startRow,move.startCol) + + # undo enpassantPossible + if move.enpassantPossible: + self.board[move.endRow][move.endCol] = "--" + # leave the landing square blank + self.board[move.startRow][move.endCol] = move.pieceCaptured + # redo the enpassant capture + # if i undo the move, i have to set the enpassantPossible to the square where the enpassant capture was possible + self.enpassantPossible = (move.endRow,move.endCol) + # undo 2 square pawn advance + if move.pieceMoved[1] == 'p' and abs(move.startRow - move.endRow) == 2: + self.enpassantPossible = () + self.castleRightsLog.pop() + self.currentCastlingRights = self.castleRightsLog[-1] + self.currentCastlingRights = castleRights(self.currentCastlingRights.wks,self.currentCastlingRights.bks,self.currentCastlingRights.wqs,self.currentCastlingRights.bqs) + # undo castling rights + # if a rook or a king moves from its starting position + # then we have to update the castling rights + # if a rook or a king moves from its starting position + # then we have to update the castling rights + if move.isCastleMove: + if move.endCol - move.startCol == 2: + # king side castle move + self.board[move.endRow][move.endCol+1] = self.board[move.endRow][move.endCol-1] + self.board[move.endRow][move.endCol-1] = "--" + else: + # queen side castle move + self.board[move.endRow][move.endCol-2] = self.board[move.endRow][move.endCol+1] + self.board[move.endRow][move.endCol+1] = "--" + self.checkmate = False + self.stalemate = False + + def updateCastleRights(self,move): + if move.pieceMoved == 'wK': + self.currentCastlingRights.wks = False + self.currentCastlingRights.wqs = False + elif move.pieceMoved == 'bK': + self.currentCastlingRights.bks = False + self.currentCastlingRights.bqs = False + elif move.pieceMoved == 'wR': + if move.startRow == 7: + if move.startCol == 0: + self.currentCastlingRights.wqs = False + elif move.startCol == 7: + self.currentCastlingRights.wks = False + elif move.pieceMoved == 'bR': + if move.startRow == 0: + if move.startCol == 0: + self.currentCastlingRights.bqs = False + elif move.startCol == 7: + self.currentCastlingRights.bks = False + def getvalidmoves(self): + for log in self.moveLog: + print(log.getChessNotation()) + # to store a copy of the enpassantPossible variable + temp_enpassantPossible = self.enpassantPossible + tempCastleRights = castleRights(self.currentCastlingRights.wks,self.currentCastlingRights.bks,self.currentCastlingRights.wqs,self.currentCastlingRights.bqs) + # 1. generate all possible moves + moves = self.getAllPossibleMoves() + + # 2. for each move, make the move + + if self.whitemove: + self.getCastleMoves(self.whiteKingLocation[0],self.whiteKingLocation[1],moves) + else: + self.getCastleMoves(self.blackKingLocation[0],self.blackKingLocation[1],moves) + + + # while removing an element from a list, we have to traverse the list backwards + # because the indexes change after removing the element + + + + for i in range(len(moves)-1,-1,-1): + + # make move + self.makeMove(moves[i]) + + # 3. generate all possible moves for the opponent + # 4. for each of your opponent's move, see if they attack your king + self.whitemove = not self.whitemove + if self.inCheck(): + moves.remove(moves[i]) + self.whitemove = not self.whitemove + self.undoMove() + + + # 3. generate all possible moves for the opponent + # 4. for each of your opponent's move, see if they attack your king + if len(moves) == 0: + if self.inCheck(): + self.checkmate = True + else: + self.stalemate = True + # if we undo the move, we have to set the checkmate and stalemate to false + else: + self.checkmate = False + self.stalemate = False + + # because we are not making any move + # we have to reset the enpassantPossible variable + # to its original value + + self.enpassantPossible = temp_enpassantPossible + + + # reset + self.currentCastlingRights = tempCastleRights + + + return moves + + def inCheck(self): + if self.whitemove: + 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.whitemove = not self.whitemove + opp_moves = self.getAllPossibleMoves() + self.whitemove = not self.whitemove + for move in opp_moves: + if move.endRow == r and move.endCol == c: + return True + return False + + + + + + + # all moves without considering checks + def getAllPossibleMoves(self): + + # + + + + # empty list for storing all possible moves + poss_moves = [] + # loop through all the squares in the board using nested for loop + for rows in range(len(self.board)): + for columns in range(len(self.board[rows])): + + # checking the first char of pieces + # assigning moves to pieces according to their color + turn = self.board[rows][columns][0] + if (turn == 'w' and self.whitemove) or (turn == 'b' and not self.whitemove): + # if the piece is a pawn + # because each piece has its own set of rules + piece = self.board[rows][columns][1] + self.moveFunctions[piece](rows,columns,poss_moves) + # calls the proper function for each piece + + + + return poss_moves + + def getPawnMoves(self,rows,columns,poss_moves): + # if white pawn + # + if self.whitemove: + # if the square in front of the pawn is empty + if self.board[rows-1][columns] == "--": + # going a row ahead not diagonal so column reamins the same + poss_moves.append(Move((rows,columns),(rows-1,columns),self.board)) + + # if the pawn is in its starting position + # for pawns first move + # we can move two squares ahead + # so append move rows-2 + + # check two conditions for this + # 1. the square two squares ahead is empty + # pawn is at row 6 (its starting position) + if rows == 6 and self.board[rows-2][columns] == "--": + poss_moves.append(Move((rows,columns),(rows-2,columns),self.board)) + # mark this square by photo + + + + if columns-1 >= 0: + if self.board[rows-1][columns-1][0] == 'b': + poss_moves.append(Move((rows,columns),(rows-1,columns-1),self.board)) + elif (rows-1,columns-1) == self.enpassantPossible: + # if the square is the enpassant square + # then we can capture the pawn + # so we have to add the move + poss_moves.append(Move((rows,columns),(rows-1,columns-1),self.board,enpassantPossible = True)) + if columns+1 <= 7: + if self.board[rows-1][columns+1][0] == 'b': + poss_moves.append(Move((rows,columns),(rows-1,columns+1),self.board)) + elif (rows-1,columns+1) == self.enpassantPossible: + # if the square is the enpassant square + # then we can capture the pawn + # so we have to add the move + poss_moves.append(Move((rows,columns),(rows-1,columns+1),self.board,enpassantPossible = True)) + else: + if self.board[rows+1][columns] == "--": + poss_moves.append(Move((rows,columns),(rows+1,columns),self.board)) + if rows == 1 and self.board[rows+2][columns] == "--": + poss_moves.append(Move((rows,columns),(rows+2,columns),self.board)) + if columns-1 >= 0: + if self.board[rows+1][columns-1][0] == 'w': + poss_moves.append(Move((rows,columns),(rows+1,columns-1),self.board)) + elif (rows+1,columns-1) == self.enpassantPossible: + # if the square is the enpassant square + # then we can capture the pawn + # so we have to add the move + poss_moves.append(Move((rows,columns),(rows+1,columns-1),self.board,enpassantPossible = True)) + if columns+1 <= 7: + if self.board[rows+1][columns+1][0] == 'w': + poss_moves.append(Move((rows,columns),(rows+1,columns+1),self.board)) + elif (rows+1,columns+1) == self.enpassantPossible: + # if the square is the enpassant square + # then we can capture the pawn + # so we have to add the move + poss_moves.append(Move((rows,columns),(rows+1,columns+1),self.board,enpassantPossible = True)) + def getRookMoves(self,rows,columns,poss_moves): + + # to get the direction like vector without changing the value of the original tuple + # back row,back column,forward row,forward column + directions = ((-1,0),(0,-1),(1,0),(0,1)) + # white rook --> wR + # black rook --> bR + # so we can use the first character to check the color of the piece + # and the second character to check the type of the piece + # so we can use the second character to check the type of the piece + # so we can use the second character to check the type of the piece + enemy_color = "b" if self.whitemove else "w" + for d in directions: + for i in range(1,8): + + # direction into magnitude + endRow = rows + d[0]*i + + # kaha into kitne se + endCol = columns + d[1]*i + # if the square is on the board + + if 0 <= endRow < 8 and 0 <= endCol < 8: + # square is on the board + endPiece = self.board[endRow][endCol] + # if the square is empty + + # append the moves till the squares are empty + if endPiece == "--": + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + elif endPiece[0] == enemy_color: + # move to capture the piece + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + break + # cant go over the enemy piece + else: + # cant capture alsi + break + # cant go over your own piece + else: + # all squares are out of board + break + + + def getKnightMoves(self,rows,columns,poss_moves): + knight_moves = ((-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)) + ally_color = "w" if self.whitemove else "b" + for m in knight_moves: + endRow = rows + m[0] + endCol = columns + m[1] + if 0 <= endRow < 8 and 0 <= endCol < 8: + + endPiece = self.board[endRow][endCol] + if endPiece[0] != ally_color: + # enemy hai kya + # nahi na + # toh move + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + + + def getBishopMoves(self,rows,columns,poss_moves): + # bishop can move diagonally + # left diagonal down, left diagonal up, right diagonal down, right diagonal up + directions = ((-1,-1),(-1,1),(1,-1),(1,1)) + enemy_color = "b" if self.whitemove else "w" + for d in directions: + # maximum square limit + for i in range(1,8): + endRow = rows + d[0]*i + endCol = columns + d[1]*i + if 0 <= endRow < 8 and 0 <= endCol < 8: + endPiece = self.board[endRow][endCol] + if endPiece == "--": + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + elif endPiece[0] == enemy_color: + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + break + else: + break + else: + break + + + def getQueenMoves(self,rows,columns,poss_moves): + + self.getBishopMoves(rows,columns,poss_moves) + self.getRookMoves(rows,columns,poss_moves) + + def getKingMoves(self,rows,columns,poss_moves): + # all squares surrounding the king + directions = ((-1,-1),(-1,0),(-1,1),(0,-1),(0,1),(1,-1),(1,0),(1,1)) + + # but just one move + + ally_color = "w" if self.whitemove else "b" + + for d in directions: + # traversing for all possible moves + + endRow = rows + d[0] + endCol = columns + d[1] + + # filtering out the moves that are out of board + if 0 <= endRow < 8 and 0 <= endCol < 8: + + endPiece = self.board[endRow][endCol] + + # not running on a ally piece + if endPiece[0] != ally_color: + poss_moves.append(Move((rows,columns),(endRow,endCol),self.board)) + + # self.getCastleMoves(rows,columns,poss_moves) + + + def getCastleMoves(self,rows,columns,poss_moves): + if self.squareUnderAttack(rows,columns): + return + if (self.whitemove and self.currentCastlingRights.wks) or (not self.whitemove and self.currentCastlingRights.bks): + self.getKingSideCastleMoves(rows,columns,poss_moves) + if (self.whitemove and self.currentCastlingRights.wqs) or (not self.whitemove and self.currentCastlingRights.bqs): + self.getQueenSideCastleMoves(rows,columns,poss_moves) + + def getKingSideCastleMoves(self,rows,columns,poss_moves): + if self.board[rows][columns+1] == '--' and self.board[rows][columns+2] == '--': + if not self.squareUnderAttack(rows,columns+1) and not self.squareUnderAttack(rows,columns+2): + poss_moves.append(Move((rows,columns),(rows,columns+2),self.board,isCastleMove = True)) + + + def getQueenSideCastleMoves(self,rows,columns,poss_moves): + if self.board[rows][columns-1] == '--' and self.board[rows][columns-2] == '--' and self.board[rows][columns-3] == '--': + if not self.squareUnderAttack(rows,columns-1) and not self.squareUnderAttack(rows,columns-2): + poss_moves.append(Move((rows,columns),(rows,columns-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 + # white king side, black king side, white queen side, black queen side + + + + + + + + + + +class Move(): + # map position from rows and columns to ranks and files in chess + + + # so using dictionaries to map + ranksToRows = {"1":7,"2":6,"3":5,"4":4, + "5":3,"6":2,"7":1,"8":0} + + # reversing the above dictionary + rowsToRanks = {v:k for k,v in ranksToRows.items()} + + + # using for converting columns to files + 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()} + + + # fn with optional parameter + def __init__(self,start_sq,end_sq,board,enpassantPossible = False,isCastleMove = False): + # start_sq + # source + + # end_sq + # destination + + # board state passed to validate the move and store information about the move + # what piece was captured? --> information + + # for first tuple that is sq_selected in player_clicks + self.startRow = start_sq[0] + self.startCol = start_sq[1] + + # for second tuple that is sq_selected in player_clicks + self.endRow = end_sq[0] + self.endCol = end_sq[1] + + # refers to pos in board + self.pieceMoved = board[self.startRow][self.startCol] # piece moved + + # refers to pos in board + self.pieceCaptured = board[self.endRow][self.endCol] # piece captured + self.moveID = self.startRow*1000 + self.startCol*100 + self.endRow*10 + self.endCol + + # default value for flag + self.pawn_promotion = False + # conditions of location and piece for pawn promotion + if (self.pieceMoved == "wp" and self.endRow == 0) or (self.pieceMoved == "bp" and self.endRow == 7): + self.pawn_promotion = True + + # flag for en passant move + self.enpassantPossible = enpassantPossible + if self.enpassantPossible: + self.pieceCaptured = 'wp' if self.pieceMoved == 'bp' else 'bp' + + self.isCastleMove = isCastleMove + # if pawn is moving two squares ahead + # if self.pieceMoved[1] == 'p' and abs(self.startRow - self.endRow) == 2: + # self.isEnpassantMove = True + # if pawn is moving two squares ahead + # if the pawn moves two squares ahead + + + + + + # self.promotionChoice = "Q" + + # we could write pawn promotion flags in the getpawnmoves itself but we chose this because of less new code to be written here + print(self.moveID) + + + def __eq__(self, other): + # comparing this object to another object + + # to ensure that we are comparing two move objects and not some other class object + if isinstance(other,Move): + return self.moveID == other.moveID + return False + + def getChessNotation(self): + # you can add to make this like real chess notation + return self.pieceMoved + "to" + self.getRankFile(self.startRow,self.startCol) + self.getRankFile(self.endRow,self.endCol) + + def getRankFile(self,rows,columns): + return self.colsToFiles[columns] + self.rowsToRanks[rows] + + # castling + # king cannot move to a square that is under attack + # sqs clear + # sqs cannot be under attack + # king didnt move + # king not in check + # first move of king and rook \ No newline at end of file diff --git a/Python/Two Player Chess/main.py b/Python/Two Player Chess/main.py new file mode 100644 index 0000000..ef3b2a1 --- /dev/null +++ b/Python/Two Player Chess/main.py @@ -0,0 +1,326 @@ +import pygame as p +import engine + + +# square window for our game. +# can change screen size from here +screen_width = screen_height = 550 +screen_caption = "Two Player Chess" +icon = p.image.load(r"Images\icon.png") + +# rows and columns + +dimensions = 8 + +# making sqaures in the screen to display chess board boxes +sq_size = screen_height // dimensions + +fps = 30 +# to pass as an argument in clock.tick +# adjust if game become laggy + +images = {} + +def load_images(): + + # load all images once as it is cpu heavy task + pieces = ["wp", "wR", "wN", "wB", "wQ", "wK", "bp", "bR", "bN", "bB", "bQ", "bK"] + for piece in pieces: + image_path = r"Images" + "\\" + piece + ".png" + + images[piece] = p.transform.scale(p.image.load(image_path).convert_alpha(), (sq_size, sq_size)) + + # pygame.transform.scale to adjust the image + +def main(): + p.init() + + + + # os.system("welcome.mp3") + + + # setting screen with sizes + + # closing our face detection window + + + screen = p.display.set_mode((screen_width,screen_height), p.HWSURFACE | p.DOUBLEBUF) + + + + p.display.set_caption(screen_caption) + p.display.set_icon(icon) + p.display.update() + # clock object + clock = p.time.Clock() + # fps change karega to limit CPU in clock.tick(15) + + screen.fill(p.Color("white")) + # aise hi + + # creating a gamestate object joh ki constructor ko call karega apne + # dot operator to call gamestate() in engine + + gs = engine.gamestate() + + + # to store valid moves + valid_moves = gs.getvalidmoves() + + + # print(gs.board) + move_made = False + # to update valid moves only when a move is made + + # flag variable for when a move is made + # loading the images "once" + load_images() + + # running variable to check start and quit + running = True + # tuple to keep the last square selected + sq_selected = () + # no square is selected at start + # tuple: (row,col) + # playerClicks = [] + + # list to keep two inputs + player_clicks = [] + # keep track of player clicks (two tuples: [(6, 4), (4, 4)]) + + + done = True + + chess = p.transform.scale_by(p.image.load(r"Images\chess.jpg"),0.25) + screen.fill(p.Color("black")) + while done: + + screen.blit(chess,p.Rect(200-5*sq_size + 180,200-5*sq_size + 200,10,10)) + screen.blit(p.transform.scale_by(icon,0.5),p.Rect(470-5*sq_size+15,screen_height/2-270,10,10)) + showtext(screen, "Welcome to ChessAI", (screen_height/2 - 230,screen_height/2 - 10), 40) + showtext(screen, "Press any key to start the game", (screen_height/2 - 220,screen_height/2+50),25) + + + p.display.flip() + for event in p.event.get(): + if event.type == p.QUIT: + p.quit() + if event.type == p.KEYDOWN: + done = False + # showtext(screen, predicted_name + " is playing") + + + + + + # start of my gameloop + while running: + + # lets keep a for loop to get events + for event in p.event.get(): + # print(p.display.Info()) + # if the type of event is this + + if event.type == p.QUIT: + # to exit the whileloop + running = False + elif event.type == p.MOUSEBUTTONDOWN: + # mouse kaha h? + mouse_location = p.mouse.get_pos() # (x,y) location of mouse + # get x and y from list + column = mouse_location[0]//sq_size + row = mouse_location[1]//sq_size + + + # first click is select, second click is undo + if sq_selected == (row,column): + # user clicks same sqaure again + sq_selected = () # undo + player_clicks = [] + else: + # store the square selected by the user now + sq_selected = (row,column) + player_clicks.append(sq_selected) + # first time it will append to empty list then it appends to list[0] + + + # hume pata karna hai user ka first click hai ya second + if len(player_clicks)==2: + + # do clicks hogye toh bolenge make move + # so call the move class constructor + move = engine.Move(player_clicks[0],player_clicks[1],gs.board) + print(move.getChessNotation()) + + # player_clicks[0] is our source + # player_clicks[1] is our piece's destination + for i in range(len(valid_moves)): + + # only get valid move object + # so check for it + + if move == valid_moves[i]: + + gs.makeMove(valid_moves[i]) + user_choice = "Q" + while move.pawn_promotion: + p.display.set_caption("Choose a piece to promote to") + screen.fill(p.Color("black")) + screen.blit(p.transform.scale_by(p.image.load(r"Images\PromotionMenu.jpg"),0.2),p.Rect(200-sq_size,200-sq_size,10,10)) + showtext(screen, "Enter the corresponding character of the piece you want to promote to :", (200,200),12) + p.display.flip() + user_choice = "" + for event in p.event.get(): + if event.type == p.KEYDOWN: + if event.key == p.K_q: + gs.makePawnPromotion(move,"Q") + move.pawn_promotion=False + + elif event.key == p.K_r: + gs.makePawnPromotion(move,"R") + move.pawn_promotion=False + + elif event.key == p.K_b: + gs.makePawnPromotion(move,"B") + move.pawn_promotion=False + elif event.key == p.K_n: + gs.makePawnPromotion(move,"N") + move.pawn_promotion=False + else: + gs.makePawnPromotion(move,"Q") + move.pawn_promotion=False + p.display.set_caption("ChessAI") + + + + + # argument to makemove is generated by the engine + + + + move_made = True + + sq_selected = () # reset user clicks + player_clicks = [] + + + + # reset the user clicks after making the move each time + if not move_made: + player_clicks = [sq_selected] + + + #gs.makeMove(move) + # to make the move + + elif event.type == p.KEYDOWN: + if event.key == p.K_z: + gs.undoMove() + + move_made = True + # when the user undoes a move the valid moves change + # so change the flag variable to true + + # to update the valid moves + if move_made: + valid_moves = gs.getvalidmoves() + move_made = False + + + + # calling the draw boardand pieces fn + draw_game_state(screen,gs) + clock.tick(fps) + p.display.flip() + # to update the display + +# method to draw sqs on board and graphics of a current gamestate +def draw_game_state(screen,gs): + + # to draw squares on the board + drawboard(screen) + + #board-->pieces order ofc matter karega nhi toh pieces piche chip jayenge + + # to draw pieces + drawpieces(screen,gs.board) # board from engine gamestate ka object gs , isliye dot + +def drawboard(screen): + # lets draw squares + # white and grey alternate + # make list to store white and grey switch karna easy hoga + # colors = [p.Color("white"), p.Color("dark gray")] + images = [p.image.load(r"images\ltb.jpg").convert_alpha(),p.image.load(r"images\dtb.jpg").convert_alpha()] + + for rows in range(dimensions): + for columns in range(dimensions): + # [00,10,20,30,40,50,60,70] + # [01,11,21,31,41,51,61,71] + # [02,12,22,32,42,52,62,72] + # [03,13,23,33,43,53,63,73] + # [04,14,24,34,44,54,64,74] + # [05,15,25,35,45,55,65,75] + # [06,16,26,36,46,56,66,76] + # [07,17,27,37,47,57,67,77] + + # trend we see here is that if we add rows and columns + # dark sqaures are odd + # light sqaures are even + + # color = colors[(rows+columns)%2] + image = images[(rows+columns)%2] + # even --> colors[0] --> white + # odd --> colors[1] --> black + + # smpart + + # just draw rectangle (surface,color,) + custom_img = p.Surface((sq_size,sq_size)) + + screen.blit(image,p.Rect(columns*sq_size,rows*sq_size,sq_size,sq_size)) + + # p.draw.rect(screen, color, p.Rect(columns*sq_size,rows*sq_size, sq_size, sq_size)) + +def drawpieces(screen,board): + for rows in range(dimensions): + for columns in range(dimensions): + pieces = board[rows][columns] + if pieces != "--": + screen.blit(images[pieces],p.Rect(columns*sq_size,rows*sq_size,sq_size,sq_size)) + # accessing our gs.board multi dim list by using [][] + # to assign each square a piece + +# function to show a menu an ask the user the piece to promote to in pawn promotion + + + + + + + + +def showtext(screen,text,location,fontsize): + font = p.font.SysFont("Copperplate gothic", fontsize, True, False) + textObject = font.render(text, 0, p.Color('White')) + location1 = p.Rect(location, location) + # textLocation = p.Rect(0, 0, screen_width, screen_height).move(screen_width / 2 - textObject.get_width() / 2, screen_height / 2 - textObject.get_height() / 2) + # white = p.Color("black") + # screen.blit(white,p.rect(textLocation,textLocation,200,200)) + screen.blit(textObject, location1) + + + + + + + + +# if we import something in the main code we need to do this cause it wont run otherwise +# THIS CODE WE HAVE TO RUN AS THIS IS OUR MAIN CODE AND WE IMPORT OTHER MODULES IN THIS CODE +# SO WE WRITE THIS +# The if __name__ == "__main__": construct is used to +# ensure that a specific block of code only runs when the Python script is executed directly, +# not when it's imported as a module in another script. +if __name__=="__main__": + main() \ No newline at end of file