Skip to content

Commit

Permalink
Merge pull request #48 from shibisuriya/chore/scripted-bot-cleanup
Browse files Browse the repository at this point in the history
Chore/scripted bot cleanup
  • Loading branch information
shibisuriya authored Dec 27, 2023
2 parents e1b8bb7 + 45b1f06 commit 5cb0aa7
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 68 deletions.
18 changes: 7 additions & 11 deletions packages/game-client/src/Grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,22 +77,22 @@ class Grid {
(event) => {
const key = event.key.toLowerCase();
if (['w', 'arrowup'].includes(key)) {
this.snakes[4].changeDirection(DIRECTIONS.UP);
this.snakes['player'].changeDirection(DIRECTIONS.UP);
// Object.values(this.snakes).forEach((snake) => {
// snake.changeDirection(DIRECTIONS.UP);
// });
} else if (['s', 'arrowdown'].includes(key)) {
this.snakes[4].changeDirection(DIRECTIONS.DOWN);
this.snakes['player'].changeDirection(DIRECTIONS.DOWN);
// Object.values(this.snakes).forEach((snake) => {
// snake.changeDirection(DIRECTIONS.DOWN);
// });
} else if (['a', 'arrowleft'].includes(key)) {
this.snakes[4].changeDirection(DIRECTIONS.LEFT);
this.snakes['player'].changeDirection(DIRECTIONS.LEFT);
// Object.values(this.snakes).forEach((snake) => {
// snake.changeDirection(DIRECTIONS.LEFT);
// });
} else if (['d', 'arrowright'].includes(key)) {
this.snakes[4].changeDirection(DIRECTIONS.RIGHT);
this.snakes['player'].changeDirection(DIRECTIONS.RIGHT);
// Object.values(this.snakes).forEach((snake) => {
// snake.changeDirection(DIRECTIONS.RIGHT);
// });
Expand Down Expand Up @@ -138,7 +138,7 @@ class Grid {
this.snakes = {};
this.bots = {}; // Keep a separate hashMap of snakes which are bots.
for (const [snakeId, initialSnakeState] of Object.entries(initialSnakesState)) {
const snake = new Snake(initialSnakeState);
const snake = new Snake(snakeId, initialSnakeState);
snake.die = (causeOfDeath) => {
// When a snake dies his body is converted to food named fillets.
const removedSnake = this.removeSnakeFromGrid(snakeId);
Expand Down Expand Up @@ -342,12 +342,8 @@ class Grid {
snake.consume(food);
});

// End the game if there is only 1 or 0 player in the map,
// since the last person existing in the map wins the game.
// This has side effects, i.e., you won't be able to play
// by yourselves alone in the map for testing purposes as well...
// In that case commnet out this piece of code.
if (Object.values(this.snakes).length <= 1) {
// If the player is dead the game is over, in bots mode...
if (!('player' in this.snakes)) {
this.endGame();
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/game-client/src/Grid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ function Grid({ view, annotations, showCellId, isGameOver }) {
<div className={styles.grid} style={{ width: `${GRID_WIDTH}px`, height: `${GRID_HEIGHT}px` }}>
{/* // I have separate <div />(s) for annotations and game, because these can overlap with each other. */}

{isGameOver && <div className={styles['game-over-banner']}>Game Over</div>}

{view.map((cell) => {
const { x, y, color, animationClass } = cell;
return (
Expand Down Expand Up @@ -47,6 +45,8 @@ function Grid({ view, annotations, showCellId, isGameOver }) {
</div>
);
})}

{isGameOver && <div className={styles['game-over-banner']}>Game Over</div>}
</div>
);
}
Expand Down
11 changes: 10 additions & 1 deletion packages/game-client/src/Snake.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import cloneDeep from 'lodash/cloneDeep';
import { BOTS } from './bots';

class Snake {
constructor(snake) {
constructor(snakeId, snake) {
// Organise the cells data into a hashMap so that it is easier to
// perform computation on this data.
this.snakeId = snakeId;
this.bodyColor = snake.bodyColor;
this.headColor = snake.headColor;
this.defaultTick = snake.defaultTick;
Expand Down Expand Up @@ -232,6 +233,7 @@ class Snake {
move: this.changeDirection.bind(this),
updateAnnotations: this.updateAnnotations.bind(this),
gameData: this.game.getGameData(),
self: this,
});
}

Expand Down Expand Up @@ -332,6 +334,13 @@ class Snake {
}
return body;
}

getCells() {
return this.keys.reduce((cells, key) => {
cells.push(this.hash[key]);
return cells;
}, []);
}
}

export default Snake;
41 changes: 23 additions & 18 deletions packages/game-client/src/bots/scripted-bots/bots/headHunter.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
import { astar } from '../algorithms/astar';
import { findDirectionUsingNeckAndHead } from '../../../helpers';
import { findDirectionUsingNeckAndHead, excludeSelf } from '../../../helpers';

const headHunter = ({ move, updateAnnotations, gameData }) => {
// const start = { x: 1, y: 1 };
// const end = { x: 10, y: 10 };
// const obstacles = [
// { x: 3, y: 2 },
// { x: 3, y: 3 },
// { x: 3, y: 4 },
// ];
const two = gameData.snakes[2]; // bot
const botHead = two.getHead();
const four = gameData.snakes[4];
const targetHead = four.getHead();
const annotations = astar(botHead, targetHead, two.getBody().concat(four.getBody()));
const headHunter = ({ move, updateAnnotations, gameData, self }) => {
// This bot is sucidal, it tries chases down the player and tries to have a head to head collison and
// killing the player...
// Not that the variable `gameData` will contain data of all the snakes including the current snake (self)...

const opponents = excludeSelf({ myId: self.snakeId, snakes: gameData.snakes });
const player = opponents['player']; // The user who plays the game will have the snakeId 'player'...

const getObstacles = () => {
const cells = self.getBody().concat(player.getBody());
for (const [snakeId, snake] of Object.entries(opponents)) {
if (snakeId !== self.snakeId && snakeId !== 'player') {
cells.push(...snake.getCells());
}
}
return cells;
};

// The object of the bots is to kill the player...
const path = astar(self.getHead(), player.getHead(), getObstacles());
// updateAnnotations(annotations);

const [_, cellToMoveTo] = annotations;
const moveDir = findDirectionUsingNeckAndHead(two.getHead(), cellToMoveTo);
const [_, cellToMoveTo] = path;
const moveDir = findDirectionUsingNeckAndHead(self.getHead(), cellToMoveTo);
move(moveDir);
console.log('Cell to move to -> ', cellToMoveTo, ' Bot head -> ', two.getHead());
console.log('Shibi headhunterbot', Date.now(), ' ', gameData);
};

export { headHunter };
72 changes: 36 additions & 36 deletions packages/game-client/src/computed.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,25 @@ const generateGridMap = () => {
const GRID_MAP = generateGridMap();

const initialSnakesState = {
// 1: {
// headColor: 'red',
// bodyColor: 'yellow',
// cells: [
// { x: 0, y: 6 }, // Head
// { x: 0, y: 5 },
// { x: 0, y: 4 },
// { x: 0, y: 3 },
// { x: 0, y: 2 },
// { x: 0, y: 1 },
// { x: 0, y: 0 }, // Tail
// ],
// isBot: true,
// direction: DIRECTIONS.DOWN,
// defaultTick: SNAKE_TICKS.ONE.TYPE,
1: {
headColor: 'red',
bodyColor: 'yellow',
cells: [
{ x: 0, y: 6 }, // Head
{ x: 0, y: 5 },
{ x: 0, y: 4 },
{ x: 0, y: 3 },
{ x: 0, y: 2 },
{ x: 0, y: 1 },
{ x: 0, y: 0 }, // Tail
],
isBot: true,
direction: DIRECTIONS.DOWN,
defaultTick: SNAKE_TICKS.ONE.TYPE,

// isBot: true,
// botName: BOTS.HEAD_HUNTER.key,
// },
isBot: true,
botName: BOTS.HEAD_HUNTER.key,
},
2: {
headColor: 'blue',
bodyColor: 'orange',
Expand All @@ -51,25 +51,25 @@ const initialSnakesState = {
direction: DIRECTIONS.DOWN,
defaultTick: SNAKE_TICKS.QUARTER.TYPE,
},
// 3: {
// headColor: 'purple',
// bodyColor: 'gold',
// cells: [
// { x: 5, y: 6 }, // Head
// { x: 5, y: 5 },
// { x: 5, y: 4 },
// { x: 5, y: 3 },
// { x: 5, y: 2 },
// { x: 5, y: 1 },
// { x: 5, y: 0 }, // Tail
// ],
// direction: DIRECTIONS.DOWN,
// defaultTick: SNAKE_TICKS.HALF.TYPE,
3: {
headColor: 'purple',
bodyColor: 'gold',
cells: [
{ x: 5, y: 6 }, // Head
{ x: 5, y: 5 },
{ x: 5, y: 4 },
{ x: 5, y: 3 },
{ x: 5, y: 2 },
{ x: 5, y: 1 },
{ x: 5, y: 0 }, // Tail
],
direction: DIRECTIONS.DOWN,
defaultTick: SNAKE_TICKS.HALF.TYPE,

// isBot: true,
// botName: BOTS.HEAD_HUNTER.key,
// },
4: {
isBot: true,
botName: BOTS.HEAD_HUNTER.key,
},
player: {
headColor: 'red',
bodyColor: 'black',
cells: [
Expand Down
11 changes: 11 additions & 0 deletions packages/game-client/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,22 @@ const whichFoodToSpawn = () => {
return FOOD_TYPES.FROG;
};

const excludeSelf = ({ myId, snakes }) => {
// This heloers lets us remove ourselves from the game data so that it is convenient to perform operaations on the data...
return Object.entries(snakes).reduce((snakes, [snakeId, snake]) => {
if (snakeId !== myId) {
snakes[snakeId] = snake;
}
return snakes;
}, {});
};

export {
findDirectionUsingNeckAndHead,
getOppositeDirection,
generateValue,
generateKey,
isCellValid,
whichFoodToSpawn,
excludeSelf,
};

0 comments on commit 5cb0aa7

Please sign in to comment.