Skip to content

Commit

Permalink
🚀 smarter snake
Browse files Browse the repository at this point in the history
  • Loading branch information
Platane committed Jul 20, 2020
1 parent 73bfce9 commit fd9d7da
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 37 deletions.
5 changes: 4 additions & 1 deletion packages/action/generateContributionSnake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ export const generateContributionSnake = async (userName: string) => {
colorSnake: "purple",
};

const gameOptions = { maxSnakeLength: 5 };
const gameOptions = {
maxSnakeLength: 5,
colors: Array.from({ length: colorScheme.length - 1 }, (_, i) => i + 1),
};

const gifOptions = { delay: 10 };

Expand Down
177 changes: 150 additions & 27 deletions packages/compute/index.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,167 @@
import { Grid, Color, copyGrid, isInsideLarge } from "./grid";
import { Grid, Color, copyGrid, isInsideLarge, getColor } from "./grid";
import { Point, around4 } from "./point";
import { stepSnake, step } from "./step";
import { copySnake, snakeSelfCollide } from "./snake";
import { step } from "./step";
import { copySnake, snakeSelfCollide, Snake } from "./snake";

const isGridEmpty = (grid: Grid) => grid.data.every((x) => x === null);

export const computeBestRun = (
const createComputeHeuristic = (
grid0: Grid,
_snake0: Snake,
colors: Color[]
) => {
const colorCount: Record<Color, number> = {};
for (let x = grid0.width; x--; )
for (let y = grid0.height; y--; ) {
const c = getColor(grid0, x, y);
if (c !== null) colorCount[c] = 1 + (colorCount[c] || 0);
}

const values = colors
.map((k) => Array.from({ length: colorCount[k] }, () => k))
.flat();

return (_grid: Grid, _snake: Snake, stack: Color[]) => {
let score = 0;

for (let i = 0; i < stack.length; i++) {
const k = Math.abs(stack[i] - values[i]);
score += k === 0 ? 100 : -100 * k;
}

return score;
};
};

const computeKey = (grid: Grid, snake: Snake, stack: Color[]) =>
grid.data.map((x) => x || 0).join("") +
"|" +
snake.map((p) => p.x + "." + p.y).join(",") +
"|" +
stack.join("");

const createCell = (
key: string,
grid: Grid,
snake: Point[],
options: { maxSnakeLength: number }
snake: Snake,
stack: Color[],
direction: Point | null,
parent: any | null,
heuristic: number
) => ({
key,
parent,
direction,
grid,
snake,
stack,
weight: 1 + (parent?.weight || 0),
f: heuristic - 0 * (1 + (parent?.weight || 0)),
});

const unwrap = (c: ReturnType<typeof createCell> | null): Point[] =>
c && c.direction ? [...unwrap(c.parent), c.direction] : [];
// c && c.parent
// ? [
// ...unwrap(c.parent),
// { x: c.snake[1].x - c.snake[0].x, y: c.snake[1].y - c.snake[0].y },
// ]
// : [];

export const computeBestRun = (
grid0: Grid,
snake0: Snake,
options: { maxSnakeLength: number; colors: Color[] }
) => {
const g = copyGrid(grid);
const s = copySnake(snake);
const q: Color[] = [];
// const grid = copyGrid(grid0);
// const snake = copySnake(snake0);
// const stack: Color[] = [];

const commands: Point[] = [];
const computeHeuristic = createComputeHeuristic(
grid0,
snake0,
options.colors
);

let u = 500;
const closeList: any = {};
const openList = [
createCell(
computeKey(grid0, snake0, []),
grid0,
snake0,
[],
null,
null,
computeHeuristic(grid0, snake0, [])
),
];

while (!isGridEmpty(g) && u-- > 0) {
let direction;
let u = 7000;

for (let k = 10; k--; ) {
direction = around4[Math.floor(Math.random() * around4.length)];
let best = openList[0];

const sn = copySnake(s);
stepSnake(sn, direction, options);
while (openList.length && u-- > 0) {
openList.sort((a, b) => b.f - a.f);
const c = openList.shift()!;

if (isInsideLarge(g, 1, sn[0].x, sn[0].y) && !snakeSelfCollide(sn)) {
break;
} else {
direction = undefined;
}
}
closeList[c.key] = true;

if (isGridEmpty(c.grid)) return unwrap(c);

if (c.f > best.f) best = c;

for (const direction of around4) {
const snake = copySnake(c.snake);
const stack = c.stack.slice();
const grid = copyGrid(c.grid);

step(grid, snake, stack, direction, options);

const key = computeKey(grid, snake, stack);

if (direction !== undefined) {
step(g, s, q, direction, options);
commands.push(direction);
if (
!closeList[key] &&
isInsideLarge(grid, 1, snake[0].x, snake[0].y) &&
!snakeSelfCollide(snake)
) {
openList.push(
createCell(
key,
grid,
snake,
stack,
direction,
c,
computeHeuristic(grid, snake, stack)
)
);
}
}
}

return commands;
return unwrap(best);

// while (!isGridEmpty(g) && u-- > 0) {
// let direction;

// for (let k = 10; k--; ) {
// direction = around4[Math.floor(Math.random() * around4.length)];

// const sn = copySnake(s);
// stepSnake(sn, direction, options);

// if (isInsideLarge(g, 1, sn[0].x, sn[0].y) && !snakeSelfCollide(sn)) {
// break;
// } else {
// direction = undefined;
// }
// }

// if (direction !== undefined) {
// step(g, s, q, direction, options);
// commands.push(direction);
// }
// }

// return commands;
};
6 changes: 2 additions & 4 deletions packages/demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ const drawOptions = {
colorSnake: "purple",
};

const gameOptions = { maxSnakeLength: 5 };
const gameOptions = { colors: [1, 2, 3, 4], maxSnakeLength: 5 };

const grid0 = generateRandomGrid(42, 7, { colors: [1, 2, 3, 4], emptyP: 3 });
const grid0 = generateRandomGrid(42, 7, { ...gameOptions, emptyP: 3 });

const snake0 = [
{ x: 4, y: -1 },
Expand Down Expand Up @@ -71,8 +71,6 @@ document.body.appendChild(input);
const autoplayButton = document.createElement("button");
let cancel: any;
const loop = () => {
debugger;

input.value = (+input.value + 1) % +input.max;
update(+input.value);
cancelAnimationFrame(cancel);
Expand Down
2 changes: 1 addition & 1 deletion packages/draw/drawWorld.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const drawWorld = (
) => {
ctx.save();

ctx.translate(2 * o.sizeCell, 2 * o.sizeCell);
ctx.translate(1 * o.sizeCell, 2 * o.sizeCell);
drawGrid(ctx, grid, o);
drawSnake(ctx, snake, o);

Expand Down
4 changes: 2 additions & 2 deletions packages/gif-creator/__tests__/createGif.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ const drawOptions = {
colorSnake: "purple",
};

const gameOptions = { maxSnakeLength: 5 };
const gameOptions = { maxSnakeLength: 5, colors: [1, 2, 3, 4] };

const gifOptions = { delay: 200 };

it("should generate gif", async () => {
const grid = generateRandomGrid(14, 7, { colors: [1, 2, 3, 4], emptyP: 3 });
const grid = generateRandomGrid(14, 7, { ...gameOptions, emptyP: 3 });

const snake = [
{ x: 4, y: -1 },
Expand Down
4 changes: 2 additions & 2 deletions packages/gif-creator/__tests__/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ const drawOptions = {
colorSnake: "purple",
};

const gameOptions = { maxSnakeLength: 5 };
const gameOptions = { maxSnakeLength: 5, colors: [1, 2, 3, 4] };

const gifOptions = { delay: 20 };

const grid = generateRandomGrid(42, 7, { colors: [1, 2, 3, 4], emptyP: 3 });
const grid = generateRandomGrid(42, 7, { ...gameOptions, emptyP: 3 });

const snake = [
{ x: 4, y: -1 },
Expand Down

0 comments on commit fd9d7da

Please sign in to comment.