From 1aa8f73142cb7c75cc85d47cebf4f76baf5a6c02 Mon Sep 17 00:00:00 2001 From: Abbe Keultjes Date: Sun, 10 Jan 2021 16:13:09 +0100 Subject: [PATCH] feat(grid): subsequent traverse() calls now stop when attempting to traverse outside the grid When a grid has a traverser that's not the (default) infiniteGenerator (currently this can be achieved with grid.rectangle() and grid.traverse(), or by creating a grid with a custom traverser) and it's traversed again, the 2nd traverser (and all subsequent traversers) return early when an attempt is made to move to hex coordinates that are not in the first traverser --- src/grid/grid.ts | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/grid/grid.ts b/src/grid/grid.ts index ef48a796..eca59cba 100644 --- a/src/grid/grid.ts +++ b/src/grid/grid.ts @@ -1,4 +1,4 @@ -import { createHex, Hex } from '../hex' +import { createHex, equals, Hex, HexCoordinates } from '../hex' import { rectangle, RectangleOptions } from './functions' import { GridGenerator, Traverser } from './types' @@ -9,30 +9,18 @@ interface InternalTraverser { // eslint-disable-next-line @typescript-eslint/no-empty-function function* infiniteTraverser(): GridGenerator {} -// fixme: there's a lot of duplicate iteration, use a cache (or memoisation?) export class Grid { static of(hexPrototype: T, traverser?: InternalTraverser) { return new Grid(hexPrototype, traverser) } + // todo: rename traverser to iterator? constructor(public hexPrototype: T, private traverser: InternalTraverser = infiniteTraverser) {} [Symbol.iterator]() { return this.traverser() } - has(coordinates: HexCoordinates) { - // the defaultTraverser "has" all coordinates - if (this.traverser === infiniteTraverser) { - return true - } - for (const hex of this) { - if (equals(hex, coordinates)) { - return true - } - } - } - clone(traverser = this.traverser) { return Grid.of(this.hexPrototype, traverser.bind(this)) } @@ -45,8 +33,8 @@ export class Grid { // fixme: use generic functions for these kinds of operations // something like https://github.com/benji6/imlazy or https://github.com/lodash/lodash/wiki/FP-Guide each(fn: (hex: T) => void) { - const each: InternalTraverser = function* () { - for (const hex of this) { + const each: InternalTraverser = function* each() { + for (const hex of this.traverser()) { fn(hex) yield hex } @@ -55,8 +43,8 @@ export class Grid { } map(fn: (hex: T) => T) { - const map: InternalTraverser = function* () { - for (const hex of this) { + const map: InternalTraverser = function* map() { + for (const hex of this.traverser()) { yield fn(hex) } } @@ -65,7 +53,7 @@ export class Grid { // todo: alias to take or takeUntil? run(stopFn: (hex: T) => boolean = () => false) { - for (const hex of this) { + for (const hex of this.traverser()) { if (stopFn(hex)) { return this } @@ -77,16 +65,23 @@ export class Grid { if (commands.length === 0) { return this // or clone()? todo: when to return clone and when not? } - let coordinates = this.traverser().next().value || { q: 0, r: 0 } - function* traverse(this: Grid) { + const traverse: InternalTraverser = function* traverse() { + const hasTraversedBefore = this.traverser !== infiniteTraverser + const previousHexes = [...this.traverser()] + let coordinates: HexCoordinates = previousHexes[previousHexes.length - 1] || { q: 0, r: 0 } + for (const command of commands) { for (const nextCoordinates of command(coordinates)) { coordinates = nextCoordinates + if (hasTraversedBefore && !previousHexes.some((prevCoords) => equals(prevCoords, coordinates))) { + return // todo: or continue? or make this configurable? + } yield createHex(this.hexPrototype, coordinates) } } } + return this.clone(traverse) } }