diff --git a/src/models/tiles-in-range.test.ts b/src/models/tiles-in-range.test.ts new file mode 100644 index 0000000..a9aca5c --- /dev/null +++ b/src/models/tiles-in-range.test.ts @@ -0,0 +1,50 @@ +import { tilesInRange } from "./tiles-in-range"; + +describe("tilesInRange()", () => { + /** + * [ ] [X] [X] + * [ ] [X] [.] [X] + * [ ] [ ] [X] [X] [ ] + * + */ + it("returns tilesID[] in range 1 from origin tile", () => { + const got = tilesInRange("1,-2", { range: 1 }); + const expected = ["1,-3", "2,-3", "0,-2", "2,-2", "1,-1", "2,-1"]; + expect(got.sort()).toEqual(expected.sort()); + }); + + /** + * [ ] [ ] [X] [X] [X] + * [ ] [ ] [X] [X] [X] [X] + * [ ] [ ] [X] [X] [.] [X] [X] + * [ ] [ ] [ ] [X] [X] [X] [X] [ ] + * [ ] [ ] [ ] [X] [X] [X] [ ] + * [ ] [ ] [ ] [ ] [ ] [ ] + * [ ] [ ] [ ] [ ] [ ] + * + */ + it("returns tilesID[] in range 2 from origin tile", () => { + const got = tilesInRange("1,-1", { range: 2 }); + const expected = [ + "0,-3", + "1,-3", + "2,-3", + "-1,-2", + "0,-2", + "1,-2", + "2,-2", + "-1,-1", + "0,-1", + "2,-1", + "3,-1", + "-1,0", + "0,0", + "1,0", + "2,0", + "0,1", + "1,1", + "2,1", + ]; + expect(got.sort()).toEqual(expected.sort()); + }); +}); diff --git a/src/models/tiles-in-range.ts b/src/models/tiles-in-range.ts new file mode 100644 index 0000000..31233bd --- /dev/null +++ b/src/models/tiles-in-range.ts @@ -0,0 +1,68 @@ +import { asTileID, coordinates } from "./tiles"; + +const NEIGHBOUR_TILES_MEM_CACHE = {} as Record; + +export function tilesInRange( + tileId: TileID, + { range }: { range?: number } = {} +) { + return tilesInRangeRec(tileId, range || 1, []).filter((t) => t !== tileId); +} + +function tilesInRangeRec( + tileId: TileID, + range: number, + acc: TileID[] +): TileID[] { + const neighbourTiles = tilesInRange1Cached(tileId); + if (range === 1) { + return addAll(acc, neighbourTiles); + } + const tiles = neighbourTiles.flatMap((tileId) => + tilesInRangeRec(tileId, range - 1, acc) + ); + return addAll(acc, tiles); +} + +function addAll(a: T[], b: T[]) { + return Array.from(new Set([...a, ...b])); +} + +function tilesInRange1Cached(tileId: TileID) { + if (!NEIGHBOUR_TILES_MEM_CACHE[tileId]) { + NEIGHBOUR_TILES_MEM_CACHE[tileId] = tilesInRange1(tileId); + } + return NEIGHBOUR_TILES_MEM_CACHE[tileId]; +} + +function tilesInRange1(tileId: TileID) { + const { x, y } = coordinates(tileId); + + return range1Variance(y).flatMap((variance) => { + const tileIdStr = asTileID({ + x: x + variance[0], + y: y + variance[1], + }); + return tileIdStr ? [tileIdStr] : []; + }); +} + +function range1Variance(y: number) { + return y % 2 === 0 + ? [ + [0, -1], + [+1, -1], + [-1, 0], + [+1, 0], + [0, +1], + [+1, +1], + ] + : [ + [-1, -1], + [0, -1], + [-1, 0], + [+1, 0], + [-1, +1], + [0, +1], + ]; +} diff --git a/src/models/tiles.test.ts b/src/models/tiles.test.ts deleted file mode 100644 index 091048d..0000000 --- a/src/models/tiles.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { tilesInRange } from "./tiles"; - -describe("tilesInRange()", () => { - /** - * [ ] [X] [X] - * [ ] [X] [.] [X] - * [ ] [ ] [X] [X] [ ] - * - */ - it("returns tilesID[] in range 1 from origin tile", () => { - const got = tilesInRange("1,-2", { range: 1 }); - expect(got).toEqual(["1,-3", "2,-3", "0,-2", "2,-2", "1,-1", "2,-1"]); - }); -}); diff --git a/src/models/tiles.ts b/src/models/tiles.ts index fdca1fb..4445236 100644 --- a/src/models/tiles.ts +++ b/src/models/tiles.ts @@ -8,7 +8,6 @@ * [ ] [ ] [ ] [ ] [ ] [ ] * [ ] [ ] [ ] [ ] [ ] * - * => * * [-2,-3] [-1,-3] [+0,-3] [1,-3] [2,-3] * @@ -101,35 +100,3 @@ export function asTileID({ x, y }: { x: number; y: number }): TileID | null { export function row(n: -3 | -2 | -1 | 0 | 1 | 2 | 3): TileID[] { return tiles.filter((id) => coordinates(id).y === n); } - -export function tilesInRange(tile: TileID, { range } = { range: 1 }): TileID[] { - const { x, y } = coordinates(tile); - - // TODO use range param instead of fixed array? - const range1Movements = - y % 2 === 0 - ? [ - [0, -1], - [+1, -1], - [-1, 0], - [+1, 0], - [0, +1], - [+1, +1], - ] - : [ - [-1, -1], - [0, -1], - [-1, 0], - [+1, 0], - [-1, +1], - [0, +1], - ]; - - return range1Movements.flatMap((variance) => { - const tileIdStr = asTileID({ - x: x + variance[0], - y: y + variance[1], - }); - return tileIdStr ? [tileIdStr] : []; - }); -}