Skip to content

Commit

Permalink
feat(grid): restrict Grid.traverse() to the hexes in the grid (from t…
Browse files Browse the repository at this point in the history
…he previous iteration, if any)
  • Loading branch information
flauwekeul committed Apr 22, 2021
1 parent 193531d commit c25249b
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 37 deletions.
20 changes: 9 additions & 11 deletions playground/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { at, createHex, createHexPrototype, Grid, Hex, move, PointyCompassDirection } from '../dist'
import { at, createHex, createHexPrototype, Grid, Hex, move, PointyCompassDirection, repeat } from '../dist'
import { render } from './render'

interface CustomHex extends Hex {
Expand All @@ -13,22 +13,20 @@ const hexPrototype = createHexPrototype<CustomHex>({
})
// const hex = createHex(hexPrototype, { q: 4, r: 3 })

const grid = Grid.of(hexPrototype)
const result = grid
.rectangle({ width: 3, height: 3 })
Grid.of(hexPrototype)
// .rectangle({ width: 3, height: 3 })
.traverse(
// repeat(5, move(PointyCompassDirection.E)),
at({ q: 2, r: 0 }),
move(PointyCompassDirection.E),
move(PointyCompassDirection.E),
at({ q: 1, r: 0 }),
move(PointyCompassDirection.E),
move(PointyCompassDirection.SE),
move(PointyCompassDirection.W),
)
.traverse(at({ q: 1, r: 1 }), repeat(3, move(PointyCompassDirection.E)))
.each((hex) => {
hex.svg = render(createHex(hexPrototype, hex))
// console.log(hex)
console.log(hex)
})
.run(({ r }) => r === 2)
console.log(result)
.run()

// createSuite()
// .add('without prototype', function () {
Expand Down
7 changes: 4 additions & 3 deletions src/grid/functions/at.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { createHex, Hex, HexCoordinates } from '../../hex'
import { GridGeneratorFunction } from '../types'

export const at = <T extends Hex>(coordinates: HexCoordinates) =>
function* next(hex: T) {
export const at = <T extends Hex>(coordinates: HexCoordinates): GridGeneratorFunction<T> =>
function* (currentHex) {
// todo: make createHex accept hex instances or use cloneHex()?
yield createHex(Object.getPrototypeOf(hex), coordinates)
yield createHex(Object.getPrototypeOf(currentHex), coordinates)
}

export const start = at
11 changes: 7 additions & 4 deletions src/grid/functions/move.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { createHex, Hex } from '../../hex'
import { DIRECTION_COORDINATES } from '../constants'
import { FlatCompassDirection, PointyCompassDirection } from '../types'
import { FlatCompassDirection, GridGeneratorFunction, PointyCompassDirection } from '../types'

export const move = <T extends Hex>(direction: PointyCompassDirection | FlatCompassDirection) =>
function* next(hex: T) {
export const move = <T extends Hex>(
direction: PointyCompassDirection | FlatCompassDirection,
): GridGeneratorFunction<T> =>
function* (currentHex) {
const { q, r } = DIRECTION_COORDINATES[direction]
const nextCoordinates = { q: currentHex.q + q, r: currentHex.r + r }
// todo: make createHex accept hex instances or use cloneHex()?
yield createHex(Object.getPrototypeOf(hex), { q: hex.q + q, r: hex.r + r })
yield createHex(Object.getPrototypeOf(currentHex), nextCoordinates)
}
14 changes: 9 additions & 5 deletions src/grid/functions/repeat.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Hex } from '../../hex'
import { GridGenerator } from '../types'
import { GridGeneratorFunction } from '../types'

export const repeat = <T extends Hex>(amount: number, command: (hex: T) => GridGenerator<T>) =>
function* next(hex: T) {
// todo: looks a lot like Grid.traverse()
export const repeat = <T extends Hex>(amount: number, command: GridGeneratorFunction<T>): GridGeneratorFunction<T> =>
function* (currentHex) {
let nextHex = currentHex
for (let i = 0; i < amount; i++) {
const hexes = command(hex)
for (hex of hexes) {
const hexes = command(nextHex)
for (const hex of hexes) {
yield hex
nextHex = hex
}
// todo: yield* command(grid, nextHex)
}
}
48 changes: 34 additions & 14 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
import { Hex } from '../hex'
import { equals, Hex, HexCoordinates } from '../hex'
import { rectangle, RectangleOptions } from './functions'
import { GridGenerator } from './types'
import { GridGenerator, GridGeneratorFunction } from './types'

// eslint-disable-next-line @typescript-eslint/no-empty-function
function* defaultTraverser<T extends Hex>(): GridGenerator<T> {}
function* infiniteTraverser<T extends Hex>(): GridGenerator<T> {}

// fixme: there's a lot of duplicate iteration, use a cache (or memoisation?)
export class Grid<T extends Hex> {
static of<T extends Hex>(hexPrototype: T, traverser?: (this: Grid<T>) => GridGenerator<T>) {
static of<T extends Hex>(hexPrototype: T, traverser?: GridGeneratorFunction<T>) {
return new Grid(hexPrototype, traverser)
}

constructor(public hexPrototype: T, private traverser: () => GridGenerator<T> = defaultTraverser) {}
constructor(public hexPrototype: T, private traverser: GridGeneratorFunction<T> = infiniteTraverser) {}

[Symbol.iterator]() {
return this.traverser()
// todo: {} as T is a bit hacky, but making it an optional parameter is meh
return this.traverser({} as T)
}

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) {
// todo: maybe not bind and pass grid to traverser?
return new Grid(this.hexPrototype, traverser.bind(this))
// todo: maybe not bind and pass grid to traverser? Tried this, but it results in a "Maximum call stack size exceeded" error
return Grid.of(this.hexPrototype, traverser.bind(this))
}

rectangle(options: RectangleOptions) {
Expand All @@ -31,6 +45,8 @@ export class Grid<T extends Hex> {
// return ((grid: Grid<T>) => fns.reduce((prev, fn) => fn(prev), grid))(this)
// }

// 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 traverser = function* (this: Grid<T>) {
for (const hex of this) {
Expand Down Expand Up @@ -60,19 +76,23 @@ export class Grid<T extends Hex> {
return this // or clone()? todo: when to return clone and when not?
}

traverse(...commands: ((hex: T) => GridGenerator<T>)[]) {
// todo: move this inside generator?
traverse(...commands: GridGeneratorFunction<T>[]) {
if (commands.length === 0) {
return this.clone()
return this // or clone()? todo: when to return clone and when not?
}
let nextHex = this.traverser().next().value || ({ q: 0, r: 0 } as T)
// todo: {} as T is a bit hacky, but making it an optional parameter is meh
let currentHex = this.traverser({} as T).next().value || ({ q: 0, r: 0 } as T)

const traverser = function* (this: Grid<T>) {
for (const command of commands) {
const hexes = command(nextHex)
const hexes = command(currentHex)
for (const hex of hexes) {
// stop once the hex is outside the grid
if (!this.has(hex)) {
return
}
yield hex
nextHex = hex
currentHex = hex
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/grid/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ export enum FlatCompassDirection {
}

export type GridGenerator<T extends Hex> = Generator<T, void>

export interface GridGeneratorFunction<T extends Hex> {
(currentHex: T): GridGenerator<T>
}
3 changes: 3 additions & 0 deletions src/hex/functions/equals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { AxialCoordinates } from '../types'

export const equals = (hexA: AxialCoordinates, hexB: AxialCoordinates) => hexA.q === hexB.q && hexA.r === hexB.r
1 change: 1 addition & 0 deletions src/hex/functions/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './corners'
export * from './createHex'
export * from './createHexPrototype'
export * from './equals'
export * from './height'
export * from './hexToPoint'
export * from './isFlat'
Expand Down

0 comments on commit c25249b

Please sign in to comment.