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