first commit
This commit is contained in:
37
components/draughts/game/DraughtsGameContext.jsx
Normal file
37
components/draughts/game/DraughtsGameContext.jsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { createContext, useCallback, useContext } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useDraughtsBoard } from '../board/DraughtsBoardContext';
|
||||
import { useDraughtsTimer } from './timer/use-draughts-timer';
|
||||
import { useDraughtsComputer } from './computer/use-draughts-computer';
|
||||
import { Players } from '@draughts/core';
|
||||
|
||||
export const DraughtsGameContext = createContext();
|
||||
|
||||
export const useDraughtsGame = () => useContext(DraughtsGameContext);
|
||||
|
||||
export function DraughtsGameProvider(props) {
|
||||
const { resetBoard } = useDraughtsBoard();
|
||||
|
||||
const [whiteTimer, resetWhiteTimer] = useDraughtsTimer(Players.WHITE);
|
||||
const [blackTimer, resetBlackTimer] = useDraughtsTimer(Players.BLACK);
|
||||
|
||||
useDraughtsComputer();
|
||||
|
||||
const restartGame = useCallback(() => {
|
||||
resetBoard();
|
||||
resetWhiteTimer();
|
||||
resetBlackTimer();
|
||||
}, [resetBoard, resetWhiteTimer, resetBlackTimer]);
|
||||
|
||||
return (
|
||||
<DraughtsGameContext.Provider
|
||||
value={{ blackTimer, restartGame, whiteTimer }}
|
||||
>
|
||||
{props.children}
|
||||
</DraughtsGameContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
DraughtsGameProvider.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
31
components/draughts/game/computer/use-draughts-computer.js
Normal file
31
components/draughts/game/computer/use-draughts-computer.js
Normal file
@ -0,0 +1,31 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useDraughtsSettings } from '../../settings/DraughtsSettingsContext';
|
||||
import { useDraughtsBoard } from '../../board/DraughtsBoardContext';
|
||||
import { GameStates } from '@draughts/core';
|
||||
|
||||
export function useDraughtsComputer() {
|
||||
const { board, doMove } = useDraughtsBoard();
|
||||
const { userPlayer, computerDifficulty } = useDraughtsSettings();
|
||||
|
||||
const workerRef = useRef();
|
||||
useEffect(() => {
|
||||
if (board.state !== GameStates.PLAYING) return;
|
||||
if (board.playerToMove === userPlayer) return;
|
||||
|
||||
workerRef.current = new Worker(new URL('worker.js', import.meta.url));
|
||||
|
||||
const handleWorkerMessage = (event) => {
|
||||
doMove(event.data);
|
||||
};
|
||||
|
||||
workerRef.current.addEventListener('message', handleWorkerMessage);
|
||||
workerRef.current.postMessage({ board, computerDifficulty });
|
||||
|
||||
return () => {
|
||||
workerRef.current.removeEventListener('message', handleWorkerMessage);
|
||||
workerRef.current.terminate();
|
||||
};
|
||||
}, [board, userPlayer, computerDifficulty, doMove]);
|
||||
|
||||
return null;
|
||||
}
|
||||
40
components/draughts/game/computer/worker.js
Normal file
40
components/draughts/game/computer/worker.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { ComputerDifficulty } from '../../settings/constants/computer-difficulty';
|
||||
import { alphaBetaMove } from '@draughts/computer';
|
||||
import { Board } from '@draughts/core';
|
||||
|
||||
const getRandomMove = (board) => {
|
||||
return board.moves.at(Math.floor(Math.random() * board.moves.length));
|
||||
};
|
||||
|
||||
const getComputerMove = (board, difficulty) => {
|
||||
if (difficulty === ComputerDifficulty.MEDIUM) {
|
||||
return alphaBetaMove(board, 3);
|
||||
}
|
||||
if (difficulty === ComputerDifficulty.HARD) {
|
||||
return alphaBetaMove(board, 6);
|
||||
}
|
||||
return getRandomMove(board);
|
||||
};
|
||||
|
||||
const getComputerTimeout = (board) => {
|
||||
return 200 + board.moves.length * Math.floor(Math.random() * 200);
|
||||
};
|
||||
|
||||
addEventListener('message', (event) => {
|
||||
const startTime = Date.now();
|
||||
|
||||
const board = new Board(
|
||||
event.data.board.position,
|
||||
event.data.board.playerToMove,
|
||||
event.data.board.firstMove
|
||||
);
|
||||
const move = getComputerMove(board, event.data.computerDifficulty);
|
||||
|
||||
const endTime = Date.now();
|
||||
const elapsedTime = startTime - endTime;
|
||||
const waitTime = Math.max(0, getComputerTimeout(board) - elapsedTime);
|
||||
|
||||
setTimeout(() => {
|
||||
postMessage(move);
|
||||
}, waitTime);
|
||||
});
|
||||
43
components/draughts/game/timer/use-draughts-timer.js
Normal file
43
components/draughts/game/timer/use-draughts-timer.js
Normal file
@ -0,0 +1,43 @@
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import { useDraughtsBoard } from '../../board/DraughtsBoardContext';
|
||||
import { GameStates } from '@draughts/core';
|
||||
|
||||
const TIMER_TICK = 100;
|
||||
const INITIAL_TIME = 5 * 60 * 1000;
|
||||
|
||||
export function useDraughtsTimer(player) {
|
||||
const { board } = useDraughtsBoard();
|
||||
const [timer, setTimer] = useState(INITIAL_TIME);
|
||||
|
||||
useEffect(() => {
|
||||
if (board.state !== GameStates.PLAYING) return;
|
||||
if (board.firstMove) return;
|
||||
if (board.playerToMove !== player) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setTimer((timer) => {
|
||||
const updatedTime = timer - TIMER_TICK;
|
||||
return updatedTime >= 0 ? updatedTime : 0;
|
||||
});
|
||||
}, TIMER_TICK);
|
||||
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
setTimer((timer) => timer + 1000);
|
||||
};
|
||||
}, [player, board, setTimer]);
|
||||
|
||||
const timerInfo = useMemo(() => {
|
||||
const totalSeconds = Math.floor(timer / 1000);
|
||||
return {
|
||||
complete: timer === 0,
|
||||
millis: timer % 1000,
|
||||
minutes: Math.floor(totalSeconds / 60),
|
||||
seconds: totalSeconds % 60,
|
||||
};
|
||||
}, [timer]);
|
||||
|
||||
const resetTimer = () => setTimer(INITIAL_TIME);
|
||||
|
||||
return [timerInfo, resetTimer];
|
||||
}
|
||||
Reference in New Issue
Block a user