feature added: checkmate detection and ending game on checkmate

This commit is contained in:
Moon Patel
2023-07-05 21:25:28 +05:30
parent 313906eb5d
commit 128e86ecb4
6 changed files with 118 additions and 59 deletions
+6 -2
View File
@@ -1,7 +1,7 @@
import { Button, Group, Stack, Text, Title } from '@mantine/core';
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom';
import { getUserData } from '../../utils/auth'
import { getAuthToken, getUserData } from '../../utils/auth'
const Challenges = () => {
const navigate = useNavigate();
@@ -15,7 +15,11 @@ const Challenges = () => {
const fetchData = async () => {
let url = `${import.meta.env.VITE_BACKEND_HOST}/api/user/${username}/challenges`;
try {
response = await fetch(url, { signal: abortController.signal })
response = await fetch(url, {
signal: abortController.signal, headers: {
'Authorization': `Bearer ${getAuthToken()}`
}
})
const data = await response.json();
if (data.success) {
setChallenges(data.challenges);
+2
View File
@@ -1,4 +1,6 @@
export const SOCKET_EVENTS = {
CONNECT:'connect',
DISCONNECT:'disconnect',
CONNECTION: "connection",
JOIN_ROOM: "join-room",
JOIN_ROOM_SUCCESS: "join-room-success",
+17 -5
View File
@@ -22,7 +22,11 @@ const reducer = (state, action) => {
let updatedGameHistory = state.gameHistory;
let { san, after } = newChessObj.move(action.val);
updatedGameHistory.push({ move: san, fen: after });
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
if (newChessObj.isCheckmate()) {
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'CHECKMATE' };
} else {
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
}
}
case CAPTURE_PIECE:
{
@@ -31,7 +35,11 @@ const reducer = (state, action) => {
let updatedGameHistory = state.gameHistory;
let { san, after } = newChessObj.move(action.val);
updatedGameHistory.push({ move: san, fen: after });
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
if (newChessObj.isCheckmate()) {
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1, hasGameEnded: true, gameEndedReason: 'CHECKMATE' };
} else {
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null, gameHistory: updatedGameHistory, currentIndex: updatedGameHistory.length - 1 };
}
}
case JUMP_TO:
{
@@ -61,8 +69,10 @@ function chessGameStateInit(myColor) {
let gameHistory = [];
let selected = null;
let currentIndex = -1;
let hasGameEnded = false;
let gameEndedReason = "";
return { chess, chessBoard, moveHints, selected, gameHistory, currentIndex };
return { chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason };
}
// the ChessGameContextProvider seperates the game logic from the ChessBoard component and exposes
@@ -70,7 +80,7 @@ function chessGameStateInit(myColor) {
const ChessGameContextProvider = ({ children }) => {
let myColor = localStorage.getItem('myColor');
console.log('INSIDE CONTEXT PROVIDER');
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
const [{ chess, chessBoard, moveHints, selected, gameHistory, currentIndex, hasGameEnded, gameEndedReason }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
const [isTimerOn, setIsTimerOn] = useState(true);
console.log(gameHistory);
@@ -192,7 +202,9 @@ const ChessGameContextProvider = ({ children }) => {
return (
<ChessGameContext.Provider value={{
myColor, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked, selectPiece, handleDrop, gameHistory, jumpTo, getChessBoard, currentIndex, goAhead, goBack, setGameHistory, isTimerOn
myColor, chessBoard, moveHints, selected, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked,
selectPiece, handleDrop, gameHistory, jumpTo, getChessBoard, currentIndex, goAhead, goBack, setGameHistory,
isTimerOn, hasGameEnded, gameEndedReason
}}>
{children}
<audio src='/src/assets/move-self.mp3' ref={moveAudioRef} />
+7
View File
@@ -0,0 +1,7 @@
import React from 'react'
const useQuery = () => {
}
export default useQuery
+7 -1
View File
@@ -34,10 +34,16 @@ const useStyles = createStyles((theme) => ({
const ChessBoard = () => {
const { classes } = useStyles();
const { getChessBoard, handleOpponentMove, handleDrop } = useContext(ChessGameContext)
const { getChessBoard, handleOpponentMove, handleDrop,hasGameEnded,gameEndedReason } = useContext(ChessGameContext)
let roomID = localStorage.getItem('roomID');
const chessBoard = getChessBoard();
if(hasGameEnded) {
console.log('Game ended due to',gameEndedReason)
} else {
console.log('Game not ended yet')
}
useEffect(() => {
socket.on(CHESS_OPPONENT_MOVE, handleOpponentMove)
+79 -51
View File
@@ -1,4 +1,4 @@
import { Avatar, Button, Flex, Group, Image, Loader, MediaQuery, NavLink, Text, Title } from '@mantine/core'
import { Avatar, Button, Flex, Group, Image, Loader, MediaQuery, Modal, NavLink, Text, Title } from '@mantine/core'
import React, { useContext, useEffect, useState } from 'react'
import ChessBoard from '../Chess/ChessBoard'
import { useNavigate, useParams } from 'react-router-dom'
@@ -7,9 +7,14 @@ import { getUserData } from '../../../utils/auth'
import { ChessGameContext } from '../../context/chess-game-context'
import GameHistory from '../../components/GameHistory'
import Timer from './Timer'
import { useDisclosure } from '@mantine/hooks'
import { SOCKET_EVENTS } from '../../constants'
const { CONNECT, DISCONNECT, CHESS_MOVE, CHESS_OPPONENT_MOVE, CONNECTION, JOIN_ROOM, JOIN_ROOM_ERROR, JOIN_ROOM_SUCCESS, ROOM_FULL, USER_JOINED_ROOM } = SOCKET_EVENTS;
const ChessGame = () => {
const { gameHistory, setGameHistory, isTimerOn,setIsTimerOn } = useContext(ChessGameContext);
const { setGameHistory, isTimerOn, setIsTimerOn, hasGameEnded, gameEndedReason } = useContext(ChessGameContext);
const [gameEndedModalOpen, modalFunctions] = useDisclosure(true);
const user = getUserData();
let username = user.username;
let color = localStorage.getItem('myColor')
@@ -31,37 +36,40 @@ const ChessGame = () => {
useEffect(() => {
socket.connect();
socket.on('connect', () => {
socket.on(CONNECT, () => {
console.log('Connected');
});
socket.on('join-room-success', (fetchedGameHistory) => {
socket.on(JOIN_ROOM_SUCCESS, (fetchedGameHistory) => {
console.log('Room joined:', roomID);
setGameHistory(fetchedGameHistory);
setHasJoinedRoom(true);
});
socket.on('room-full', () => {
socket.on(ROOM_FULL, () => {
console.log('Room is full');
})
socket.on("join-room-error", (err) => {
socket.on(JOIN_ROOM_ERROR, (err) => {
console.error("Error:", err);
})
console.log('JOINING ROOM')
socket.emit("join-room", roomID, { username, color })
socket.emit(JOIN_ROOM, roomID, { username, color })
socket.on('disconnect', (reason) => {
socket.on(DISCONNECT, (reason) => {
console.log('Socket disconnected due to', reason);
})
});
socket.on('opponent-move', (data) => {
socket.on(CHESS_OPPONENT_MOVE, (data) => {
console.log(data);
// setIsTimerOn(true);
})
socket.on(USER_JOINED_ROOM, () => {
setIsWaiting(false);
});
}, []);
if (!hasJoinedRoom) return (
@@ -69,47 +77,67 @@ const ChessGame = () => {
)
return (
<Flex gap="xl" miw={360} justify='center' align='center' wrap='nowrap' mt={{ base: '50px', sm: '0px' }} direction={{ base: 'column', lg: 'row' }}>
<Flex gap="xs" justify='center' align='start' wrap='nowrap' direction='column' >
<div style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
<NavLink
style={{width:"500px"}}
p="2px"
label={opponent}
icon={<Avatar radius="3px" children={opponent[0].toUpperCase()} />}
description={"description"}
/>
{/* <Timer on={!isTimerOn} /> */}
</div>
<ChessBoard />
<div style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
<NavLink
style={{width:"500px"}}
p="2px"
label={username}
icon={<Avatar radius="3px" children={username[0].toUpperCase()} />}
description={"description"}
/>
{/* <Timer on={isTimerOn} /> */}
</div>
</Flex>
<MediaQuery smallerThan="lg" styles={{ display: 'none' }}>
<Flex maw={450} sx={{
width: '100%',
height: '600px',
textAlign: 'center',
borderRadius: '10px'
}} bg='gray' p="10px" justify='start' align='center' direction='column' h="600px">
<Title>Game Data</Title>
<Flex direction='column'>
<GameHistory />
</Flex>
<Flex>
<Button onClick={exitGame} color='red'>Exit Game</Button>
</Flex>
<>
<Modal onClose={modalFunctions.close} opened={hasGameEnded && gameEndedModalOpen} centered>
<Text>Game ended due to {gameEndedReason}</Text>
<Button color='lime' onClick={exitGame}>Go back</Button>
<Button mx='md' color='lime' onClick={modalFunctions.close}>OK</Button>
</Modal>
<Flex gap="xl" miw={360} justify='center' align='center' wrap='nowrap' mt={{ base: '50px', sm: '0px' }} direction={{ base: 'column', lg: 'row' }}>
<Flex gap="xs" justify='center' align='start' wrap='nowrap' direction='column' >
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<NavLink
style={{ width: "500px" }}
p="2px"
label={isWaiting ? "Waiting for opponent..." : opponent}
icon={<Avatar radius="3px" children={opponent[0].toUpperCase()} />}
description={"description"}
/>
{/* <Timer on={!isTimerOn} /> */}
</div>
{
// TODO: handle isWaiting state
false ?
<>
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
<Image width={600} miw={480} src="/src/assets/chess_board.png" />
</MediaQuery>
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
<Image width="100%" maw={540} src="/src/assets/chess_board.png" />
</MediaQuery>
</>
:
<ChessBoard />
}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<NavLink
style={{ width: "500px" }}
p="2px"
label={username}
icon={<Avatar radius="3px" children={username[0].toUpperCase()} />}
description={"description"}
/>
{/* <Timer on={isTimerOn} /> */}
</div>
</Flex>
</MediaQuery>
</Flex>
<MediaQuery smallerThan="lg" styles={{ display: 'none' }}>
<Flex maw={450} sx={{
width: '100%',
height: '600px',
textAlign: 'center',
borderRadius: '10px'
}} bg='gray' p="10px" justify='start' align='center' direction='column' h="600px">
<Title>Game Data</Title>
<Flex direction='column'>
<GameHistory />
</Flex>
<Flex>
<Button onClick={exitGame} color='red'>Exit Game</Button>
</Flex>
</Flex>
</MediaQuery>
</Flex>
</>
)
}