🚀 refactor getBestRoute
This commit is contained in:
48
packages/demo/canvas.ts
Normal file
48
packages/demo/canvas.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Color, Grid } from "@snk/compute/grid";
|
||||
import { drawWorld } from "@snk/draw/drawWorld";
|
||||
import { Snake } from "@snk/compute/snake";
|
||||
|
||||
export const drawOptions = {
|
||||
sizeBorderRadius: 2,
|
||||
sizeCell: 16,
|
||||
sizeDot: 12,
|
||||
colorBorder: "#1b1f230a",
|
||||
colorDots: {
|
||||
1: "#9be9a8",
|
||||
2: "#40c463",
|
||||
3: "#30a14e",
|
||||
4: "#216e39",
|
||||
5: "orange",
|
||||
},
|
||||
colorEmpty: "#ebedf0",
|
||||
colorSnake: "purple",
|
||||
};
|
||||
|
||||
export const createCanvas = ({
|
||||
width,
|
||||
height,
|
||||
}: {
|
||||
width: number;
|
||||
height: number;
|
||||
}) => {
|
||||
const canvas = document.createElement("canvas");
|
||||
const upscale = 2;
|
||||
const w = drawOptions.sizeCell * (width + 4);
|
||||
const h = drawOptions.sizeCell * (height + 4) + 200;
|
||||
canvas.width = w * upscale;
|
||||
canvas.height = h * upscale;
|
||||
canvas.style.width = w + "px";
|
||||
canvas.style.height = h + "px";
|
||||
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.scale(upscale, upscale);
|
||||
|
||||
const draw = (grid: Grid, snake: Snake, stack: Color[]) => {
|
||||
ctx.clearRect(0, 0, 9999, 9999);
|
||||
drawWorld(ctx, grid, snake, stack, drawOptions);
|
||||
};
|
||||
|
||||
return { draw, canvas };
|
||||
};
|
||||
105
packages/demo/demo.getAvailableRoutes.ts
Normal file
105
packages/demo/demo.getAvailableRoutes.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { copyGrid } from "@snk/compute/grid";
|
||||
import { copySnake } from "@snk/compute/snake";
|
||||
import { createCanvas } from "./canvas";
|
||||
import { stepSnake } from "@snk/compute/step";
|
||||
import { samples } from "./samples";
|
||||
import { getAvailableRoutes } from "@snk/compute/getAvailableRoutes";
|
||||
|
||||
//
|
||||
// init
|
||||
|
||||
const label = new URLSearchParams(window.location.search).get("sample");
|
||||
const { grid: grid0, snake: snake0, gameOptions } =
|
||||
samples.find((s) => s.label === label) || samples[0];
|
||||
|
||||
//
|
||||
// compute
|
||||
|
||||
const routes = getAvailableRoutes(grid0, snake0, gameOptions, 20);
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
const { canvas, draw } = createCanvas(grid0);
|
||||
|
||||
const update = (n: number, k: number) => {
|
||||
const snake = copySnake(snake0);
|
||||
const grid = copyGrid(grid0);
|
||||
const route = routes[n];
|
||||
|
||||
const trace = [{ ...snake[0] }];
|
||||
|
||||
for (let i = 0; i < k; i++) {
|
||||
stepSnake(snake, route.directions[i], gameOptions);
|
||||
trace.push({ ...snake[0] });
|
||||
}
|
||||
|
||||
draw(grid, snake, []);
|
||||
|
||||
const [cell] = route.snakeN;
|
||||
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
ctx.fillStyle = "orange";
|
||||
ctx.fillRect(
|
||||
16 * (routes[i].snakeN[0].x + 1) + 6,
|
||||
16 * (routes[i].snakeN[0].y + 2) + 6,
|
||||
4,
|
||||
4
|
||||
);
|
||||
}
|
||||
|
||||
ctx.fillStyle = "orange";
|
||||
ctx.fillRect(16 * (cell.x + 1) + 4, 16 * (cell.y + 2) + 4, 8, 8);
|
||||
|
||||
for (let i = 0; i < trace.length; i++) {
|
||||
ctx.fillStyle = "purple";
|
||||
ctx.fillRect(16 * (trace[i].x + 1) + 6, 16 * (trace[i].y + 2) + 6, 4, 4);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// controls
|
||||
|
||||
// const input0: any = document.createElement("input");
|
||||
// input0.type = "range";
|
||||
// input0.style.width = "100%";
|
||||
// input0.min = 0;
|
||||
// input0.max = snakeSteps.length - 1;
|
||||
// input0.step = 1;
|
||||
// input0.value = 0;
|
||||
// input0.addEventListener("input", () => {
|
||||
// const grid = copyGrid(grid0);
|
||||
// draw(grid, snakeSteps[+input0.value], []);
|
||||
// });
|
||||
// document.body.appendChild(input0);
|
||||
|
||||
const inputA: any = document.createElement("input");
|
||||
inputA.type = "range";
|
||||
inputA.style.width = "100%";
|
||||
inputA.min = 0;
|
||||
inputA.max = routes.length - 1;
|
||||
inputA.step = 1;
|
||||
inputA.value = 0;
|
||||
inputA.addEventListener("input", () => {
|
||||
inputB.value = inputB.max = routes[+inputA.value].directions.length;
|
||||
update(+inputA.value, +inputB.value);
|
||||
});
|
||||
document.body.appendChild(inputA);
|
||||
|
||||
const inputB: any = document.createElement("input");
|
||||
inputB.type = "range";
|
||||
inputB.style.width = "100%";
|
||||
inputB.min = 0;
|
||||
inputB.step = 1;
|
||||
inputB.value = 0;
|
||||
inputB.addEventListener("input", () => {
|
||||
update(+inputA.value, +inputB.value);
|
||||
});
|
||||
document.body.appendChild(inputB);
|
||||
|
||||
if (routes[+inputA.value]) {
|
||||
inputB.value = inputB.max = routes[+inputA.value].directions.length;
|
||||
update(+inputA.value, +inputB.value);
|
||||
}
|
||||
48
packages/demo/demo.getBestRoute.ts
Normal file
48
packages/demo/demo.getBestRoute.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { copyGrid } from "@snk/compute/grid";
|
||||
import { copySnake } from "@snk/compute/snake";
|
||||
import { createCanvas } from "./canvas";
|
||||
import { step } from "@snk/compute/step";
|
||||
import { getBestRoute } from "@snk/compute/getBestRoute";
|
||||
import { samples } from "./samples";
|
||||
|
||||
//
|
||||
// init
|
||||
|
||||
const label = new URLSearchParams(window.location.search).get("sample");
|
||||
const { grid: grid0, snake: snake0, gameOptions } =
|
||||
samples.find((s) => s.label === label) || samples[0];
|
||||
|
||||
//
|
||||
// compute
|
||||
|
||||
const s0 = Date.now();
|
||||
const bestRoute = getBestRoute(grid0, snake0, gameOptions);
|
||||
console.log(`computed in ${Date.now() - s0}ms`);
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
const { draw } = createCanvas(grid0);
|
||||
|
||||
//
|
||||
// controls
|
||||
|
||||
const inputK: any = document.createElement("input");
|
||||
inputK.type = "range";
|
||||
inputK.style.width = "100%";
|
||||
inputK.min = 0;
|
||||
inputK.max = bestRoute.length;
|
||||
inputK.step = 1;
|
||||
inputK.value = 0;
|
||||
inputK.addEventListener("input", () => {
|
||||
const snake = copySnake(snake0);
|
||||
const grid = copyGrid(grid0);
|
||||
const stack: any[] = [];
|
||||
|
||||
for (let i = 0; i < +inputK.value; i++)
|
||||
step(grid, snake, stack, bestRoute[i], gameOptions);
|
||||
|
||||
draw(grid, snake, stack);
|
||||
});
|
||||
document.body.appendChild(inputK);
|
||||
draw(grid0, snake0, []);
|
||||
48
packages/demo/demo.index.ts
Normal file
48
packages/demo/demo.index.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { copyGrid } from "@snk/compute/grid";
|
||||
import { copySnake } from "@snk/compute/snake";
|
||||
import { createCanvas } from "./canvas";
|
||||
import { step } from "@snk/compute/step";
|
||||
import { getBestRoute } from "@snk/compute/getBestRoute";
|
||||
import { samples } from "./samples";
|
||||
|
||||
//
|
||||
// init
|
||||
|
||||
const label = "realistic";
|
||||
const { grid: grid0, snake: snake0, gameOptions } = samples.find(
|
||||
(s) => s.label === label
|
||||
);
|
||||
|
||||
//
|
||||
// compute
|
||||
|
||||
const s0 = performance.now();
|
||||
const bestRoute = getBestRoute(grid0, snake0, gameOptions, 120);
|
||||
console.log(performance.now() - s0);
|
||||
//
|
||||
// draw
|
||||
|
||||
const { draw } = createCanvas(grid0);
|
||||
|
||||
//
|
||||
// controls
|
||||
|
||||
const inputK: any = document.createElement("input");
|
||||
inputK.type = "range";
|
||||
inputK.style.width = "100%";
|
||||
inputK.min = 0;
|
||||
inputK.max = bestRoute.length;
|
||||
inputK.step = 1;
|
||||
inputK.value = 0;
|
||||
inputK.addEventListener("input", () => {
|
||||
const snake = copySnake(snake0);
|
||||
const grid = copyGrid(grid0);
|
||||
const stack: any[] = [];
|
||||
|
||||
for (let i = 0; i < +inputK.value; i++)
|
||||
step(grid, snake, stack, bestRoute[i], gameOptions);
|
||||
|
||||
draw(grid, snake, stack);
|
||||
});
|
||||
document.body.appendChild(inputK);
|
||||
draw(grid0, snake0, []);
|
||||
@@ -1,97 +0,0 @@
|
||||
import { generateRandomGrid } from "@snk/compute/generateGrid";
|
||||
import { Color, copyGrid } from "@snk/compute/grid";
|
||||
import { computeBestRun } from "@snk/compute";
|
||||
import { step } from "@snk/compute/step";
|
||||
import { drawWorld } from "@snk/draw/drawWorld";
|
||||
import { copySnake } from "@snk/compute/snake";
|
||||
|
||||
const drawOptions = {
|
||||
sizeBorderRadius: 2,
|
||||
sizeCell: 16,
|
||||
sizeDot: 12,
|
||||
colorBorder: "#1b1f230a",
|
||||
colorDots: { 1: "#9be9a8", 2: "#40c463", 3: "#30a14e", 4: "#216e39" },
|
||||
colorEmpty: "#ebedf0",
|
||||
colorSnake: "purple",
|
||||
};
|
||||
|
||||
const gameOptions = { colors: [1, 2, 3], maxSnakeLength: 5 };
|
||||
|
||||
const grid0 = generateRandomGrid(42, 7, { ...gameOptions, emptyP: 2 });
|
||||
|
||||
const snake0 = [
|
||||
{ x: 4, y: -1 },
|
||||
{ x: 3, y: -1 },
|
||||
{ x: 2, y: -1 },
|
||||
{ x: 1, y: -1 },
|
||||
{ x: 0, y: -1 },
|
||||
];
|
||||
const stack0: Color[] = [];
|
||||
|
||||
const chain = computeBestRun(grid0, snake0, gameOptions);
|
||||
|
||||
//
|
||||
// draw
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const upscale = 2;
|
||||
const width = drawOptions.sizeCell * (grid0.width + 4);
|
||||
const height = drawOptions.sizeCell * (grid0.height + 4) + 100;
|
||||
canvas.width = width * upscale;
|
||||
canvas.height = height * upscale;
|
||||
canvas.style.width = width + "px";
|
||||
canvas.style.height = height + "px";
|
||||
document.body.appendChild(canvas);
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
ctx.scale(upscale, upscale);
|
||||
|
||||
const update = (n: number) => {
|
||||
const snake = copySnake(snake0);
|
||||
const stack = stack0.slice();
|
||||
const grid = copyGrid(grid0);
|
||||
|
||||
for (let i = 0; i < n; i++) step(grid, snake, stack, chain[i], gameOptions);
|
||||
|
||||
ctx.clearRect(0, 0, 9999, 9999);
|
||||
drawWorld(ctx, grid, snake, stack, drawOptions);
|
||||
};
|
||||
|
||||
//
|
||||
// controls
|
||||
|
||||
const input: any = document.createElement("input");
|
||||
input.type = "range";
|
||||
input.style.width = "100%";
|
||||
input.min = 0;
|
||||
input.max = chain.length;
|
||||
input.step = 1;
|
||||
input.value = 0;
|
||||
input.addEventListener("input", () => {
|
||||
setAutoPlay(false);
|
||||
update(+input.value);
|
||||
});
|
||||
document.addEventListener("click", () => input.focus());
|
||||
|
||||
document.body.appendChild(input);
|
||||
|
||||
const autoplayButton = document.createElement("button");
|
||||
let cancel: any;
|
||||
const loop = () => {
|
||||
input.value = (+input.value + 1) % +input.max;
|
||||
update(+input.value);
|
||||
cancelAnimationFrame(cancel);
|
||||
cancel = requestAnimationFrame(loop);
|
||||
};
|
||||
const setAutoPlay = (a: boolean) => {
|
||||
autoplayButton.innerHTML = a ? "pause" : "play";
|
||||
if (a) loop();
|
||||
else cancelAnimationFrame(cancel);
|
||||
};
|
||||
autoplayButton.addEventListener("click", () => {
|
||||
debugger;
|
||||
setAutoPlay(autoplayButton.innerHTML === "play");
|
||||
});
|
||||
document.body.appendChild(autoplayButton);
|
||||
|
||||
setAutoPlay(true);
|
||||
update(+input.value);
|
||||
@@ -6,11 +6,11 @@
|
||||
"@snk/draw": "1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"html-webpack-plugin": "4.3.0",
|
||||
"ts-loader": "8.0.1",
|
||||
"webpack": "4.43.0",
|
||||
"webpack-cli": "3.3.12",
|
||||
"webpack-dev-server": "3.11.0",
|
||||
"ts-loader": "8.0.1",
|
||||
"html-webpack-plugin": "4.3.0"
|
||||
"webpack-dev-server": "3.11.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "tsc webpack.config.ts",
|
||||
|
||||
85
packages/demo/samples.ts
Normal file
85
packages/demo/samples.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
// @ts-ignore
|
||||
import * as ParkMiller from "park-miller";
|
||||
import { generateRandomGrid } from "@snk/compute/generateGrid";
|
||||
import { createEmptyGrid, setColor } from "@snk/compute/grid";
|
||||
|
||||
export const samples: any[] = [];
|
||||
|
||||
{
|
||||
const gameOptions = {
|
||||
colors: [1, 2, 3],
|
||||
maxSnakeLength: 1,
|
||||
};
|
||||
const snake = [{ x: 0, y: -1 }];
|
||||
const grid = createEmptyGrid(6, 6);
|
||||
samples.push({
|
||||
label: "empty",
|
||||
grid,
|
||||
snake,
|
||||
gameOptions,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const gameOptions = {
|
||||
colors: [1, 2, 3],
|
||||
maxSnakeLength: 1,
|
||||
};
|
||||
const snake = [{ x: 0, y: -1 }];
|
||||
const grid = createEmptyGrid(6, 6);
|
||||
setColor(grid, 2, 2, 2);
|
||||
samples.push({
|
||||
label: "small",
|
||||
grid,
|
||||
snake,
|
||||
gameOptions,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const gameOptions = {
|
||||
colors: [1, 2, 3],
|
||||
maxSnakeLength: 5,
|
||||
};
|
||||
const random = new ParkMiller(10);
|
||||
const rand = (a: number, b: number) => random.integerInRange(a, b - 1);
|
||||
const grid = generateRandomGrid(52, 7, { ...gameOptions, emptyP: 2 }, rand);
|
||||
const snake = [
|
||||
{ x: 4, y: -1 },
|
||||
{ x: 3, y: -1 },
|
||||
{ x: 2, y: -1 },
|
||||
{ x: 1, y: -1 },
|
||||
{ x: 0, y: -1 },
|
||||
];
|
||||
|
||||
samples.push({
|
||||
label: "realistic",
|
||||
grid,
|
||||
snake,
|
||||
gameOptions,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const gameOptions = {
|
||||
colors: [1, 2, 3],
|
||||
maxSnakeLength: 5,
|
||||
};
|
||||
const random = new ParkMiller(10);
|
||||
const rand = (a: number, b: number) => random.integerInRange(a, b - 1);
|
||||
const grid = generateRandomGrid(20, 7, { ...gameOptions, emptyP: 2 }, rand);
|
||||
const snake = [
|
||||
{ x: 4, y: -1 },
|
||||
{ x: 3, y: -1 },
|
||||
{ x: 2, y: -1 },
|
||||
{ x: 1, y: -1 },
|
||||
{ x: 0, y: -1 },
|
||||
];
|
||||
|
||||
samples.push({
|
||||
label: "realistic-small",
|
||||
grid,
|
||||
snake,
|
||||
gameOptions,
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as path from "path";
|
||||
|
||||
// @ts-ignore
|
||||
import * as HtmlWebpackPlugin from "html-webpack-plugin";
|
||||
|
||||
import type { Configuration } from "webpack";
|
||||
|
||||
const basePathname = (process.env.BASE_PATHNAME || "")
|
||||
@@ -10,7 +10,11 @@ const basePathname = (process.env.BASE_PATHNAME || "")
|
||||
|
||||
const config: Configuration = {
|
||||
mode: "development",
|
||||
entry: "./index",
|
||||
entry: {
|
||||
"demo.getAvailableRoutes": "./demo.getAvailableRoutes",
|
||||
"demo.getBestRoute": "./demo.getBestRoute",
|
||||
"demo.index": "./demo.index",
|
||||
},
|
||||
resolve: { extensions: [".ts", ".js"] },
|
||||
output: {
|
||||
path: path.join(__dirname, "dist"),
|
||||
@@ -28,11 +32,16 @@ const config: Configuration = {
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
title: "demo",
|
||||
filename: "index.html",
|
||||
meta: {
|
||||
viewport: "width=device-width, initial-scale=1, shrink-to-fit=no",
|
||||
},
|
||||
chunks: ["demo.index"],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "demo-getAvailableRoutes.html",
|
||||
chunks: ["demo.getAvailableRoutes"],
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "demo-getBestRoute.html",
|
||||
chunks: ["demo.getBestRoute"],
|
||||
}),
|
||||
],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user