Skip to content

Commit

Permalink
Merge pull request #19 from shibisuriya/feat/tracks
Browse files Browse the repository at this point in the history
Feat/tracks
  • Loading branch information
shibisuriya authored Oct 3, 2023
2 parents e6d8c4e + 11ff10a commit a78ee55
Show file tree
Hide file tree
Showing 11 changed files with 206 additions and 92 deletions.
16 changes: 13 additions & 3 deletions packages/game-client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,20 @@ function App() {

useInput({ snakes, onUp, onDown, onLeft, onRight, snakeId });

const { food, getFood, removeFood, spawnFood, isFood, setFood } = useFood({ initialFoodState });

const getSnakeCells = () => allSnakeCells();

const { food, getFood, removeFood, spawnFood, isFood, setFood } = useFood({ initialFoodState, getSnakeCells });

const getTracks = () => {
return { addSnakeToTrack, removeSnakeFromTracks, resetSnakeTrack };
};

// Don't keep direction of the snakes inside of useState()...
const {
snakes,
moveForward,
getSnakeCells: allSnakeCells,
getAllSnakeIds,
} = useSnakes({
initialSnakesState,
getDirection,
Expand All @@ -44,9 +49,14 @@ function App() {
setDirection,
isFood,
setFood,
getTracks,
});

useTicks({ moveForward, snakes, food, spawnFood, getSnakeCells });
const { addSnakeToTrack, removeSnakeFromTracks, resetSnakeTrack } = useTicks({
moveForward,
spawnFood,
getAllSnakeIds,
});

return (
<div className={styles.game}>
Expand Down
2 changes: 1 addition & 1 deletion packages/game-client/src/Grid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function Cell({ x, y, color }) {
function Food({ x, y, type }) {
return (
<div
className={`${styles.cell} ${styles.food} ${styles[type]}`}
className={`${styles.cell} ${styles.food} ${styles[type.toLowerCase()]}`}
style={{
top: `${x * CELL_DIMENSION}px`,
left: `${y * CELL_DIMENSION}px`,
Expand Down
2 changes: 1 addition & 1 deletion packages/game-client/src/computed.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NUMBER_OF_COLUMNS, NUMBER_OF_ROWS } from './constants';
import { generateKey, generateValue } from './utils';
import { generateKey, generateValue } from './helpers';

const generateGridMap = () => {
const hash = {};
Expand Down
53 changes: 30 additions & 23 deletions packages/game-client/src/constants.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { findKeyByValue, areValuesUnique } from './utils';

// in px (pixels)
const GRID_WIDTH = 30 * 50;
const GRID_HEIGHT = 30 * 30;
Expand All @@ -23,31 +25,36 @@ const DIRECTIONS = {

const DEFAULT_DIRECTION = DIRECTIONS.RIGHT;

const FOOD_TYPES = {
PROTEIN: { TYPE: 'protein', chance: 95, growth: 1 },
WALLRIDER_PORTION: { TYPE: 'wallrider-portion', chance: 2, growth: 0 },
REDBULL: { TYPE: 'redbull', chance: 3, growth: 0 },
FILLET: { TYPE: 'fillet', chance: 0, growth: 2 },
};

const TICK_TYPES = {
SNAKES: 'snakes',
FOOD: 'food',
const SNAKE_TICKS = {
ONE: { TYPE: 'ONE', DURATION: 1 * 1000 },
HALF: { TYPE: 'HALF', DURATION: 0.5 * 1000 },
QUARTER: { TYPE: 'QUARTER', DURATION: 0.25 * 1000 },
TWO_TENTH: { TYPE: 'TWO_TENTH', DURATION: 0.2 * 1000 },
ONE_TENTH: {
TYPE: 'ONE_TENTH',
DURATION: 1000 * 0.1,
},
};

const TICKS = {
[TICK_TYPES.FOOD]: {
0.1: 1000 * 0.1,
},
[TICK_TYPES.SNAKES]: {
// 1: 1 * 1000,
// 0.5: 0.5 * 1000,
// 0.25: 0.25 * 1000,
0.1: 1000 * 0.1,
const FOOD_TICKS = {
ONE_TENTH: {
TYPE: 'ONE_TENTH',
DURATION: 1000 * 0.1,
},
};

const SPEED = 1 * 100;
// Two setInterval shouldn't have the same duration, so check
// if they are unique.
areValuesUnique(SNAKE_TICKS);

const DEFAULT_TRACK = SNAKE_TICKS.QUARTER.TYPE;

const FOOD_TYPES = {
PROTEIN: { TYPE: 'PROTEIN', chance: 95, growth: 1 },
WALLRIDER_PORTION: { TYPE: 'WALLRIDER_PORTION', chance: 2, growth: 0 },
REDBULL: { TYPE: 'REDBULL', chance: 3, growth: 0, speed: SNAKE_TICKS.ONE_TENTH.TYPE },
FILLET: { TYPE: 'FILLET', chance: 0, growth: 2 },
};

const defaultDirections = {
1: DIRECTIONS.DOWN,
Expand All @@ -66,7 +73,7 @@ export {
DIRECTIONS,
DEFAULT_DIRECTION,
FOOD_TYPES,
TICKS,
TICK_TYPES,
SPEED,
DEFAULT_TRACK,
SNAKE_TICKS,
FOOD_TICKS,
};
2 changes: 1 addition & 1 deletion packages/game-client/src/grid.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
background-color: #d62828;
}

.food.wallrider-portion {
.food.wallrider_portion {
background-color: cyan;
}

Expand Down
36 changes: 36 additions & 0 deletions packages/game-client/src/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { NUMBER_OF_COLUMNS, NUMBER_OF_ROWS, DIRECTIONS } from './constants';

const isCellValid = (i, j) => {
return !(i > NUMBER_OF_ROWS || j > NUMBER_OF_COLUMNS);
};

const generateKey = (i, j) => {
if (!isCellValid(i, j)) {
throw new Error(`Invalid coordinates! ${i} ${j}`);
}
return `${i}-${j}`;
};

const generateValue = (x, y) => {
if (!isCellValid(x, y)) {
throw new Error(`Invalid coordinates! ${i} ${j}`);
}
return { x, y };
};

const getOppositeDirection = (direction) => {
switch (direction) {
case DIRECTIONS.DOWN:
return DIRECTIONS.UP;
case DIRECTIONS.UP:
return DIRECTIONS.DOWN;
case DIRECTIONS.LEFT:
return DIRECTIONS.RIGHT;
case DIRECTIONS.RIGHT:
return DIRECTIONS.LEFT;
default:
throw new Error(`Invalid direction, ${direction}.`);
}
};

export { getOppositeDirection, generateValue, generateKey, isCellValid };
2 changes: 1 addition & 1 deletion packages/game-client/src/hooks/useDirection.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef } from 'react';
import { DIRECTIONS } from '../constants';
import { getOppositeDirection } from '../utils';
import { getOppositeDirection } from '../helpers';

const useDirection = (initialState, snakeId) => {
const directions = useRef({ ...initialState }); // useRef is changing the supplied object :(
Expand Down
16 changes: 10 additions & 6 deletions packages/game-client/src/hooks/useFood.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react';
import { generateKey, generateValue, generateRandomNumber, getSnakeCellsAsHash } from '../utils';
import { generateRandomNumber } from '../utils';
import { generateKey, generateValue } from '../helpers';
import { GRID_MAP } from '../computed';
import cloneDeep from 'lodash/cloneDeep';
import { FOOD_TYPES } from '../constants';
Expand All @@ -24,15 +25,15 @@ const whichFoodToSpawn = () => {
return FOOD_TYPES.PROTEIN;
};

const useFood = ({ initialFoodState = {}, getSnakes }) => {
const useFood = ({ initialFoodState = {}, getSnakeCells }) => {
const [map, setMap] = useState(initialFoodState);
const mapRef = useRef(map);

const getFood = () => {
return cloneDeep(mapRef.current); // Prevents users from modifiying the data.
};

const spawnFood = (getSnakeCells) => {
const spawnFood = () => {
const snakeCells = getSnakeCells();
const emptyCells = {};

Expand All @@ -51,14 +52,17 @@ const useFood = ({ initialFoodState = {}, getSnakes }) => {
}
};

const setFood = (x, y, type = FOOD_TYPES.PROTEIN.TYPE) => {
const setFood = (x, y, type) => {
if (!type) {
throw new Error('Please mention the food type that needs to be added to the map!');
}
const key = generateKey(x, y);
if (key in mapRef.current) {
throw new Error('The cell you supplied is already a food.');
} else {
Object.assign(mapRef.current, { [key]: { ...generateValue(x, y), type } });
}
setMap(mapRef.current);
setMap({ ...mapRef.current });
};

const removeFood = (x, y) => {
Expand All @@ -68,7 +72,7 @@ const useFood = ({ initialFoodState = {}, getSnakes }) => {
} else {
const removedFood = cloneDeep(mapRef.current[key]);
delete mapRef.current[key];
setMap(mapRef.current);
setMap({ ...mapRef.current });
return removedFood;
}
};
Expand Down
30 changes: 26 additions & 4 deletions packages/game-client/src/hooks/useSnakes.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
import React, { useRef, useState } from 'react';
import { generateKey } from '../utils';
import { generateKey } from '../helpers';
import cloneDeep from 'lodash/cloneDeep';
import { DIRECTIONS, NUMBER_OF_ROWS, NUMBER_OF_COLUMNS, defaultDirections, FOOD_TYPES } from '../constants';

const useSnakes = ({ initialSnakesState, getDirection, getFood, removeFood, setDirection, isFood, setFood }) => {
const useSnakes = ({
initialSnakesState,
getDirection,
getFood,
removeFood,
setDirection,
isFood,
setFood,
getTracks,
}) => {
const [snakes, setSnakes] = useState(initialSnakesState);
const snakesRef = useRef(snakes);

const getAllSnakeIds = () => {
const allSnakeIds = Object.keys(snakesRef.current).reduce((hash, snake) => {
hash[snake] = {};
return hash;
}, {});
return allSnakeIds;
};

const getSnakeCells = () => {
// return cells that are occupied by snakes.
return cloneDeep(
Expand Down Expand Up @@ -63,7 +80,7 @@ const useSnakes = ({ initialSnakesState, getDirection, getFood, removeFood, setD
}
};

const moveForward = (snakeId = 1) => {
const moveForward = (snakeId) => {
if (snakeId in snakesRef.current) {
const updatedHash = { ...snakesRef.current[snakeId].hash };
const updatedList = [...snakesRef.current[snakeId].list];
Expand Down Expand Up @@ -102,6 +119,11 @@ const useSnakes = ({ initialSnakesState, getDirection, getFood, removeFood, setD
if (newHeadKey in getFood()) {
// TODO: refactor...
const removedFood = removeFood(newHead.x, newHead.y);
const { speed } = FOOD_TYPES[removedFood.type];
if (speed) {
const { addSnakeToTrack, removeSnakeFromTracks, resetSnakeTrack } = getTracks();
addSnakeToTrack(speed, snakeId);
}
switch (removedFood.type) {
case FOOD_TYPES.PROTEIN.TYPE:
updateSnake(snakeId, { hash: updatedHash, list: updatedList });
Expand Down Expand Up @@ -142,7 +164,7 @@ const useSnakes = ({ initialSnakesState, getDirection, getFood, removeFood, setD
// }
};

return { snakes, moveForward, removeSnake, resetSnake, getSnakes, getSnakeCells };
return { snakes, moveForward, removeSnake, resetSnake, getSnakes, getSnakeCells, getAllSnakeIds };
};

export { useSnakes };
Loading

0 comments on commit a78ee55

Please sign in to comment.