From 1d24bc8a0f19ff3ea7c24d1924b38483e6f20f72 Mon Sep 17 00:00:00 2001 From: platane Date: Fri, 23 Oct 2020 21:42:35 +0200 Subject: [PATCH] :rocket: improve algorithm for enclaved cell --- packages/compute/getAvailableRoutes.ts | 2 +- packages/compute/pruneLayer.ts | 16 ++-- packages/demo/demo.getAvailableRoutes.ts | 97 +++++++++++++++--------- packages/types/__fixtures__/grid.ts | 32 +++++++- packages/types/__fixtures__/snake.ts | 1 + 5 files changed, 103 insertions(+), 45 deletions(-) diff --git a/packages/compute/getAvailableRoutes.ts b/packages/compute/getAvailableRoutes.ts index 001e00c..00f9067 100644 --- a/packages/compute/getAvailableRoutes.ts +++ b/packages/compute/getAvailableRoutes.ts @@ -36,7 +36,7 @@ export const getAvailableRoutes = ( const ny = cy + dy; if ( - isInsideLarge(grid, 1, nx, ny) && + isInsideLarge(grid, 2, nx, ny) && !snakeWillSelfCollide(snake, dx, dy) ) { const nsnake = nextSnake(snake, dx, dy); diff --git a/packages/compute/pruneLayer.ts b/packages/compute/pruneLayer.ts index fba847b..34b5943 100644 --- a/packages/compute/pruneLayer.ts +++ b/packages/compute/pruneLayer.ts @@ -4,19 +4,25 @@ import { sortPush } from "./utils/sortPush"; import type { Color, Grid } from "@snk/types/grid"; import type { Point } from "@snk/types/point"; -type M = Point & { parent: M | null; h: number }; +type M = Point & { parent: M | null; h: number; w: number }; const unwrap = (grid: Grid, m: M | null): Point[] => m ? [...unwrap(grid, m.parent), m] : []; +const contains = (arr: Point[], x: number, y: number, limit: number) => { + for (let i = Math.max(0, arr.length - limit); i--; ) + if (arr[i].x === x && arr[i].y === y) return true; + return false; +}; + const getEscapePath = ( grid: Grid, x: number, y: number, color: Color, - forbidden: Point[] = [] + previousSnake: Point[] = [] ) => { - const openList: M[] = [{ x, y, h: 0, parent: null }]; + const openList: M[] = [{ x, y, h: 0, w: 0, parent: null }]; const closeList: Point[] = []; while (openList.length) { @@ -28,7 +34,7 @@ const getEscapePath = ( const x = c.x + a.x; const y = c.y + a.y; - if (!forbidden.some((cl) => cl.x === x && cl.y === y)) { + if (!contains(previousSnake, x, y, c.w)) { if (!isInside(grid, x, y)) return unwrap(grid, { x, y, parent: c } as any); @@ -39,7 +45,7 @@ const getEscapePath = ( !closeList.some((cl) => cl.x === x && cl.y === y) ) { const h = Math.abs(grid.height / 2 - y); - const o = { x, y, parent: c, h }; + const o = { x, y, parent: c, h, w: c.w + 1 }; sortPush(openList, o, (a, b) => a.h - b.h); closeList.push(o); diff --git a/packages/demo/demo.getAvailableRoutes.ts b/packages/demo/demo.getAvailableRoutes.ts index 98f6c84..7e88481 100644 --- a/packages/demo/demo.getAvailableRoutes.ts +++ b/packages/demo/demo.getAvailableRoutes.ts @@ -1,62 +1,85 @@ import { createCanvas } from "./canvas"; -import { snakeToCells } from "@snk/types/snake"; -import { GUI } from "dat.gui"; +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 { Point } from "@snk/types/point"; import type { Snake } from "@snk/types/snake"; +import type { Color, Empty } from "@snk/types/grid"; // // compute -const routes: Snake[][] = []; +const solutions: { + x: number; + y: number; + chain: Snake[]; + color: Color | Empty; +}[] = []; getAvailableRoutes(grid, snake, (chain) => { - routes.push(chain); - return routes.length > 10; + 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; }); - -const config = { routeN: 0, routeK: 0 }; - -// -// draw +solutions.sort((a, b) => a.color - b.color); const { canvas, ctx, draw } = createCanvas(grid); document.body.appendChild(canvas); -draw(grid, snake, []); - -let cancel: number; - -const mod = (x: number, m: number) => ((x % m) + m) % m; +let k = 0; +let i = solutions[k].chain.length - 1; const onChange = () => { - const t = Math.floor(Date.now() / 300); + const { chain } = solutions[k]; - cancelAnimationFrame(cancel); - cancel = requestAnimationFrame(onChange); + ctx.clearRect(0, 0, 9999, 9999); - const chain = routes[config.routeN] || [snake]; - - draw(grid, chain[mod(-t, chain.length)], []); - - const cells: Point[] = []; - chain.forEach((s) => cells.push(...snakeToCells(s))); + draw(grid, chain[i], []); ctx.fillStyle = "orange"; - ctx.fillRect(0, 0, 1, 1); - - cells - .filter((x, i, arr) => i === arr.indexOf(x)) - .forEach((c) => { + chain + .map(snakeToCells) + .flat() + .forEach(({ x, y }) => { ctx.beginPath(); - ctx.fillRect((1 + c.x + 0.5) * 16 - 2, (2 + c.y + 0.5) * 16 - 2, 4, 4); + ctx.fillRect((1 + x + 0.5) * 16 - 2, (2 + y + 0.5) * 16 - 2, 4, 4); }); }; -// -// ui - -const gui = new GUI(); -gui.add(config, "routeN", 0, routes.length - 1, 1).onChange(onChange); - 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); diff --git a/packages/types/__fixtures__/grid.ts b/packages/types/__fixtures__/grid.ts index b6104f9..72389eb 100644 --- a/packages/types/__fixtures__/grid.ts +++ b/packages/types/__fixtures__/grid.ts @@ -19,7 +19,6 @@ setColor(corner, 4, 0, 1 as Color); setColor(corner, 4, 4, 1 as Color); setColor(corner, 0, 0, 1 as Color); -// enclaved color export const enclave = createEmptyGrid(7, 7); setColor(enclave, 3, 4, 2 as Color); setColor(enclave, 2, 3, 2 as Color); @@ -29,13 +28,42 @@ setColor(enclave, 4, 3, 2 as Color); setColor(enclave, 3, 3, 1 as Color); setColor(enclave, 5, 5, 1 as Color); -// enclaved color export const enclaveBorder = createEmptyGrid(7, 7); setColor(enclaveBorder, 1, 0, 3 as Color); setColor(enclaveBorder, 2, 1, 3 as Color); setColor(enclaveBorder, 3, 0, 3 as Color); setColor(enclaveBorder, 2, 0, 1 as Color); +export const enclaveM = createEmptyGrid(7, 7); +setColor(enclaveM, 1, 0, 3 as Color); +setColor(enclaveM, 2, 0, 3 as Color); +setColor(enclaveM, 3, 0, 3 as Color); +setColor(enclaveM, 1, 4, 3 as Color); +setColor(enclaveM, 3, 4, 3 as Color); +setColor(enclaveM, 4, 1, 3 as Color); +setColor(enclaveM, 4, 2, 3 as Color); +setColor(enclaveM, 4, 3, 3 as Color); +setColor(enclaveM, 0, 1, 3 as Color); +setColor(enclaveM, 0, 2, 3 as Color); +setColor(enclaveM, 0, 3, 3 as Color); +setColor(enclaveM, 2, 2, 1 as Color); + +export const enclaveK = createEmptyGrid(7, 7); +setColor(enclaveK, 1, 1, 3 as Color); +setColor(enclaveK, 2, 1, 3 as Color); +setColor(enclaveK, 3, 1, 3 as Color); +setColor(enclaveK, 0, 1, 3 as Color); +setColor(enclaveK, 0, 2, 3 as Color); +setColor(enclaveK, 0, 3, 3 as Color); +setColor(enclaveK, 3, 1, 3 as Color); +setColor(enclaveK, 3, 2, 3 as Color); +setColor(enclaveK, 3, 3, 3 as Color); +setColor(enclaveK, 1, 4, 3 as Color); +setColor(enclaveK, 3, 4, 3 as Color); +setColor(enclaveK, 3, 5, 3 as Color); +setColor(enclaveK, 1, 5, 3 as Color); +setColor(enclaveK, 2, 2, 1 as Color); + const create = (width: number, height: number, emptyP: number) => { const grid = createEmptyGrid(width, height); const random = new ParkMiller(10); diff --git a/packages/types/__fixtures__/snake.ts b/packages/types/__fixtures__/snake.ts index 16ea281..0f56e6d 100644 --- a/packages/types/__fixtures__/snake.ts +++ b/packages/types/__fixtures__/snake.ts @@ -5,5 +5,6 @@ const create = (length: number) => export const snake1 = create(1); export const snake3 = create(3); +export const snake4 = create(4); export const snake5 = create(5); export const snake9 = create(9);