diff --git a/packages/game-client/src/App.jsx b/packages/game-client/src/App.jsx
index 1269dc3..2a35277 100644
--- a/packages/game-client/src/App.jsx
+++ b/packages/game-client/src/App.jsx
@@ -3,6 +3,7 @@ import Game from './Game';
import { Divider, Space, Checkbox, Flex, Select, Button } from 'antd';
import { stringToBoolean } from './utils';
import { allSnakesSelectOption } from './constants';
+import DirectionSelector from './components/DirectionSelector';
function App() {
const [recordInLocalStorage, setRecordInLocalStorage] = useState(
@@ -14,22 +15,31 @@ function App() {
const gameRef = useRef();
const [snakeIdList, setSnakeIdList] = useState([allSnakesSelectOption]);
- const [selectedSnake, setSelectedSnake] = useState(allSnakesSelectOption);
+ const [selectedSnake, setSelectedSnake] = useState(allSnakesSelectOption.id);
+ const [directions, setDirections] = useState({});
- function updateSnakeIdList(ids) {
- if (ids.length > 0) {
- if (!ids.includes(selectedSnake) && selectedSnake !== allSnakesSelectOption) {
- const [firstId] = ids;
- setSelectedSnake(firstId);
+ function updateSnakeIdList(snakes) {
+ if (snakes.length > 0) {
+ if (!snakes.find(({ id }) => id === selectedSnake) && selectedSnake !== allSnakesSelectOption.id) {
+ const [{ id }] = snakes;
+ setSelectedSnake(id);
}
-
- setSnakeIdList([allSnakesSelectOption].concat(ids));
+ setSnakeIdList([allSnakesSelectOption].concat(snakes));
} else {
setSelectedSnake(null);
setSnakeIdList([]);
}
}
+ function updateDirectionList(directions) {
+ setDirections(directions);
+ }
+
+ function updateSnakeDirection(snakeId, direction) {
+ setSelectedSnake(snakeId);
+ gameRef.current.setDirection(snakeId, direction);
+ }
+
return (
@@ -74,7 +84,7 @@ function App() {
onChange={(val) => {
setSelectedSnake(val);
}}
- options={snakeIdList.map((id) => {
+ options={snakeIdList.map(({ id }) => {
return { value: id, label: id };
})}
/>
@@ -90,11 +100,27 @@ function App() {
+
+ {snakeIdList.map((snake, index) => {
+ const { id, headColor, bodyColor } = snake;
+ return (
+
+ );
+ })}
+
);
diff --git a/packages/game-client/src/Game.jsx b/packages/game-client/src/Game.jsx
index 5d400d4..6778517 100644
--- a/packages/game-client/src/Game.jsx
+++ b/packages/game-client/src/Game.jsx
@@ -18,13 +18,16 @@ import styles from './app.module.css';
// }
const Game = forwardRef((props, ref) => {
- const { showCellId, isGamePaused, updateSnakeIdList } = props;
+ const { showCellId, isGamePaused, updateSnakeIdList, updateDirectionList } = props;
const [snakeId, setSnakeId] = useState(1);
// Keep the direction of the snakes inside useRef since we don't
// want to force rerender of the component when the user changes
// the direction.
- const { getDirection, onLeft, onRight, onUp, onDown, setDirection } = useDirection(defaultDirections, 1);
+ const { getDirection, onLeft, onRight, onUp, onDown, setDirection, directions } = useDirection(
+ defaultDirections,
+ 1,
+ );
useInput({ snakes, onUp, onDown, onLeft, onRight, snakeId });
@@ -54,9 +57,18 @@ const Game = forwardRef((props, ref) => {
});
useEffect(() => {
- updateSnakeIdList(Object.keys(snakes));
+ updateSnakeIdList(
+ Object.entries(snakes).map(([key, value]) => {
+ const { bodyColor, headColor } = value;
+ return { id: key, bodyColor, headColor };
+ }),
+ );
}, [snakes]);
+ useEffect(() => {
+ updateDirectionList(directions);
+ }, [directions]);
+
const { addSnakeToTrack, removeSnakeFromTracks, resetSnakeTrack } = useTicks({
moveForward,
spawnFood,
@@ -64,9 +76,9 @@ const Game = forwardRef((props, ref) => {
isGamePaused,
});
- function nextMove(snakeId = allSnakesSelectOption) {
+ function nextMove(snakeId = allSnakesSelectOption.id) {
if (snakeId) {
- moveForward(snakeId === allSnakesSelectOption ? Object.keys(snakes) : [snakeId]); // Move all the snakes available one step forward.
+ moveForward(snakeId === allSnakesSelectOption.id ? Object.keys(snakes) : [snakeId]); // Move all the snakes available one step forward.
}
}
@@ -81,6 +93,7 @@ const Game = forwardRef((props, ref) => {
nextMove,
prevMove,
spawnFood,
+ setDirection,
};
},
[],
diff --git a/packages/game-client/src/app.module.css b/packages/game-client/src/app.module.css
index a23a64a..9dff16d 100644
--- a/packages/game-client/src/app.module.css
+++ b/packages/game-client/src/app.module.css
@@ -1,6 +1,6 @@
.game {
display: flex;
- align-items: center;
justify-content: center;
+ margin-top: 30px;
height: 100vh;
}
diff --git a/packages/game-client/src/components/DirectionSelector.jsx b/packages/game-client/src/components/DirectionSelector.jsx
new file mode 100644
index 0000000..649655a
--- /dev/null
+++ b/packages/game-client/src/components/DirectionSelector.jsx
@@ -0,0 +1,46 @@
+import React, { Fragment, useState } from 'react';
+import { Radio, Row, Col, Space } from 'antd';
+import styles from './directionselector.module.css';
+
+function DirectionSelector({ id, headColor, bodyColor, direction, updateSnakeDirection }) {
+ return (
+
+
+
+
{
+ updateSnakeDirection(id, e.target.value);
+ }}
+ value={direction}
+ >
+
+
+
+ W
+
+
+
+
+ N
+
+
+
+ S
+
+
+
+
+ E
+
+
+
+
+
+
+ );
+}
+
+export default DirectionSelector;
diff --git a/packages/game-client/src/components/directionselector.module.css b/packages/game-client/src/components/directionselector.module.css
new file mode 100644
index 0000000..2cd8919
--- /dev/null
+++ b/packages/game-client/src/components/directionselector.module.css
@@ -0,0 +1,30 @@
+.radio-button {
+ margin: 10px;
+ padding: 6px;
+}
+
+.colors-container {
+ margin-top: 10px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.head {
+ width: 50px;
+ height: 50px;
+}
+
+.body {
+ width: 30px;
+ height: 30px;
+}
+
+.id {
+ text-align: center;
+}
+
+.container {
+ border: 1px solid black;
+ border-radius: 15px;
+}
diff --git a/packages/game-client/src/constants.js b/packages/game-client/src/constants.js
index 9c2f4ac..0dd4786 100644
--- a/packages/game-client/src/constants.js
+++ b/packages/game-client/src/constants.js
@@ -5,7 +5,7 @@ const GRID_WIDTH = 30 * 13;
const GRID_HEIGHT = 30 * 13;
const CELL_DIMENSION = 30;
-export const allSnakesSelectOption = 'All';
+export const allSnakesSelectOption = { id: 'All' };
if (GRID_HEIGHT % CELL_DIMENSION !== 0) {
throw new Error('GRID_HEIGHT is not divislbe by CELL_DIMENSION');
@@ -49,7 +49,7 @@ const FOOD_TICKS = {
// if they are unique.
areValuesUnique(SNAKE_TICKS);
-const DEFAULT_TRACK = SNAKE_TICKS.ONE.TYPE;
+const DEFAULT_TRACK = SNAKE_TICKS.QUARTER.TYPE;
const FOOD_EFFECTS = {
GROW: 'grow',
diff --git a/packages/game-client/src/hooks/useDirection.js b/packages/game-client/src/hooks/useDirection.js
index 26564b2..d949a14 100644
--- a/packages/game-client/src/hooks/useDirection.js
+++ b/packages/game-client/src/hooks/useDirection.js
@@ -1,10 +1,11 @@
-import React, { useRef } from 'react';
+import React, { useEffect, useRef, useState } from 'react';
import { DIRECTIONS } from '../constants';
import { getOppositeDirection } from '../helpers';
import { cloneDeep } from 'lodash';
const useDirection = (initialState, snakeId) => {
- const directions = useRef(cloneDeep({ ...initialState })); // useRef is changing the supplied object :(
+ const directionsRef = useRef(cloneDeep({ ...initialState })); // useRef is changing the supplied object :(
+ const [directions, setDirections] = useState(directionsRef.current);
const onUp = () => {
const direction = getDirection(snakeId);
@@ -44,15 +45,16 @@ const useDirection = (initialState, snakeId) => {
};
const setDirection = (snakeId, direction) => {
- directions.current[snakeId] = direction;
+ directionsRef.current[snakeId] = direction;
+ setDirections({ ...directionsRef.current });
};
const getDirection = (snakeId) => {
- return directions.current[snakeId];
+ return directionsRef.current[snakeId];
};
return {
- directions: directions.current,
+ directions,
onUp,
onDown,
onLeft,
diff --git a/packages/game-client/src/hooks/useSnakes.js b/packages/game-client/src/hooks/useSnakes.js
index 852dcf4..d43938b 100644
--- a/packages/game-client/src/hooks/useSnakes.js
+++ b/packages/game-client/src/hooks/useSnakes.js
@@ -121,7 +121,10 @@ const useSnakes = ({
// Remove the tail to make it look like the snake has moved forward.
const tailKey = snakesRef.current[snakeId].list.pop(); // mutates.
- delete snakesRef.current[snakeId].hash[tailKey];
+ if (tailKey !== newHeadKey) {
+ // When snake bites its own tail... It survives!
+ delete snakesRef.current[snakeId].hash[tailKey];
+ }
}
} else {
throw new Error('The id mentioned is not in the hash!');