Chess game logic move to chess-game-context.jsx
Improves code clarity as logic is handled by context provider and application is handled by the component itself
This commit is contained in:
@@ -65,6 +65,7 @@ function socketIOServerInit(server) {
|
||||
});
|
||||
|
||||
socket.on("move", (roomID, moveData) => {
|
||||
console.log(moveData);
|
||||
socket.to(roomID).emit("opponent-move", moveData);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
const AuthContext = createContext();
|
||||
|
||||
import React from 'react'
|
||||
|
||||
const AuthContextProvider = ({ children }) => {
|
||||
return (
|
||||
<AuthContext.Provider value={{
|
||||
|
||||
}}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default auth - context
|
||||
@@ -1,43 +1,32 @@
|
||||
import React, { useState } from 'react'
|
||||
import React, { useContext, useState } from 'react'
|
||||
import Piece from './Piece';
|
||||
import { socket } from '../socket';
|
||||
import { Box, Flex } from '@mantine/core';
|
||||
import { useDroppable } from '@dnd-kit/core'
|
||||
import { ChessGameContext } from '../context/chess-game-context';
|
||||
|
||||
const Cell = ({ cell, chess, marked, dispatch,selected }) => {
|
||||
const { square, type, color } = cell;
|
||||
const { isOver, setNodeRef } = useDroppable({ id: square });
|
||||
const Cell = ({ cell }) => {
|
||||
let { square, type, color } = cell;
|
||||
const { getSquareColor, isSquareMarked, handleSquareClick } = useContext(ChessGameContext)
|
||||
const [isDropped, setIsDropped] = useState(false);
|
||||
let squareColor = chess.squareColor(square) === 'light' ? "w" : "b";
|
||||
const { isOver, setNodeRef } = useDroppable({ id: square });
|
||||
let squareColor = getSquareColor(square);
|
||||
let marked = isSquareMarked(square);
|
||||
|
||||
const handleClick = () => {
|
||||
console.log(!type, selected, marked)
|
||||
if (chess.turn() !== localStorage.getItem('myColor')) return;
|
||||
if (chess.myColor === color) {
|
||||
if (type && chess.turn() === chess.myColor) {
|
||||
return dispatch({ type: 'SELECT_PIECE', val: square });
|
||||
}
|
||||
if (!type && selected && marked) {
|
||||
console.log(square)
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from: selected, to: square } })
|
||||
}
|
||||
if (type && marked) {
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from: selected, to: square } })
|
||||
}
|
||||
}
|
||||
handleSquareClick(square);
|
||||
}
|
||||
|
||||
let content;
|
||||
content = marked ? <Mark /> : <Piece cell={cell} dispatch={dispatch} />;
|
||||
let content = marked ? <Mark /> : <Piece cell={cell} />;
|
||||
|
||||
return (
|
||||
<Flex ref={setNodeRef} style={{aspectRatio:'1/1'}} onClick={handleClick} bg={squareColor === 'w' ? "white" : "gray"} >
|
||||
<Flex ref={setNodeRef} style={{ aspectRatio: '1/1' }} onClick={handleClick} bg={squareColor === 'w' ? "white" : "gray"} >
|
||||
{content}
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
export const Mark = () => {
|
||||
const Mark = () => {
|
||||
return (
|
||||
<Box w="33%" h="33%" sx={{ backgroundColor: '#77777777', borderRadius: '100%' }} m="auto"></Box>
|
||||
)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Image } from '@mantine/core';
|
||||
import React, { useEffect } from 'react';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { useDraggable } from '@dnd-kit/core'
|
||||
import { ChessGameContext } from '../context/chess-game-context';
|
||||
|
||||
const Piece = ({ cell, dispatch }) => {
|
||||
const Piece = ({ cell }) => {
|
||||
const { selectPiece } = useContext(ChessGameContext)
|
||||
let { square, type, color } = cell;
|
||||
let logo = null;
|
||||
switch (type) {
|
||||
@@ -31,6 +33,7 @@ const Piece = ({ cell, dispatch }) => {
|
||||
...cell
|
||||
}
|
||||
});
|
||||
|
||||
const style = transform ? {
|
||||
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`,
|
||||
cursor: isDragging ? 'grabbing' : 'pointer',
|
||||
@@ -38,19 +41,21 @@ const Piece = ({ cell, dispatch }) => {
|
||||
aspectRatio: '1',
|
||||
touchAction: 'none'
|
||||
} : undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (isDragging) {
|
||||
dispatch({ type: 'SELECT_PIECE', val: cell });
|
||||
selectPiece(cell);
|
||||
}
|
||||
}, [isDragging])
|
||||
|
||||
|
||||
if (logo) {
|
||||
return (
|
||||
<Image ref={setNodeRef} style={style} sx={{ cursor: 'pointer' }} {...listeners} {...attributes} src={`/src/assets/${logo}.png`} />
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div style={{width:'100%'}}>
|
||||
</div>
|
||||
<div style={{ width: '100%' }}></div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
import React, { createContext, useReducer, useRef } from 'react'
|
||||
import { ChessModified, chessInit } from '../../utils/chess';
|
||||
|
||||
export const ChessGameContext = createContext();
|
||||
// myColor: null, chess: null, chessBoard: null, moveHints: null, selected: null, dispatch: null, handleOpponentMove: null, handleSquareClick: null, getSquareColor: null, isSquareMarked: null, selectPiece: null, handleDrop: null
|
||||
|
||||
|
||||
const reducer = (state, action) => {
|
||||
console.log(state.chess.myColor)
|
||||
switch (action.type) {
|
||||
case 'SELECT_PIECE':
|
||||
{
|
||||
console.log('SELECTING...', action.val)
|
||||
return { ...state, moveHints: state.chess.getMoves(action.val), selected: action.val };
|
||||
}
|
||||
case 'MOVE_PIECE':
|
||||
{
|
||||
console.log('Moving', action.val, state.chess.turn());
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor })
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null };
|
||||
}
|
||||
case 'CAPTURE_PIECE':
|
||||
{
|
||||
console.log('Capture', action.val, state.chess.turn())
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor, selected: null });
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [] };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
function chessGameStateInit(myColor) {
|
||||
let chess = chessInit(myColor);
|
||||
let chessBoard = chess.getBoard(myColor);
|
||||
let moveHints = [];
|
||||
let selected = null;
|
||||
|
||||
return { chess, chessBoard, moveHints, selected }
|
||||
}
|
||||
|
||||
// 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');
|
||||
const [{ chess, chessBoard, moveHints, selected }, dispatch] = useReducer(reducer, myColor, chessGameStateInit);
|
||||
|
||||
const moveAudioRef = useRef(null);
|
||||
const captureAudioRef = useRef(null);
|
||||
const gameEndAudioRef = useRef(null);
|
||||
const checkAudioRef = useRef(null);
|
||||
|
||||
// data received through socket
|
||||
function handleOpponentMove(data) {
|
||||
let { from, to } = data;
|
||||
console.log(from + to)
|
||||
if (!chess.get(to)) {
|
||||
console.log('Moving piece: ', data)
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from, to } });
|
||||
moveAudioRef.current.play();
|
||||
return;
|
||||
} else {
|
||||
console.log('Capturing piece');
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from, to } });
|
||||
captureAudioRef.current.play();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// called when user clicks a square
|
||||
function handleSquareClick(square) {
|
||||
let { type, color } = chess.get(square);
|
||||
let marked = moveHints.includes(square);
|
||||
console.log('handleSquareClick', square)
|
||||
|
||||
console.log(!type, selected, marked)
|
||||
if (chess.turn() === myColor) {
|
||||
if (type && color === myColor) {
|
||||
return dispatch({ type: 'SELECT_PIECE', val: square });
|
||||
}
|
||||
if (!type && selected && marked) {
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from: selected, to: square } })
|
||||
}
|
||||
if (type && marked) {
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from: selected, to: square } })
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrop(moveData, emitToSocketCallback) {
|
||||
let { from, to } = moveData;
|
||||
if (moveHints.includes(to)) {
|
||||
console.log(chess.get(from))
|
||||
if (chess.get(to)) {
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from: from, to: to } }); // capture piece
|
||||
captureAudioRef.current.play();
|
||||
emitToSocketCallback(moveData);
|
||||
} else {
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from: from, to: to } }); // move piece
|
||||
moveAudioRef.current.play();
|
||||
emitToSocketCallback(moveData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectPiece({ square, type, color: pieceColor }) {
|
||||
if (pieceColor === myColor && myColor === chess.turn()) {
|
||||
console.log(square, type, pieceColor)
|
||||
dispatch({ type: 'SELECT_PIECE', val: square });
|
||||
}
|
||||
}
|
||||
|
||||
function getSquareColor(square) {
|
||||
return chess.squareColor(square) === 'light' ? "w" : "b";
|
||||
}
|
||||
|
||||
function isSquareMarked(square) {
|
||||
return moveHints.includes(square);
|
||||
}
|
||||
|
||||
return (
|
||||
<ChessGameContext.Provider value={{
|
||||
myColor, chess, chessBoard, moveHints, selected, dispatch, handleOpponentMove, handleSquareClick, getSquareColor, isSquareMarked, selectPiece, handleDrop
|
||||
}}>
|
||||
{children}
|
||||
<audio src='/src/assets/move-self.mp3' ref={moveAudioRef} />
|
||||
<audio src='/src/assets/capture.mp3' ref={captureAudioRef} />
|
||||
<audio src='/src/assets/game-end.webm.mp3' ref={gameEndAudioRef} />
|
||||
<audio src='/src/assets/move-check.mp3' ref={checkAudioRef} />
|
||||
</ChessGameContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default ChessGameContextProvider
|
||||
@@ -1,10 +1,9 @@
|
||||
import React, { useEffect, useReducer, useRef } from 'react';
|
||||
import { ChessModified, chess, chessInit } from '../../../utils/chess';
|
||||
import React, { useContext, useEffect, useReducer, useRef } from 'react';
|
||||
import Cell from '../../components/Cell';
|
||||
import { socket } from '../../socket';
|
||||
import { Flex, createStyles } from '@mantine/core';
|
||||
import { DndContext } from '@dnd-kit/core'
|
||||
import { useElementSize } from '@mantine/hooks';
|
||||
import { ChessGameContext } from '../../context/chess-game-context';
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
chessboard: {
|
||||
@@ -32,120 +31,39 @@ const useStyles = createStyles((theme) => ({
|
||||
}
|
||||
}))
|
||||
|
||||
const reducer = (state, action) => {
|
||||
console.log(state.chess.myColor)
|
||||
switch (action.type) {
|
||||
case 'SELECT_PIECE':
|
||||
{
|
||||
if (state.chess.turn() !== localStorage.getItem('myColor')) return state;
|
||||
return { ...state, moveHints: state.chess.getMoves(action.val.square), selected: action.val.square };
|
||||
}
|
||||
case 'MOVE_PIECE':
|
||||
{
|
||||
console.log('Moving', action.val, state.chess.turn());
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor })
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [], selected: null };
|
||||
}
|
||||
case 'CAPTURE_PIECE':
|
||||
{
|
||||
console.log('Capture', action.val, state.chess.turn())
|
||||
let newChessObj = new ChessModified({ prop: state.chess.fen(), color: state.chess.myColor, selected: null });
|
||||
newChessObj.move(action.val);
|
||||
return { ...state, chess: newChessObj, chessBoard: newChessObj.getBoard(localStorage.getItem('myColor')), moveHints: [] };
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
const ChessBoard = ({ color }) => {
|
||||
const ref = useRef()
|
||||
const { height, width } = useElementSize(ref)
|
||||
const { classes } = useStyles();
|
||||
const moveAudioRef = useRef(null);
|
||||
const captureAudioRef = useRef(null);
|
||||
const gameEndAudioRef = useRef(null);
|
||||
const checkAudioRef = useRef(null);
|
||||
|
||||
const { chessBoard, handleOpponentMove, handleDrop } = useContext(ChessGameContext)
|
||||
let roomID = localStorage.getItem('roomID');
|
||||
|
||||
const [gameState, dispatch] = useReducer(reducer, {
|
||||
chess: chessInit(color), chessBoard: chess.getBoard(color), moveHints: [], selected: null
|
||||
});
|
||||
|
||||
const chessBoardRef = useRef(gameState.chessBoard);
|
||||
chessBoardRef.current = gameState.chessBoard;
|
||||
|
||||
useEffect(() => {
|
||||
function handleOpponentMove(data) {
|
||||
let { from, to } = data;
|
||||
console.log(from + to)
|
||||
if (!gameState.chess.get(to)) {
|
||||
console.log('Moving piece: ', data)
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from, to } });
|
||||
moveAudioRef.current.play();
|
||||
return;
|
||||
} else {
|
||||
console.log('Capturing piece');
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from, to } });
|
||||
captureAudioRef.current.play();
|
||||
return;
|
||||
}
|
||||
}
|
||||
socket.on('opponent-move', handleOpponentMove)
|
||||
|
||||
return () => {
|
||||
socket.off('move', handleOpponentMove);
|
||||
socket.off('opponent-move');
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<DndContext onDragEnd={evt => {
|
||||
let srcSquare = evt.active.id;
|
||||
let destSquare = evt.over.id;
|
||||
|
||||
if (gameState.moveHints.includes(destSquare)) {
|
||||
console.log(gameState.chess.get(srcSquare))
|
||||
if (gameState.chess.get(destSquare)) {
|
||||
captureAudioRef.current.play();
|
||||
dispatch({ type: 'CAPTURE_PIECE', val: { from: srcSquare, to: destSquare } }); // capture piece
|
||||
socket.emit('move', roomID, { from: srcSquare, to: destSquare })
|
||||
} else {
|
||||
moveAudioRef.current.play();
|
||||
dispatch({ type: 'MOVE_PIECE', val: { from: srcSquare, to: destSquare } }); // move piece
|
||||
socket.emit('move', roomID, { from: srcSquare, to: destSquare })
|
||||
}
|
||||
}
|
||||
let from = evt.active.id;
|
||||
let to = evt.over.id;
|
||||
handleDrop({ from, to }, (moveData) => {
|
||||
socket.emit('move', roomID, moveData);
|
||||
})
|
||||
}}>
|
||||
<Flex ref={ref} className={
|
||||
classes.chessboard
|
||||
}>
|
||||
<Flex className={classes.chessboard}>
|
||||
<div>
|
||||
{gameState.chessBoard.map((row, rowIndex) => {
|
||||
{chessBoard.map((row, rowIndex) => {
|
||||
return (
|
||||
<Flex className={classes.boardrow} key={rowIndex * 2}>
|
||||
{row.map((cell) => {
|
||||
return (
|
||||
<Cell
|
||||
key={cell.square}
|
||||
cell={cell}
|
||||
chess={chess}
|
||||
marked={gameState.moveHints.includes(cell.square)}
|
||||
dispatch={dispatch}
|
||||
selected={gameState.selected}
|
||||
/>)
|
||||
})}
|
||||
{row.map(cell => <Cell key={cell.square} cell={cell} />)}
|
||||
</Flex>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</Flex>
|
||||
<audio src='/src/assets/move-self.mp3' ref={moveAudioRef} />
|
||||
<audio src='/src/assets/capture.mp3' ref={captureAudioRef} />
|
||||
<audio src='/src/assets/game-end.webm.mp3' ref={gameEndAudioRef} />
|
||||
<audio src='/src/assets/move-check.mp3' ref={checkAudioRef} />
|
||||
</DndContext >
|
||||
</DndContext>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import ChessBoard from '../Chess/ChessBoard'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { socket } from '../../socket'
|
||||
import { getUserData } from '../../../utils/auth'
|
||||
import ChessGameContextProvider from '../../context/chess-game-context'
|
||||
|
||||
const ChessGame = () => {
|
||||
const user = getUserData();
|
||||
@@ -71,7 +72,9 @@ const ChessGame = () => {
|
||||
icon={<Avatar radius="3px" children={opponent[0].toUpperCase()} />}
|
||||
description={"description"}
|
||||
/>
|
||||
<ChessBoard color={localStorage.getItem('myColor')} />
|
||||
<ChessGameContextProvider>
|
||||
<ChessBoard />
|
||||
</ChessGameContextProvider>
|
||||
<NavLink
|
||||
p="2px"
|
||||
label={username}
|
||||
|
||||
Reference in New Issue
Block a user