add game logic
This commit is contained in:
parent
591ed62ccd
commit
fbd1e0d932
@ -2,9 +2,9 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<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" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + React + TS</title>
|
<title>Game of Life</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<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 CellContainer from './CellContainer';
|
||||||
import {useCallback, useEffect, useState} from 'react';
|
import {useCallback, useEffect, useRef, useState} from 'react';
|
||||||
|
|
||||||
import '../../styles/game.less';
|
import '../../styles/game.less';
|
||||||
import ControlPanel from './ControlPanel';
|
import ControlPanel from './ControlPanel';
|
||||||
@ -13,12 +13,20 @@ export enum GameState {
|
|||||||
Running
|
Running
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GameRules {
|
||||||
|
dead: number[];
|
||||||
|
alive: number[];
|
||||||
|
spawn: number[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface GameOptions {
|
export interface GameOptions {
|
||||||
gameState: GameState;
|
gameState: GameState;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
speed: number;
|
speed: number;
|
||||||
minSpeed: number;
|
minSpeed: number;
|
||||||
|
randomizerDensity: number;
|
||||||
|
gameRules: GameRules;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultGameOptions: GameOptions = {
|
const defaultGameOptions: GameOptions = {
|
||||||
@ -26,13 +34,30 @@ const defaultGameOptions: GameOptions = {
|
|||||||
width: 25,
|
width: 25,
|
||||||
height: 25,
|
height: 25,
|
||||||
speed: 1000,
|
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 Game = ({}: GameProps) => {
|
||||||
const [gameOptions, setGameOptions] = useState<GameOptions>(defaultGameOptions);
|
const [gameOptions, setGameOptions] = useState<GameOptions>(defaultGameOptions);
|
||||||
const [cells, setCells] = useState<boolean[]>([]);
|
const [cells, setCells] = useState<boolean[]>([]);
|
||||||
|
|
||||||
|
const intervalRef = useRef<number|null>(null);
|
||||||
|
|
||||||
|
const {
|
||||||
|
randomizerDensity,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
speed,
|
||||||
|
gameState,
|
||||||
|
gameRules
|
||||||
|
} = gameOptions;
|
||||||
|
|
||||||
const handleStart = useCallback(() => {
|
const handleStart = useCallback(() => {
|
||||||
setGameOptions(prev => {
|
setGameOptions(prev => {
|
||||||
prev.gameState = GameState.Running;
|
prev.gameState = GameState.Running;
|
||||||
@ -49,25 +74,25 @@ const Game = ({}: GameProps) => {
|
|||||||
|
|
||||||
const handleRandomFill = useCallback(() => {
|
const handleRandomFill = useCallback(() => {
|
||||||
const newCells: boolean[] = [];
|
const newCells: boolean[] = [];
|
||||||
const totalCells = gameOptions.width * gameOptions.height;
|
const totalCells = width * height;
|
||||||
|
|
||||||
for(let i = 0; i < totalCells; i++) {
|
for(let i = 0; i < totalCells; i++) {
|
||||||
newCells.push(Math.random() < 0.5);
|
newCells.push(Math.random() < randomizerDensity);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCells(newCells);
|
setCells(newCells);
|
||||||
}, []);
|
}, [randomizerDensity, width, height]);
|
||||||
|
|
||||||
const handleClear = useCallback(() => {
|
const handleClear = useCallback(() => {
|
||||||
const newCells: boolean[] = [];
|
const newCells: boolean[] = [];
|
||||||
const totalCells = gameOptions.width * gameOptions.height;
|
const totalCells = width * height;
|
||||||
|
|
||||||
for(let i = 0; i < totalCells; i++) {
|
for(let i = 0; i < totalCells; i++) {
|
||||||
newCells.push(false);
|
newCells.push(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCells(newCells);
|
setCells(newCells);
|
||||||
}, []);
|
}, [width, height]);
|
||||||
|
|
||||||
const handleCellUpdate = useCallback((newVal: boolean, position: number) => {
|
const handleCellUpdate = useCallback((newVal: boolean, position: number) => {
|
||||||
setCells(prev => {
|
setCells(prev => {
|
||||||
@ -87,12 +112,69 @@ const Game = ({}: GameProps) => {
|
|||||||
})
|
})
|
||||||
}, [gameOptions]);
|
}, [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(() => {
|
useEffect(() => {
|
||||||
handleClear();
|
handleClear();
|
||||||
}, [handleClear]);
|
}, [handleClear]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(gameState !== GameState.Running) return;
|
||||||
|
|
||||||
|
intervalRef.current = setInterval(() => {
|
||||||
|
calcNextIteration();
|
||||||
|
}, speed);
|
||||||
|
}, [gameState, speed, intervalRef, calcNextIteration]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='game-container'>
|
<div className='game-container'>
|
||||||
|
<h1 className='title'>Game of Life</h1>
|
||||||
<ControlPanel
|
<ControlPanel
|
||||||
gameOptions={gameOptions}
|
gameOptions={gameOptions}
|
||||||
handleStart={handleStart}
|
handleStart={handleStart}
|
||||||
|
|||||||
@ -4,6 +4,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.control-panel {
|
.control-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user