add game logic
This commit is contained in:
parent
591ed62ccd
commit
fbd1e0d932
@ -2,9 +2,9 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link rel="icon" type="image/svg+xml" href="/conway-life-glider.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Game of Life</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
1
public/conway-life-glider.svg
Normal file
1
public/conway-life-glider.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="512px" height="512px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path fill="#000" d="M19.51 19.51V492.3H492.3V19.51H19.51zm18 18H171.1V171.1H37.51V37.51zm151.59 0h133.6V171.1H189.1V37.51zm151.6 0h133.6V171.1H340.7V37.51zm-84.8 9.94a56.85 56.85 0 0 0-56.8 56.85 56.85 56.85 0 0 0 56.8 56.9 56.85 56.85 0 0 0 56.9-56.9 56.85 56.85 0 0 0-56.9-56.85zM37.51 189.1H171.1v133.6H37.51V189.1zm151.59 0h133.6v133.6H189.1V189.1zm151.6 0h133.6v133.6H340.7V189.1zm66.9 10a56.85 56.85 0 0 0-56.8 56.8 56.85 56.85 0 0 0 56.8 56.9 56.85 56.85 0 0 0 56.8-56.9 56.85 56.85 0 0 0-56.8-56.8zM37.51 340.7H171.1v133.6H37.51V340.7zm151.59 0h133.6v133.6H189.1V340.7zm151.6 0h133.6v133.6H340.7V340.7zm-236.4 10.1a56.85 56.85 0 0 0-56.85 56.8 56.85 56.85 0 0 0 56.85 56.8 56.85 56.85 0 0 0 56.9-56.8 56.85 56.85 0 0 0-56.9-56.8zm151.6 0a56.85 56.85 0 0 0-56.8 56.8 56.85 56.85 0 0 0 56.8 56.8 56.85 56.85 0 0 0 56.9-56.8 56.85 56.85 0 0 0-56.9-56.8zm151.7 0a56.85 56.85 0 0 0-56.8 56.8 56.85 56.85 0 0 0 56.8 56.8 56.85 56.85 0 0 0 56.8-56.8 56.85 56.85 0 0 0-56.8-56.8z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -1,5 +1,5 @@
|
||||
import CellContainer from './CellContainer';
|
||||
import {useCallback, useEffect, useState} from 'react';
|
||||
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||
|
||||
import '../../styles/game.less';
|
||||
import ControlPanel from './ControlPanel';
|
||||
@ -13,12 +13,20 @@ export enum GameState {
|
||||
Running
|
||||
}
|
||||
|
||||
interface GameRules {
|
||||
dead: number[];
|
||||
alive: number[];
|
||||
spawn: number[];
|
||||
}
|
||||
|
||||
export interface GameOptions {
|
||||
gameState: GameState;
|
||||
width: number;
|
||||
height: number;
|
||||
speed: number;
|
||||
minSpeed: number;
|
||||
randomizerDensity: number;
|
||||
gameRules: GameRules;
|
||||
}
|
||||
|
||||
const defaultGameOptions: GameOptions = {
|
||||
@ -26,13 +34,30 @@ const defaultGameOptions: GameOptions = {
|
||||
width: 25,
|
||||
height: 25,
|
||||
speed: 1000,
|
||||
minSpeed: 100
|
||||
minSpeed: 10,
|
||||
randomizerDensity: 0.3,
|
||||
gameRules: {
|
||||
dead: [0,1,4,5,6,7,8],
|
||||
alive: [2],
|
||||
spawn: [3]
|
||||
}
|
||||
}
|
||||
|
||||
const Game = ({}: GameProps) => {
|
||||
const [gameOptions, setGameOptions] = useState<GameOptions>(defaultGameOptions);
|
||||
const [cells, setCells] = useState<boolean[]>([]);
|
||||
|
||||
const intervalRef = useRef<number|null>(null);
|
||||
|
||||
const {
|
||||
randomizerDensity,
|
||||
width,
|
||||
height,
|
||||
speed,
|
||||
gameState,
|
||||
gameRules
|
||||
} = gameOptions;
|
||||
|
||||
const handleStart = useCallback(() => {
|
||||
setGameOptions(prev => {
|
||||
prev.gameState = GameState.Running;
|
||||
@ -49,25 +74,25 @@ const Game = ({}: GameProps) => {
|
||||
|
||||
const handleRandomFill = useCallback(() => {
|
||||
const newCells: boolean[] = [];
|
||||
const totalCells = gameOptions.width * gameOptions.height;
|
||||
const totalCells = width * height;
|
||||
|
||||
for(let i = 0; i < totalCells; i++) {
|
||||
newCells.push(Math.random() < 0.5);
|
||||
newCells.push(Math.random() < randomizerDensity);
|
||||
}
|
||||
|
||||
setCells(newCells);
|
||||
}, []);
|
||||
}, [randomizerDensity, width, height]);
|
||||
|
||||
const handleClear = useCallback(() => {
|
||||
const newCells: boolean[] = [];
|
||||
const totalCells = gameOptions.width * gameOptions.height;
|
||||
const totalCells = width * height;
|
||||
|
||||
for(let i = 0; i < totalCells; i++) {
|
||||
newCells.push(false);
|
||||
}
|
||||
|
||||
setCells(newCells);
|
||||
}, []);
|
||||
}, [width, height]);
|
||||
|
||||
const handleCellUpdate = useCallback((newVal: boolean, position: number) => {
|
||||
setCells(prev => {
|
||||
@ -87,12 +112,69 @@ const Game = ({}: GameProps) => {
|
||||
})
|
||||
}, [gameOptions]);
|
||||
|
||||
const countNeighbours = useCallback((x: number, y: number) => {
|
||||
let cnt = 0;
|
||||
|
||||
for(let xMod = -1; xMod <= 1; xMod++) {
|
||||
for(let yMod = -1; yMod <= 1; yMod++) {
|
||||
const currX = x + xMod;
|
||||
const currY = y + yMod;
|
||||
|
||||
if(
|
||||
(xMod === 0 && yMod === 0) ||
|
||||
(currX < 0 || currX >= width) ||
|
||||
(currY < 0 || currX >= height)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(cells[currY * width + currX]) cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
return cnt;
|
||||
}, [cells]);
|
||||
|
||||
const calcNextIteration = useCallback(() => {
|
||||
const newCells: boolean[] = [];
|
||||
|
||||
for(let y = 0; y < height; y++) {
|
||||
for(let x = 0; x < width; x++) {
|
||||
const neighbourCnt = countNeighbours(x, y);
|
||||
|
||||
|
||||
if(gameRules.dead.includes(neighbourCnt)) {
|
||||
newCells[y * width + x] = false;
|
||||
} else if(gameRules.alive.includes(neighbourCnt)) {
|
||||
newCells[y * width + x] = cells[y * width + x];
|
||||
} else if(gameRules.spawn.includes(neighbourCnt)) {
|
||||
newCells[y * width + x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCells(newCells);
|
||||
}, [cells, width, height]);
|
||||
|
||||
useEffect(() => {
|
||||
handleClear();
|
||||
}, [handleClear]);
|
||||
|
||||
useEffect(() => {
|
||||
if(intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
|
||||
if(gameState !== GameState.Running) return;
|
||||
|
||||
intervalRef.current = setInterval(() => {
|
||||
calcNextIteration();
|
||||
}, speed);
|
||||
}, [gameState, speed, intervalRef, calcNextIteration]);
|
||||
|
||||
return (
|
||||
<div className='game-container'>
|
||||
<h1 className='title'>Game of Life</h1>
|
||||
<ControlPanel
|
||||
gameOptions={gameOptions}
|
||||
handleStart={handleStart}
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user