Skip to content

Commit

Permalink
feat: give bots access to the previous other players state + a few re…
Browse files Browse the repository at this point in the history
…lated fixes & improvements (#70)

## How does this PR impact the user?

https://www.loom.com/share/b8bfff4f10f047fabc83fcb9bb275536

## Description

- [x] give bots access to the previous other players state
- [x] fix bug where the bot that doesn't move can't eat anyone
- [x] optimize collision detection a bit
- [x] switch game from turn-by-turn logic (chess-like) to "all players
make moves simultaneously and then the engine computes collisions"

## Limitations

N/A

## Checklist

- [x] my PR is focused and contains one wholistic change
- [x] I have added screenshots or screen recordings to show the changes
  • Loading branch information
yurijmikhalevich authored Nov 13, 2024
1 parent 5358a84 commit 1c10f89
Show file tree
Hide file tree
Showing 8 changed files with 273 additions and 103 deletions.
33 changes: 33 additions & 0 deletions other/__snapshots__/prepareBotCode.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`correctly saves values for a different previous state 1`] = `
"
global._player = {"x":0,"y":0,"radius":5,"username":"yurij"};
global._otherPlayers = [{"x":10,"y":10,"radius":5,"username":"danil"},{"x":20,"y":20,"radius":5,"username":"andrey"}];
global._food = [{"x":30,"y":30,"radius":5},{"x":40,"y":40,"radius":5}];
global._previousOtherPlayersState = [{"x":11,"y":12,"radius":4,"username":"danil"},{"x":22,"y":40,"radius":5,"username":"andrey"}];
global._worldWidth = 100;
global._worldHeight = 100;
console.log('hello world');
console.log('hello bot api');
"
`;

exports[`handles the case when the bot was not in the previous bot state 1`] = `
"
global._player = {"x":0,"y":0,"radius":5,"username":"yurij"};
global._otherPlayers = [{"x":10,"y":10,"radius":5,"username":"danil"},{"x":20,"y":20,"radius":5,"username":"andrey"}];
global._food = [{"x":30,"y":30,"radius":5},{"x":40,"y":40,"radius":5}];
global._previousOtherPlayersState = [];
global._worldWidth = 100;
global._worldHeight = 100;
console.log('hello world');
console.log('hello bot api');
"
`;

