Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions frontend/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ export const DISPATCH_EVENTS = {
JUMP_TO: "JUMP_TO",
SET_GAME_HISTORY: "SET_GAME_HISTORY",
END_GAME: "END_GAME",
ADD_PREMOVE: "ADD_PREMOVE",
DELETE_PREMOVES: "DELETE_PREMOVES",
};
83 changes: 65 additions & 18 deletions frontend/src/context/chess-game-context.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { socket } from '../socket';
import { ChessModified, chessInit } from '../utils/chess';

import { DISPATCH_EVENTS, SOCKET_EVENTS } from '../constants';
const { MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY, END_GAME } = DISPATCH_EVENTS
const { MOVE_PIECE, SELECT_PIECE, JUMP_TO, SET_GAME_HISTORY, END_GAME, ADD_PREMOVE, DELETE_PREMOVES } = DISPATCH_EVENTS
const { GAME_END } = SOCKET_EVENTS;

export const ChessGameContext = createContext();
Expand All @@ -21,30 +21,40 @@ const reducer = (state, action) => {
}
case MOVE_PIECE:
{
let newChessObj = new ChessModified(state.chess.fen());
const { from ,to } = action.val;
const newChessCopyObj = new ChessModified(state.chessCopy.fen());
let { san, after } = newChessCopyObj.move({from, to});
const newChessObj = new ChessModified(newChessCopyObj.fen());

const preMoves = [ ...state.preMoves ];
action.val.isPreMove && preMoves.shift();
preMoves.forEach(({from, to}) => {
const piece = newChessObj.remove(from);
newChessObj.put(piece, to);
})

let updatedGameHistory = state.gameHistory;
let { san, after } = newChessObj.move(action.val);
updatedGameHistory.push({ move: san, fen: after });
let newState;
if (newChessObj.isCheckmate()) {
action.val.callback();
action.val.playAudioCallback("CHECKMATE");
newState = { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'CHECKMATE' };
newState = { ...state, chess: newChessObj, chessCopy: newChessCopyObj, chessBoard: newChessObj.getBoard(), preMoves, moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'CHECKMATE' };
} else if(newChessObj.isCheck() || newChessObj.inCheck()) {
action.val.playAudioCallback("CHECK");
newState = { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
newState = { ...state, chess: newChessObj, chessCopy: newChessCopyObj, chessBoard: newChessObj.getBoard(), preMoves, moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
} else if (newChessObj.isStalemate()) {
action.val.callback();
action.val.playAudioCallback("STALEMATE");
newState = { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'STALEMATE' };
newState = { ...state, chess: newChessObj, chessCopy: newChessCopyObj, chessBoard: newChessObj.getBoard(), preMoves, moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'STALEMATE' };
}
else {
if(!state.chess.get(action.val.to)) {
action.val.playAudioCallback("MOVE");
} else {
action.val.playAudioCallback("CAPTURE");
}
newState = { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
newState = { ...state, chess: newChessObj, chessCopy: newChessCopyObj, chessBoard: newChessObj.getBoard(), preMoves, moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
}
return newState;
}
Expand All @@ -68,6 +78,20 @@ const reducer = (state, action) => {
{
return { ...state, hasGameEnded: true, gameEndedReason: action.val }
}
case ADD_PREMOVE:
{
const updatedPreMoves = [ ...state.preMoves, action.val ];
const newChessObj = new ChessModified(state.chessCopy.fen());
updatedPreMoves.forEach(({from, to}) => {
const piece = newChessObj.remove(from);
newChessObj.put(piece, to);
})
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(), preMoves: updatedPreMoves }
}
case DELETE_PREMOVES:
{
return { ...state, chess: state.chessCopy, chessBoard: state.chessCopy.getBoard(), preMoves: [] }
}
default:
return state;
}
Expand All @@ -79,23 +103,25 @@ const reducer = (state, action) => {

function chessGameStateInit(myColor) {
let chess = chessInit(myColor);
let chessCopy = chessInit(myColor);
let chessBoard = chess.getBoard();
let preMoves = [];
let moveHints = [];
let gameHistory = [];
let selected = null;
let currentIndex = -1;
let hasGameEnded = false;
let gameEndedReason = "";

return { chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason };
return { chess, chessCopy, chessBoard, preMoves, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason };
}

// the ChessGameContextProvider seperates the game logic from the ChessBoard component and exposes
// some functions to update game state.
const ChessGameContextProvider = ({ children }) => {
let myColor = localStorage.getItem('myColor');
let roomID = localStorage.getItem('roomID');
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
const [{ chess, chessCopy, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason, preMoves }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
const [isTimerOn, setIsTimerOn] = useState(true);

const chessRef = useRef(chess);
Expand Down Expand Up @@ -142,37 +168,58 @@ const ChessGameContextProvider = ({ children }) => {
function handleOpponentMove(data, callback) {
let { from, to } = data;
console.log("Opponent move:",from,to);
dispatch({type:MOVE_PIECE,val: { from, to, callback,playAudioCallback }});
dispatch({type:MOVE_PIECE,val: { from, to, callback,playAudioCallback, isPreMove: false }});
}

// called when user clicks a square
function handleSquareClick(square, emitToSocketCallback, callback) {
let { type, color } = chessRef.current.get(square);
let marked = moveHintsRef.current.includes(square);

if (chessRef.current.turn() === myColor) {
if (type && color === myColor) {
selectPiece({square,color});
return;
} else if(marked) {
dispatch({ type: MOVE_PIECE, val: { from: selectedRef.current, to: square, callback,playAudioCallback } })
dispatch({ type: MOVE_PIECE, val: { from: selectedRef.current, to: square, callback,playAudioCallback, isPreMove: false } })
console.log("Move:",{ from: selectedRef.current, to: square })
emitToSocketCallback({ from: selectedRef.current, to: square })
}
} else {
if (selectedRef.current) {
dispatch({ type: ADD_PREMOVE, val: {from: selectedRef.current, to: square}});
}
}
}

function handleDrop(moveData, emitToSocketCallback, callback) {
let { from, to } = moveData;
const isOpponentTurn = chessCopy.turn() !== myColor;
if (isOpponentTurn && from !== to && chess.get(from).color === myColor) {
dispatch({ type: ADD_PREMOVE, val: {from, to}});
return;
}
if (moveHintsRef.current.includes(to)) {
dispatch({ type: MOVE_PIECE, val: { from: from, to: to, callback,playAudioCallback } }); // capture piece
dispatch({ type: MOVE_PIECE, val: { from: from, to: to, callback,playAudioCallback, isPreMove: false } }); // capture piece
console.log("Move:",{ from,to })
emitToSocketCallback(moveData);
}
}

function handlePreMove(callback, emitMoveToSocketCallback) {
const {from, to} = preMoves[0];
const moveHints = chessCopy.getMoves(from);
if(moveHints.includes(to)) {
dispatch({ type: MOVE_PIECE, val: { from, to, isPreMove: true, callback, playAudioCallback }})
console.log("Move:", { from, to });
emitMoveToSocketCallback({ from, to });
}
else{
dispatch({ type: DELETE_PREMOVES });
}
}

function selectPiece({ square, color: pieceColor }) {
if (pieceColor === myColor && myColor === chessRef.current.turn()) {
if (pieceColor === myColor) {
dispatch({ type: SELECT_PIECE, val: square });
}
}
Expand All @@ -190,8 +237,8 @@ const ChessGameContextProvider = ({ children }) => {
}

function getChessBoard() {
if (currentIndexRef.current === -1 || gameHistoryRef.current.length === 0) {
return new ChessModified().getBoard();
if (currentIndex === gameHistory.length - 1) {
return chessBoard;
} else {
// console.log(chess);
let currentChessBoard = new ChessModified(gameHistoryRef.current[currentIndexRef.current].fen).getBoard();
Expand Down Expand Up @@ -227,7 +274,7 @@ const ChessGameContextProvider = ({ children }) => {

return (
<ChessGameContext.Provider value={{
myColor, chess, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked,
myColor, preMoves, chess, chessCopy, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, handlePreMove, getSquareColor, isSquareMarked,
selectPiece, handleDrop, gameHistory, jumpTo, getChessBoard, currentIndex, goAhead, goBack, setGameHistory,
isTimerOn, hasGameEnded, gameEndedReason, endGame,getPieceColor
}}>
Expand All @@ -244,4 +291,4 @@ ChessGameContextProvider.propTypes = {
children: PropTypes.object
}

export default ChessGameContextProvider
export default ChessGameContextProvider
20 changes: 14 additions & 6 deletions frontend/src/pages/Play/ChessGameComputer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { SOCKET_EVENTS } from '../../constants';
const { CHESS_MOVE, GAME_END } = SOCKET_EVENTS

const ChessGameComputer = () => {
const { hasGameEnded, gameEndedReason, handleOpponentMove } = useContext(ChessGameContext);
const { chessCopy, myColor, hasGameEnded, gameEndedReason, preMoves, handleOpponentMove, handlePreMove } = useContext(ChessGameContext);
const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);
const navigate = useNavigate();
const user = getUserData();
Expand All @@ -23,15 +23,14 @@ const ChessGameComputer = () => {
useEffect(() => {
socket.connect();
socket.emit('INIT', {color});

// socket.onAny(evt => {
// console.log("event", evt);
// })

socket.on("CHESS_BOT_MOVE", (data) => {
handleOpponentMove(data, () => {
socket.emit(GAME_END, roomID);
})
handleOpponentMove(data,
() => socket.emit(GAME_END, roomID),
);
});

return () => {
Expand All @@ -40,6 +39,15 @@ const ChessGameComputer = () => {
}
}, []);

useEffect(() => {
if(chessCopy.turn() === myColor && preMoves.length) {
handlePreMove(
() => socket.emit(GAME_END, roomID),
(moveData) => socket.emit(CHESS_MOVE, roomID, moveData)
)
}
}, [chessCopy])

const exitGame = () => {
console.log("Ending game");
socket.disconnect();
Expand Down Expand Up @@ -132,4 +140,4 @@ const ChessGameComputer = () => {
)
}

export default ChessGameComputer
export default ChessGameComputer
14 changes: 12 additions & 2 deletions frontend/src/pages/Play/ChessGameMultiplayer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Timer from '../Chess/Timer'
const { CONNECT, DISCONNECT, CHESS_OPPONENT_MOVE, USER_RESIGNED, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM, CHESS_MOVE } = SOCKET_EVENTS;

const ChessGameMultiplayer = () => {
const { setGameHistory, hasGameEnded, gameEndedReason, endGame, handleOpponentMove, isTimerOn } = useContext(ChessGameContext);
const { chessCopy, myColor, preMoves, setGameHistory, hasGameEnded, gameEndedReason, endGame, handleOpponentMove, handlePreMove, isTimerOn } = useContext(ChessGameContext);
const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);

const user = getUserData();
Expand Down Expand Up @@ -98,6 +98,16 @@ const ChessGameMultiplayer = () => {
}
}, []);

useEffect(() => {
if(chessCopy.turn() === myColor && preMoves.length) {
handlePreMove(
() => socket.emit(GAME_END, roomID),
(moveData) => socket.emit(CHESS_MOVE, roomID, moveData)
)
}
}, [chessCopy])


if (!hasJoinedRoom) return (
<MainLoader />
)
Expand Down Expand Up @@ -171,4 +181,4 @@ const ChessGameMultiplayer = () => {
)
}

export default ChessGameMultiplayer
export default ChessGameMultiplayer
11 changes: 7 additions & 4 deletions frontend/src/pages/Settings/Friends.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ const Friends = () => {
const form = useForm({ initialValues: { username: '' }, })

const addFriend = async () => {
console.log(form.values);
const response = await fetch(`${import.meta.env.VITE_BACKEND_HOST}/api/user/friends/${form.values.username}`, {
console.log("form values",form.values);
const response = await fetch(`${import.meta.env.VITE_BACKEND_HOST}/api/user/friends`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({ username: form.values.username })
body: JSON.stringify({friendUsername: form.values.username}),
});
if (!response.ok) {
const resData = await response.json();
Expand All @@ -36,4 +39,4 @@ const Friends = () => {
)
}

export default Friends
export default Friends