diff --git a/backend/socket.js b/backend/socket.js index ab59063..341bb4c 100644 --- a/backend/socket.js +++ b/backend/socket.js @@ -79,7 +79,7 @@ function socketIOServerInit(server) { // io.to(roomID).emit("new user joined the room"); console.log(data, "joined"); let room = getRoom(roomID); - io.to(roomID).emit(USER_JOINED_ROOM, data.username); + socket.to(roomID).emit(USER_JOINED_ROOM, data.username); socket.emit(result, room.gameHistory); // room joined successfully } else { socket.emit(result); // room is full diff --git a/frontend/src/context/chess-game-context.jsx b/frontend/src/context/chess-game-context.jsx index 3df4393..7d9639a 100644 --- a/frontend/src/context/chess-game-context.jsx +++ b/frontend/src/context/chess-game-context.jsx @@ -12,7 +12,6 @@ const { GAME_END, CHESS_MOVE } = SOCKET_EVENTS; export const ChessGameContext = createContext(); const reducer = (state, action) => { - // console.log('Before', state); try { switch (action.type) { case SELECT_PIECE: @@ -78,12 +77,9 @@ const reducer = (state, action) => { return state; } } catch (err) { - console.log('Error', err); - console.log('After', state); - // state.chess.getBoard() + console.error(err); + return state; } - // console.log('After', state); - return state; } function chessGameStateInit(myColor) { @@ -107,6 +103,16 @@ const ChessGameContextProvider = ({ children }) => { const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason }, dispatch] = useReducer(reducer, myColor, chessGameStateInit); const [isTimerOn, setIsTimerOn] = useState(true); + const chessRef = useRef(chess); + const moveHintsRef = useRef(moveHints); + const selectedRef = useRef(selected); + const gameHistoryRef = useRef(gameHistory); + const currentIndexRef = useRef(currentIndex); + chessRef.current = chess; + selectedRef.current = selected; + moveHintsRef.current = moveHints; + gameHistoryRef.current = gameHistory; + currentIndexRef.current = currentIndex; const moveAudioRef = useRef(null); const captureAudioRef = useRef(null); @@ -116,38 +122,35 @@ const ChessGameContextProvider = ({ children }) => { // data received through socket function handleOpponentMove(data) { let { from, to } = data; - console.log(from, to, chess.get(to), chess.ascii()); - if (!chess.get(to)) { + if (!chessRef.current.get(to)) { dispatch({ type: MOVE_PIECE, val: { from, to } }); moveAudioRef.current.play(); - console.log(chess.ascii()) return; } else { dispatch({ type: CAPTURE_PIECE, val: { from, to } }); captureAudioRef.current.play(); - console.log(chess.ascii()) return; } } // called when user clicks a square function handleSquareClick(square, emitToSocketCallback) { - let { type, color } = chess.get(square); - let marked = moveHints.includes(square); + let { type, color } = chessRef.current.get(square); + let marked = moveHintsRef.current.includes(square); - if (chess.turn() === myColor) { + if (chessRef.current.turn() === myColor) { if (type && color === myColor) { return dispatch({ type: SELECT_PIECE, val: square }); } - if (!type && selected && marked) { + if (!type && selectedRef.current && marked) { dispatch({ type: MOVE_PIECE, val: { from: selected, to: square } }) - emitToSocketCallback({ from: selected, to: square }) + emitToSocketCallback({ from: selectedRef.current, to: square }) setIsTimerOn(false) captureAudioRef.current.play(); } if (type && marked) { - dispatch({ type: CAPTURE_PIECE, val: { from: selected, to: square } }) - emitToSocketCallback({ from: selected, to: square }) + dispatch({ type: CAPTURE_PIECE, val: { from: selectedRef.current, to: square } }) + emitToSocketCallback({ from: selectedRef.current, to: square }) setIsTimerOn(false); moveAudioRef.current.play(); } @@ -159,8 +162,8 @@ const ChessGameContextProvider = ({ children }) => { function handleDrop(moveData) { let { from, to } = moveData; // console.log(from, to, ch ess.get(to), chess.ascii()) - if (moveHints.includes(to)) { - if (chess.get(to)) { + if (moveHintsRef.current.includes(to)) { + if (chessRef.current.get(to)) { dispatch({ type: CAPTURE_PIECE, val: { from: from, to: to } }); // capture piece captureAudioRef.current.play(); // setIsTimerOn(false) @@ -175,17 +178,17 @@ const ChessGameContextProvider = ({ children }) => { } function selectPiece({ square, color: pieceColor }) { - if (pieceColor === myColor && myColor === chess.turn()) { + if (pieceColor === myColor && myColor === chessRef.current.turn()) { dispatch({ type: SELECT_PIECE, val: square }); } } function getSquareColor(square) { - return chess.squareColor(square) === 'light' ? "w" : "b"; + return chessRef.current.squareColor(square) === 'light' ? "w" : "b"; } function isSquareMarked(square) { - return moveHints.includes(square); + return moveHintsRef.current.includes(square); } function jumpTo(index) { @@ -193,24 +196,24 @@ const ChessGameContextProvider = ({ children }) => { } function getChessBoard() { - if (currentIndex === -1 || gameHistory.length === 0) { + if (currentIndexRef.current === -1 || gameHistoryRef.current.length === 0) { return new ChessModified().getBoard(); } else { // console.log(chess); - let currentChessBoard = new ChessModified(gameHistory[currentIndex].fen).getBoard(); + let currentChessBoard = new ChessModified(gameHistoryRef.current[currentIndexRef.current].fen).getBoard(); return currentChessBoard; } } function goBack() { - if (currentIndex > 0) { - jumpTo(currentIndex - 1); + if (currentIndexRef.current > 0) { + jumpTo(currentIndexRef.current - 1); } } function goAhead() { - if (currentIndex < gameHistory.length - 1) { - jumpTo(currentIndex + 1); + if (currentIndexRef.current < gameHistoryRef.current.length - 1) { + jumpTo(currentIndexRef.current + 1); } } diff --git a/frontend/src/hooks/useChess.jsx b/frontend/src/hooks/useChess.jsx new file mode 100644 index 0000000..9398a38 --- /dev/null +++ b/frontend/src/hooks/useChess.jsx @@ -0,0 +1,92 @@ +import { useReducer } from "react"; + +import { ChessModified, chessInit } from "../utils/chess" +import { DISPATCH_EVENTS } from "../constants"; + +const { SELECT_PIECE, MOVE_PIECE, CAPTURE_PIECE, SET_GAME_HISTORY } = DISPATCH_EVENTS + +let BLACK = 'b', WHITE = 'w'; + +function chessStateInit() { + let chess = chessInit(); + let moveHints = []; + let gameHistory = []; + let capturedPieces = { 'w': [], 'b': [] }; + let selected = null; + // represents which move is viewed in history + let currentHistoryIndex = -1; + let hasEnded = false; + + return { chess, moveHints, selected, gameHistory, currentHistoryIndex, hasEnded, capturedPieces } +} + +const reducer = (state, action) => { + console.log(action); + switch (action.type) { + case SELECT_PIECE: { + let moveHints = state.chess.getMoves(action.val); + let selected = action.val + return { ...state, moveHints, selected }; + } + case MOVE_PIECE: { + let newChess = new ChessModified(state.chess.fen()); + console.log(newChess.ascii()); + let updatedGameHistory = [...state.gameHistory]; + let { san, after } = newChess.move(action.val); + updatedGameHistory.push({ move: san, fen: after }); + return { ...state, chess: newChess, moveHints: [], gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, selected: null }; + } + case CAPTURE_PIECE: { + let newChess = new ChessModified(state.chess.fen()); + let updatedGameHistory = [...state.gameHistory]; + let { san, after } = newChess.move(action.val); + updatedGameHistory.push({ move: san, fen: after }); + return { ...state, chess: newChess, moveHints: [], gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, selected: null }; + } + case SET_GAME_HISTORY: { + let fetchedGameHistory = action.val; + let newChess = new ChessModified(); + let updatedGameHistory = []; + let capturedPieces = { 'w': [], 'b': [] } + for (let i = 0; i < fetchedGameHistory.length; i++) { + let { san, after, captured, color } = newChess.move(fetchedGameHistory[i]); + updatedGameHistory.push({ fen: after, move: san }); + if (captured) { + color === WHITE ? capturedPieces[BLACK].push(captured) : capturedPieces[WHITE].push(captured); + } + } + return { ...state, chess: newChess, gameHistory: updatedGameHistory, currentHistoryIndex: updatedGameHistory.length - 1, capturedPieces } + } + } +} + +const useChess = () => { + const [{ chess, moveHints, selected, gameHistory, currentHistoryIndex, capturedPieces, hasEnded }, dispatch] = useReducer(reducer, null, chessStateInit); + + function selectPiece(square) { + dispatch({ type: SELECT_PIECE, val: square }) + } + + function movePiece(from, to) { + dispatch({ type: MOVE_PIECE, val: { from, to } }) + } + + function capturePiece(from, to) { + dispatch({ type: CAPTURE_PIECE, val: { from, to } }) + } + + function setGameHistory(gameHistory) { + dispatch({ type: SET_GAME_HISTORY, val: gameHistory }) + } + + return { + chessState: { + chess, selected, moveHints, gameHistory, currentHistoryIndex, capturedPieces, hasEnded + }, + chessStateModifiers: { + selectPiece, movePiece, capturePiece, setGameHistory + } + } +} + +export default useChess; \ No newline at end of file diff --git a/frontend/src/pages/Chess/ChessGame.jsx b/frontend/src/pages/Chess/ChessGame.jsx index 0afaf29..74132f6 100644 --- a/frontend/src/pages/Chess/ChessGame.jsx +++ b/frontend/src/pages/Chess/ChessGame.jsx @@ -11,10 +11,11 @@ import ChessBoard from '../Chess/ChessBoard' import GameHistory from '../../components/GameHistory' import MainLoader from '../../components/MainLoader' import { SOCKET_EVENTS } from '../../constants' +import Timer from './Timer' const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS; const ChessGame = () => { - const { setGameHistory, hasGameEnded, gameEndedReason, endGame } = useContext(ChessGameContext); + const { setGameHistory, hasGameEnded, gameEndedReason, endGame, handleOpponentMove, isTimerOn } = useContext(ChessGameContext); const [gameEndedModalOpen, modalFunctions] = useDisclosure(true); const user = getUserData(); @@ -26,6 +27,7 @@ const ChessGame = () => { const roomID = localStorage.getItem('roomID'); const navigate = useNavigate(); const opponent = localStorage.getItem('opponent'); + let connected = socket.id; const exitGame = () => { // cleanup game related data @@ -69,10 +71,7 @@ const ChessGame = () => { console.log('Socket disconnected due to', reason); }); - socket.on(CHESS_OPPONENT_MOVE, () => { - // console.log(data); - // setIsTimerOn(true); - }) + socket.on(CHESS_OPPONENT_MOVE, handleOpponentMove) socket.on(USER_JOINED_ROOM, () => { setIsWaiting(false); @@ -88,6 +87,10 @@ const ChessGame = () => { } }, []); + useEffect(() => { + console.log('Connection useEffect()'); + }, [connected]); + if (!hasJoinedRoom) return ( ) @@ -111,7 +114,7 @@ const ChessGame = () => { } description={"description"} /> - {/* */} + { // TODO: handle isWaiting state @@ -137,7 +140,7 @@ const ChessGame = () => { } description={"description"} /> - {/* */} +