🚀 refactor algorithm

This commit is contained in:
platane
2020-10-29 23:27:08 +01:00
parent 1c6814c2fa
commit d81ecec836
27 changed files with 274 additions and 777 deletions

View File

@@ -34,6 +34,7 @@ export const createCanvas = ({
canvas.style.width = w + "px";
canvas.style.height = h + "px";
canvas.style.display = "block";
canvas.style.pointerEvents = "none";
document.body.appendChild(canvas);

View File

@@ -1,87 +0,0 @@
import "./menu";
import { createCanvas } from "./canvas";
import { getHeadX, getHeadY, snakeToCells } from "@snk/types/snake";
import { grid, snake } from "./sample";
import { getColor } from "@snk/types/grid";
import { getAvailableRoutes } from "@snk/compute/getAvailableRoutes";
import type { Snake } from "@snk/types/snake";
import type { Color, Empty } from "@snk/types/grid";
//
// compute
const solutions: {
x: number;
y: number;
chain: Snake[];
color: Color | Empty;
}[] = [];
getAvailableRoutes(grid, snake, (chain) => {
const x = getHeadX(chain[0]);
const y = getHeadY(chain[0]);
if (!solutions.some((s) => x === s.x && y === s.y))
solutions.push({
x,
y,
chain: [snake, ...chain.slice().reverse()],
color: getColor(grid, x, y),
});
return false;
});
solutions.sort((a, b) => a.color - b.color);
const { canvas, ctx, draw, highlightCell } = createCanvas(grid);
document.body.appendChild(canvas);
let k = 0;
let i = solutions[k].chain.length - 1;
const onChange = () => {
const { chain } = solutions[k];
ctx.clearRect(0, 0, 9999, 9999);
draw(grid, chain[i], []);
chain
.map(snakeToCells)
.flat()
.forEach(({ x, y }) => highlightCell(x, y));
};
onChange();
const inputK = document.createElement("input") as any;
inputK.type = "range";
inputK.value = 0;
inputK.step = 1;
inputK.min = 0;
inputK.max = solutions.length - 1;
inputK.style.width = "90%";
inputK.style.padding = "20px 0";
inputK.addEventListener("input", () => {
k = +inputK.value;
i = inputI.value = inputI.max = solutions[k].chain.length - 1;
onChange();
});
document.body.append(inputK);
const inputI = document.createElement("input") as any;
inputI.type = "range";
inputI.value = inputI.max = solutions[k].chain.length - 1;
inputI.step = 1;
inputI.min = 0;
inputI.style.width = "90%";
inputI.style.padding = "20px 0";
inputI.addEventListener("input", () => {
i = +inputI.value;
onChange();
});
document.body.append(inputI);
window.addEventListener("click", (e) => {
if (e.target === document.body || e.target === document.body.parentElement)
inputK.focus();
});

View File

@@ -4,55 +4,35 @@ import { getBestRoute } from "@snk/compute/getBestRoute";
import { Color, copyGrid } from "@snk/types/grid";
import { grid, snake } from "./sample";
import { step } from "@snk/compute/step";
import { isStableAndBound, stepSpring } from "./springUtils";
const chain = [snake, ...getBestRoute(grid, snake)!];
const chain = getBestRoute(grid, snake)!;
//
// draw
let k = 0;
const spring = { x: 0, v: 0, target: 0 };
const springParams = { tension: 120, friction: 20, maxVelocity: 50 };
let animationFrame: number;
const { canvas, drawLerp } = createCanvas(grid);
const { canvas, draw } = createCanvas(grid);
document.body.appendChild(canvas);
const clamp = (x: number, a: number, b: number) => Math.max(a, Math.min(b, x));
const onChange = () => {
const gridN = copyGrid(grid);
const stack: Color[] = [];
for (let i = 0; i <= k; i++) step(gridN, stack, chain[i]);
const loop = () => {
cancelAnimationFrame(animationFrame);
stepSpring(spring, springParams, spring.target);
const stable = isStableAndBound(spring, spring.target);
const grid0 = copyGrid(grid);
const stack0: Color[] = [];
for (let i = 0; i < Math.min(chain.length, spring.x); i++)
step(grid0, stack0, chain[i]);
const snake0 = chain[clamp(Math.floor(spring.x), 0, chain.length - 1)];
const snake1 = chain[clamp(Math.ceil(spring.x), 0, chain.length - 1)];
const k = spring.x % 1;
drawLerp(grid0, snake0, snake1, stack0, k);
if (!stable) animationFrame = requestAnimationFrame(loop);
draw(gridN, chain[k], stack);
};
loop();
onChange();
const input = document.createElement("input") as any;
input.type = "range";
input.value = 0;
input.step = 1;
input.min = 0;
input.max = chain.length;
input.max = chain.length - 1;
input.style.width = "90%";
input.addEventListener("input", () => {
spring.target = +input.value;
cancelAnimationFrame(animationFrame);
animationFrame = requestAnimationFrame(loop);
k = +input.value;
onChange();
});
document.body.append(input);
window.addEventListener("click", (e) => {

View File

@@ -122,6 +122,7 @@ const createViewer = ({
const h = (height / width) * w;
canvas.style.width = w + "px";
canvas.style.height = h + "px";
canvas.style.pointerEvents = "none";
document.body.appendChild(canvas);
@@ -205,7 +206,7 @@ const onSubmit = async (userName: string) => {
const snake = snake3;
const grid = userContributionToGrid(cells);
const chain = [snake, ...getBestRoute(grid, snake)!];
const chain = getBestRoute(grid, snake)!;
dispose();
createViewer({ grid0: grid, chain, drawOptions });

View File

@@ -1,8 +1 @@
[
"getAvailableRoutes",
"pruneLayer",
"getBestRoute",
"getBestRoundTrip",
"getPathTo",
"interactive"
]
["interactive", "getBestTunnel", "getBestRoute", "getPathTo"]

View File

@@ -1,51 +0,0 @@
import "./menu";
import { createCanvas } from "./canvas";
import { Color, copyGrid } from "@snk/types/grid";
import { grid, snake } from "./sample";
import { pruneLayer } from "@snk/compute/pruneLayer";
import { getSnakeLength } from "@snk/types/snake";
const colors = [1, 2, 3] as Color[];
const snakeN = getSnakeLength(snake);
const layers = [{ grid, chunk: [] as { x: number; y: number }[] }];
let grid0 = copyGrid(grid);
for (const color of colors) {
const chunk = pruneLayer(grid0, color, snakeN);
layers.push({ chunk, grid: copyGrid(grid0) });
}
const { canvas, ctx, highlightCell, draw } = createCanvas(grid);
document.body.appendChild(canvas);
let k = 0;
const loop = () => {
const { grid, chunk } = layers[k];
draw(grid, snake, []);
ctx.fillStyle = "orange";
chunk.forEach(({ x, y }) => highlightCell(x, y));
};
loop();
const input = document.createElement("input") as any;
input.type = "range";
input.value = 0;
input.step = 1;
input.min = 0;
input.max = layers.length - 1;
input.style.width = "90%";
input.addEventListener("input", () => {
k = +input.value;
loop();
});
document.body.append(input);
window.addEventListener("click", (e) => {
if (e.target === document.body || e.target === document.body.parentElement)
input.focus();
});

View File

@@ -40,6 +40,11 @@ const config: Configuration = {
chunks: [demo],
})
),
new HtmlWebpackPlugin({
title: "snk - " + demos[0],
filename: `index.html`,
chunks: [demos[0]],
}),
],
devtool: false,