exports[`prepares bot code correctly when there are three bots and some food in the world 1`] = `
"
global._player = {"x":0,"y":0,"radius":5,"username":"yurij"};
global._otherPlayers = [{"x":10,"y":10,"radius":5,"username":"danil"},{"x":20,"y":20,"radius":5,"username":"andrey"}];
global._food = [{"x":30,"y":30,"radius":5},{"x":40,"y":40,"radius":5}];
global._previousOtherPlayersState = [{"x":10,"y":10,"radius":5,"username":"danil"},{"x":20,"y":20,"radius":5,"username":"andrey"}];
global._worldWidth = 100;
global._worldHeight = 100;
Expand All @@ -19,6 +50,7 @@ exports[`prepares bot code correctly when there are two bots in the world 1`] =
global._player = {"x":0,"y":0,"radius":5,"username":"yurij"};
global._otherPlayers = [{"x":10,"y":10,"radius":5,"username":"danil"}];
global._food = [];
global._previousOtherPlayersState = [{"x":10,"y":10,"radius":5,"username":"danil"}];
global._worldWidth = 100;
global._worldHeight = 100;
Expand All @@ -33,6 +65,7 @@ exports[`prepares bot code correctly when there is one bot in the world 1`] = `
global._player = {"x":0,"y":0,"radius":5,"username":"yurij"};
global._otherPlayers = [];
global._food = [];
global._previousOtherPlayersState = [];
global._worldWidth = 100;
global._worldHeight = 100;
Expand Down
2 changes: 2 additions & 0 deletions other/defaultCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export const jsdoc = `/**
* Information about your player.
* @property {Player[]} otherPlayers
* Information about other players.
* @property {Player[]} previousOtherPlayersState
* Information about the other players in the previous frame.
* @property {Food[]} food
* Information about the not yet eaten food.
* @property {MoveTowards} moveTowards
Expand Down
96 changes: 93 additions & 3 deletions other/prepareBotCode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ it("prepares bot code correctly when there is one bot in the world", () => {

const botApi = "console.log('hello bot api');";

const preparedCode = prepareBotCode({ bot, botCodes, state, botApi });
const preparedCode = prepareBotCode({ bot, botInfo: botCodes, state, prevBotState: state.bots, botApi });

expect(preparedCode).toMatchSnapshot();
});
Expand Down Expand Up @@ -55,7 +55,7 @@ it("prepares bot code correctly when there are two bots in the world", () => {

const botApi = "console.log('hello bot api');";

const preparedCode = prepareBotCode({ bot, botCodes, state, botApi });
const preparedCode = prepareBotCode({ bot, botInfo: botCodes, state, prevBotState: state.bots, botApi });

expect(preparedCode).toMatchSnapshot();
});
Expand Down Expand Up @@ -97,7 +97,97 @@ it("prepares bot code correctly when there are three bots and some food in the w

const botApi = "console.log('hello bot api');";

const preparedCode = prepareBotCode({ bot, botCodes, state, botApi });
const preparedCode = prepareBotCode({ bot, botInfo: botCodes, state, prevBotState: state.bots, botApi });

expect(preparedCode).toMatchSnapshot();
});

it("handles the case when the bot was not in the previous bot state", () => {
const bot = {
id: "1",
code: "console.log('hello world');",
username: "yurij",
userId: 1,
};

const botCodes = {
1: bot,
2: {
id: "2",
code: "console.log('hello world');",
username: "danil",
userId: 2,
},
3: {
id: "3",
code: "console.log('hello world');",
username: "andrey",
userId: 3,
},
};

const state = {
bots: new Map([
["1", { x: 0, y: 0, radius: 5, botId: "1", color: "#00FF00", spawnId: "s1" }],
["2", { x: 10, y: 10, radius: 5, botId: "2", color: "#00FF00", spawnId: "s2" }],
["3", { x: 20, y: 20, radius: 5, botId: "3", color: "#00FF00", spawnId: "s3" }],
]),
food: [{ x: 30, y: 30, radius: 5 }, { x: 40, y: 40, radius: 5 }],
width: 100,
height: 100,
};

const botApi = "console.log('hello bot api');";

const preparedCode = prepareBotCode({ bot, botInfo: botCodes, state, prevBotState: new Map(), botApi });

expect(preparedCode).toMatchSnapshot();
});

it("correctly saves values for a different previous state", () => {
const bot = {
id: "1",
code: "console.log('hello world');",
username: "yurij",
userId: 1,
};

const botCodes = {
1: bot,
2: {
id: "2",
code: "console.log('hello world');",
username: "danil",
userId: 2,
},
3: {
id: "3",
code: "console.log('hello world');",
username: "andrey",
userId: 3,
},
};

const state = {
bots: new Map([
["1", { x: 0, y: 0, radius: 5, botId: "1", color: "#00FF00", spawnId: "s1" }],
["2", { x: 10, y: 10, radius: 5, botId: "2", color: "#00FF00", spawnId: "s2" }],
["3", { x: 20, y: 20, radius: 5, botId: "3", color: "#00FF00", spawnId: "s3" }],
]),
food: [{ x: 30, y: 30, radius: 5 }, { x: 40, y: 40, radius: 5 }],
width: 100,
height: 100,
};

const prevBotState = new Map([
["1", { x: 2, y: 4, radius: 3, botId: "1", color: "#00FF00", spawnId: "s1" }],
["2", { x: 11, y: 12, radius: 4, botId: "2", color: "#00FF00", spawnId: "s2" }],
["3", { x: 22, y: 40, radius: 5, botId: "3", color: "#00FF00", spawnId: "s3" }],
]);

const botApi = "console.log('hello bot api');";

const preparedCode = prepareBotCode({ bot, botInfo: botCodes, state, prevBotState, botApi });

expect(preparedCode).toMatchSnapshot();
});
18 changes: 11 additions & 7 deletions other/prepareBotCode.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import type { BotCode, BotCodes } from "~/other/botCodeStore";
import type { BotCode } from "~/other/botCodeStore";
import type { WorldState } from "~/other/world";

type PrepareBotCodeArgs = {
bot: BotCode;
botCodes: BotCodes;
botInfo: Record<string, { username: string }>;
state: Pick<WorldState, "bots" | "food" | "width" | "height">;
prevBotState: WorldState["bots"];
botApi: string;
};

export default function prepareBotCode({ bot, botCodes, state, botApi }: PrepareBotCodeArgs): string | undefined {
export default function prepareBotCode({ bot, botInfo, state, prevBotState, botApi }: PrepareBotCodeArgs): string {
const { code, username } = bot;

const botObject = [...state.bots.values()].find(b => bot.id === b.botId);
if (!botObject) {
// TODO(yurij): handle this better
console.error(`Bot with id ${bot.id} not found in the world`);
return;
throw new Error(`Bot with id ${bot.id} not found in the world`);
}

const me = { x: botObject.x, y: botObject.y, radius: botObject.radius, username };
Expand All @@ -25,14 +24,19 @@ export default function prepareBotCode({ bot, botCodes, state, botApi }: Prepare
x: b.x,
y: b.y,
radius: b.radius,
username: botCodes[b.botId]?.username,
username: botInfo[b.botId]?.username,
}));
const food = state.food.map(f => ({ x: f.x, y: f.y, radius: f.radius }));

const prevOtherPlayers = [...prevBotState.values()]
.filter(b => b.botId !== bot.id)
.map(b => ({ x: b.x, y: b.y, radius: b.radius, username: botInfo[b.botId]?.username }));

const preparedCode = `
global._player = ${JSON.stringify(me)};
global._otherPlayers = ${JSON.stringify(otherPlayers)};
global._food = ${JSON.stringify(food)};
global._previousOtherPlayersState = ${JSON.stringify(prevOtherPlayers)};
global._worldWidth = ${state.width};
global._worldHeight = ${state.height};
Expand Down
8 changes: 5 additions & 3 deletions other/world.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ it("correctly removes the smaller bot when the bigger bot eats it", () => {
bot2.x = 10;
bot2.y = 10;

world.checkCollisions("1");
world.checkCollisions();

expect(world.hasBot("1")).toBe(true);
expect(world.hasBot("2")).toBe(false);
Expand All @@ -41,7 +41,7 @@ it("correctly removes food when the bot eats it", () => {
food.x = 10;
food.y = 10;

world.checkCollisions("1");
world.checkCollisions();

// @ts-expect-error - world.food is private
expect(world.food.length).toBeLessThan(100);
Expand Down Expand Up @@ -69,7 +69,7 @@ it("shouldn't crash when bot eats two food items and one of them with the last i
food2.x = 10;
food2.y = 10;

world.checkCollisions("1");
world.checkCollisions();

// @ts-expect-error - world.food is private
expect(world.food.length).toBeLessThan(100);
Expand All @@ -94,6 +94,7 @@ it("doesn't allow the bot to go outside the world with large x and y", () => {
expect(botFromStateBeforeMove.y).toBe(100);

world.moveBot("1", 101, 101);
world.checkCollisions();

const worldStateAfterMove = world.getState();
const botsAfterMove = [...worldStateAfterMove.bots.values()];
Expand Down Expand Up @@ -124,6 +125,7 @@ it("doesn't allow the bot to go outside the world with negative x and y", () =>
expect(botFromStateBeforeMove.y).toBe(0);

world.moveBot("1", -1, -1);
world.checkCollisions();

const worldStateAfterMove = world.getState();
const botsAfterMove = [...worldStateAfterMove.bots.values()];
Expand Down
Loading

0 comments on commit 1c10f89

Please sign in to comment.