Skip to content

Instantly share code, notes, and snippets.

@azz0r
Created May 11, 2023 18:11
Show Gist options
  • Save azz0r/6e1af83f0432a8363ffa6964d9f86581 to your computer and use it in GitHub Desktop.
Save azz0r/6e1af83f0432a8363ffa6964d9f86581 to your computer and use it in GitHub Desktop.
import React, { useReducer, useEffect } from 'react';
const userReducer = (state, action) => {
switch (action.type) {
case 'MOVE_UP':
return { ...state, position: [state.position[0] - 1, state.position[1]] };
case 'MOVE_DOWN':
return { ...state, position: [state.position[0] + 1, state.position[1]] };
case 'MOVE_LEFT':
return { ...state, position: [state.position[0], state.position[1] - 1] };
case 'MOVE_RIGHT':
return { ...state, position: [state.position[0], state.position[1] + 1] };
case 'CLAIM_AREA':
return { ...state, claimedArea: true };
default:
return state;
}
};
const moveEnemy = (enemy) => {
const { position, direction } = enemy;
let [row, col] = position;
// Update the enemy's position based on its direction
switch (direction) {
case 'UP':
row--;
break;
case 'DOWN':
row++;
break;
case 'LEFT':
col--;
break;
case 'RIGHT':
col++;
break;
default:
break;
}
return { ...enemy, position: [row, col] };
};
const enemyReducer = (state, action) => {
switch (action.type) {
case 'ADD_ENEMY':
return [...state, action.payload];
case 'MOVE_ENEMIES':
return state.map((enemy) => moveEnemy(enemy));
case 'FREEZE_ENEMY':
return state.map((enemy) =>
enemy.position.toString() === action.payload.toString()
? { ...enemy, frozen: true }
: enemy
);
case 'UNFREEZE_ENEMY':
return state.map((enemy) =>
enemy.position.toString() === action.payload.toString()
? { ...enemy, frozen: false }
: enemy
);
default:
return state;
}
};
const gameReducer = (state, action) => {
switch (action.type) {
case 'SET_LIVES':
return { ...state, lives: action.payload };
case 'DECREASE_LIVES':
return { ...state, lives: state.lives - 1 };
case 'SET_PAUSED':
return { ...state, paused: action.payload };
case 'SET_STARTED':
return { ...state, started: action.payload };
case 'SET_LEVEL':
return { ...state, level: action.payload };
case 'SET_TARGET_PERCENTAGE':
return { ...state, targetPercentage: action.payload };
case 'SET_SCORE':
return { ...state, score: action.payload };
case 'INCREMENT_SCORE':
return { ...state, score: state.score + action.payload };
case 'SET_TIMER':
return { ...state, timer: action.payload };
case 'DECREASE_TIMER':
return { ...state, timer: state.timer - 1 };
case 'SET_GAME_OVER':
return { ...state, gameOver: true };
default:
return state;
}
};
const Board = ({ rows, columns, markerPosition, filledAreas }) => {
const gridStyle = {
display: 'grid',
gridTemplateColumns: `repeat(${columns}, 1fr)`,
gap: '1px',
backgroundColor: 'black',
padding: '10px',
};
const cellStyle = {
width: '20px',
height: '20px',
backgroundColor: 'white',
};
const filledCellStyle = {
backgroundColor: 'blue',
};
const isCellFilled = (row, col) => {
return filledAreas.some((area) => area?.[row]?.[col]);
};
return (
<div style={gridStyle}>
{Array.from({ length: rows }, (_, row) =>
Array.from({ length: columns }, (_, col) => (
<div
key={`${row}-${col}`}
style={{
...cellStyle,
...(isCellFilled(row, col) && filledCellStyle),
}}
>
{markerPosition[0] === row && markerPosition[1] === col && (
<div
style={{
width: '10px',
height: '10px',
borderRadius: '50%',
backgroundColor: 'red',
margin: '5px',
}}
/>
)}
</div>
))
)}
</div>
);
};
const calculatePercentageFilled = (filledAreas, totalCells) => {
const filledCells = filledAreas.flat().filter((cell) => cell).length;
return (filledCells / totalCells) * 100;
};
const GameContainer = () => {
const rows = 10; // Number of rows in the game board grid
const columns = 10; // Number of columns in the game board grid
const maxLevels = 5; // Maximum number of levels in the game
// Initial state for user position
const initialUserState = {
position: [0, 0],
claimedArea: false,
};
// Initial state for enemy positions
const initialEnemyState = [
{ position: [2, 2], direction: 'DOWN', frozen: false },
{ position: [7, 7], direction: 'UP', frozen: false },
];
// Initial state for game logic
const initialGameState = {
lives: 3,
paused: false,
started: false,
level: 1,
targetPercentage: 50,
score: 0,
timer: 10,
gameOver: false,
};
const [userState, userDispatch] = useReducer(userReducer, initialUserState);
const [enemyState, enemyDispatch] = useReducer(enemyReducer, initialEnemyState);
const [gameState, gameDispatch] = useReducer(gameReducer, initialGameState);
const onStart = () => gameDispatch({ type: 'SET_STARTED', payload: true });
const onResume = () => gameDispatch({ type: 'SET_PAUSED', payload: false });
useEffect(() => {
const handleKeyDown = (event) => {
if (!gameState.paused && gameState.started) {
switch (event.key) {
case 'w':
userDispatch({ type: 'MOVE_UP' });
break;
case 's':
userDispatch({ type: 'MOVE_DOWN' });
break;
case 'a':
userDispatch({ type: 'MOVE_LEFT' });
break;
case 'd':
userDispatch({ type: 'MOVE_RIGHT' });
break;
default:
break;
}
}
};
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [gameState.paused, gameState.started, userDispatch]);
useEffect(() => {
const intervalId = setInterval(() => {
if (!gameState.paused && gameState.started && !gameState.gameOver) {
enemyDispatch({ type: 'MOVE_ENEMIES' });
const playerPosition = userState.position;
const enemyPositions = enemyState.map((enemy) => enemy.position);
if (enemyPositions.some((pos) => pos.toString() === playerPosition.toString())) {
gameDispatch({ type: 'DECREASE_LIVES' });
if (gameState.lives === 0) {
gameDispatch({ type: 'SET_GAME_OVER' });
}
}
if (userState.claimedArea) {
const filledAreas = [...enemyState, { position: userState.position, frozen: false }];
const percentageFilled = calculatePercentageFilled(filledAreas, rows * columns);
gameDispatch({ type: 'INCREMENT_SCORE', payload: percentageFilled });
if (percentageFilled >= gameState.targetPercentage) {
const nextLevel = gameState.level + 1;
const nextTargetPercentage = gameState.targetPercentage + 10;
const nextTimer = gameState.timer - 1;
gameDispatch({ type: 'SET_LEVEL', payload: nextLevel });
gameDispatch({ type: 'SET_TARGET_PERCENTAGE', payload: nextTargetPercentage });
gameDispatch({ type: 'SET_TIMER', payload: nextTimer });
if (nextLevel > maxLevels) {
gameDispatch({ type: 'SET_GAME_OVER' });
} else {
// Reset game state and proceed to the next level
userDispatch(initialUserState);
enemyDispatch(initialEnemyState);
}
} else {
userDispatch({ type: 'CLAIM_AREA' });
}
}
}
}, gameState.timer * 1000);
return () => {
clearInterval(intervalId);
};
}, [
gameState.paused,
gameState.started,
gameState.gameOver,
gameState.lives,
gameState.targetPercentage,
gameState.level,
gameState.timer,
userState.position,
userState.claimedArea,
enemyState,
userDispatch,
enemyDispatch,
gameDispatch,
]);
// Add your game logic here, including handling user input and dispatching actions
if (gameState.lives === 0) {
return <div>Game Over</div>;
}
if (gameState.paused) {
return (
<>
<div>Paused</div>
<button onClick={onResume}>Resume</button>
</>
)
}
if (!gameState.started) {
return (
<>
<div>Press Start to begin</div>
<button onClick={onStart}>Start</button>
</>
)
}
return (
<div>
<Board rows={10} columns={10} markerPosition={userState.position} filledAreas={enemyState} />
</div>
);
};
export default GameContainer;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment