diff --git a/src/components/Arbiter/Arbiter.css b/src/components/Arbiter/Arbiter.css new file mode 100644 index 0000000..f14ac82 --- /dev/null +++ b/src/components/Arbiter/Arbiter.css @@ -0,0 +1,56 @@ +.move-history-list{ + position: absolute; + top:40px; + right: 40px; + color: white; + background-color: rgba(0, 0, 0, 0.7); + padding: 15px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); +} + +.move-history-list table { + border-collapse: collapse; +} + +.move-history-list th, +.move-history-list td { + padding: 5px 10px; + text-align: center; + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.move-history-list th { + background-color: rgba(255, 255, 255, 0.1); +} + +.move-history{ + position: absolute; + right:40px; + background-color: rgba(0, 0, 0, 0.7); + padding: 10px; + border-radius: 8px; + display: flex; + gap: 10px; + align-items: center; +} + +.move-history button { + background-color: #4a4a4a; + color: white; + border: none; + padding: 5px 15px; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; +} + +.move-history button:hover { + background-color: #666; +} + +.move-history button:disabled { + background-color: #333; + cursor: not-allowed; + opacity: 0.6; +} diff --git a/src/components/Arbiter/Arbiter.tsx b/src/components/Arbiter/Arbiter.tsx index 394f237..bc2621f 100644 --- a/src/components/Arbiter/Arbiter.tsx +++ b/src/components/Arbiter/Arbiter.tsx @@ -4,11 +4,18 @@ import { initialBoard } from '../../Constants' import { PieceType, TeamType } from '../../Types' import { Piece, Position } from '../../models' import { useRef, useState } from 'react' - +import "./Arbiter.css" export default function Arbiter() { // Declaring the constants const [board, setBoard] = useState(initialBoard.clone()) + const [boardHistory, setBoardHistory] = useState([initialBoard.clone()]) const [promotionPawn, setPromotionPawn] = useState() + const [moveHistory, setMoveHistory] = useState<{ [key in TeamType]: string[] }>({ + [TeamType.RED]: [], + [TeamType.BLUE]: [], + [TeamType.YELLOW]: [], + [TeamType.GREEN]: [], + }); const modalRef = useRef(null) const checkmateModalRef = useRef(null) @@ -17,6 +24,9 @@ export default function Arbiter() { // Function for playing a move function playMove(playedPiece: Piece, destination: Position): boolean { + if(board.totalTurns !== boardHistory.length - 1) { + return false; + } // Checking if the correct team has played the piece if (playedPiece.team !== board.currentTeam) return false @@ -30,10 +40,32 @@ export default function Arbiter() { // playMove modifies the board state setBoard((board) => { const clonedBoard = board.clone() - // Playing a move clonedBoard.playMove(playedPiece, destination) + setBoardHistory([...boardHistory, clonedBoard]) + //getting the algebraic notation of the move + let move = getAlgebraicNotation(playedPiece, destination) + //checking if the move leads to check + let chk=false; + const kings=clonedBoard.pieces.filter((p) => p.isKing && p.team !== playedPiece.team) + const currentTeamPieces = clonedBoard.pieces.filter((p) => p.team === playedPiece.team) + currentTeamPieces.forEach((piece) => { + const moves = clonedBoard.getValidMoves(piece,clonedBoard.pieces) + if(moves.some((m) => kings.some((k) => k.samePosition(m)))) { + chk=true; + } + }) + if(chk){ + move = `${move}+` + } + + // Update move history for the current team + setMoveHistory((prevHistory) => ({ + ...prevHistory, + [playedPiece.team]: [...prevHistory[playedPiece.team], move], + })); + //checking if the game is over if (clonedBoard.gameOver) { checkmateModalRef.current?.classList.remove('hidden') } @@ -59,8 +91,46 @@ export default function Arbiter() { } return true } - + function numberToLetter(num: number): string { + // ASCII code for 'a' is 97, so we add (num - 1) to 97 + return String.fromCharCode(97 + num); + } // Function to promote a pawn to the desired piece + function getAlgebraicNotation(piece: Piece,destination: Position): string { + + if(piece.isPawn){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + return `${numberToLetter(destination.x)}${chk === true?'x':''}${destination.y}` + } + else if(piece.isKing){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + const dist=Math.abs(destination.x-piece.position.x)+Math.abs(destination.y-piece.position.y) + if(dist===3){ + return 'O-O' + } + else if(dist===4){ + return 'O-O-O' + } + return `K${chk === true?'x':''}${numberToLetter(destination.x)}${destination.y}` + } + else if(piece.isQueen){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + return `Q${chk === true?'x':''}${numberToLetter(destination.x)}${destination.y}` + } + else if(piece.isRook){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + return `R${chk === true?'x':''}${numberToLetter(destination.x)}${destination.y}` + } + else if(piece.isBishop){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + return `B${chk === true?'x':''}${numberToLetter(destination.x)}${destination.y}` + } + else if(piece.isKnight){ + let chk = board.pieces.some((piece)=>piece.samePosition(destination)) + return `N${chk === true?'x':''}${numberToLetter(destination.x)}${destination.y}` + } + return "#" + } function promotePawn(pieceType: PieceType) { if (promotionPawn === undefined) { return @@ -85,7 +155,27 @@ export default function Arbiter() { return clonedBoard }) - + + //updating the move history when pawn is promoted + let promotionNotation=""; + if(pieceType===PieceType.ROOK){ + promotionNotation='=R' + } + else if(pieceType===PieceType.KNIGHT){ + promotionNotation='=N' + } + else if(pieceType===PieceType.BISHOP){ + promotionNotation='=B' + } + else if(pieceType===PieceType.QUEEN){ + promotionNotation='=Q' + } + let teamHistory =moveHistory[promotionPawn.team] + teamHistory[teamHistory.length-1]=teamHistory[teamHistory.length-1]+promotionNotation + setMoveHistory((prevHistory) => ({ + ...prevHistory, + [promotionPawn.team]: [...teamHistory], + })); // Toggling the modal modalRef.current?.classList.add('hidden') } @@ -120,11 +210,21 @@ export default function Arbiter() { N: 'Knight', P: 'Pawn', } - + function gotToMove(moveNumber:number){ + if(moveNumber >= 0 && moveNumber +
+ + Move {board.totalTurns} of {boardHistory.length - 1} + +
+
+ + + + + + + + + + + {Array.from({ length: Math.max(...Object.values(moveHistory).map(moves => moves.length)) }).map((_, index) => ( + + + + + + + ))} + +
RedBlueYellowGreen
{moveHistory[TeamType.RED][index] || ''}{moveHistory[TeamType.BLUE][index] || ''}{moveHistory[TeamType.YELLOW][index] || ''}{moveHistory[TeamType.GREEN][index] || ''}
+
) } diff --git a/src/components/Chessboard/Chessboard.css b/src/components/Chessboard/Chessboard.css index 776b6e4..706d033 100644 --- a/src/components/Chessboard/Chessboard.css +++ b/src/components/Chessboard/Chessboard.css @@ -26,7 +26,7 @@ transform: translateY(-50%); left: calc(50% - 350px); display: flex; - align-items: center; + justify-content: space-around; width: 700px; background-color: rgba(0, 0, 0, 0.75); diff --git a/src/models/Board.ts b/src/models/Board.ts index 814d4c1..65977c5 100644 --- a/src/models/Board.ts +++ b/src/models/Board.ts @@ -153,7 +153,6 @@ export class Board { } } } - get isChecked() { const simulatedBoard = this.clone() const king = simulatedBoard.pieces.find( @@ -168,7 +167,6 @@ export class Board { .some((m) => m.samePosition(king.position)) ) } - // Getting the valid moves of the pieces which is being played getValidMoves(piece: Piece, boardState: Piece[]): Position[] { switch (piece.type) { diff --git a/src/models/Piece.ts b/src/models/Piece.ts index 780c3de..b15c39d 100644 --- a/src/models/Piece.ts +++ b/src/models/Piece.ts @@ -39,6 +39,17 @@ export class Piece { return this.type === PieceType.KING } + get isQueen(): boolean { + return this.type === PieceType.QUEEN + } + + get isBishop(): boolean { + return this.type === PieceType.BISHOP + } + get isKnight(): boolean { + return this.type === PieceType.KNIGHT + } + // function for same piece position samePiecePosition(otherPiece: Piece): boolean { return this.position.samePosition(otherPiece.position)