Skip to content

Commit

Permalink
✨ add, update and remove several Grid methods
Browse files Browse the repository at this point in the history
add:
- `filter()`, `map()`, `forEach()` methods, which behave nearly identical to those of `Array`
- `neighborOf()` method
remove:
- `toMap()`, `toObject()` since people can easily do that with `reduce()` and have more control
- `clone()` because it only does `return new Grid(grid)`
- `update()` because people can just use `setHexes()` with one of the iteration methods
update:
- `traverse()` to return a new grid
- add `{ allowOutside }` option to `pointToHex()`, `distance()` and `neighborOf()`.
  `allowOutside` defaults to `true`, but when set to `false` the method returns `null` when it can't find a hex in the grid
  • Loading branch information
flauwekeul committed Jul 31, 2022
1 parent 8cecfef commit e08bc46
Showing 1 changed file with 76 additions and 44 deletions.
120 changes: 76 additions & 44 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CompassDirection } from '../compass'
import { createHex, Hex, HexCoordinates, Point, pointToCube } from '../hex'
import { isFunction } from '../utils'
import { concat, distance } from './functions'
import { concat, distance, neighborOf } from './functions'
import { Traverser } from './types'

export class Grid<T extends Hex> implements Iterable<T> {
Expand Down Expand Up @@ -58,7 +59,53 @@ export class Grid<T extends Hex> implements Iterable<T> {

setHexes(hexes: Iterable<T>): this {
for (const hex of hexes) {
this.#hexes.set(hex.toString(), hex)
this.#setHex(hex)
}
return this
}

filter(predicate: (hex: T) => boolean): Grid<T> {
const result = new Grid(this.hexPrototype)

for (const hex of this) {
if (predicate(hex)) result.#setHex(hex)
}

return result
}

map(fn: (hex: T) => T): Grid<T> {
const result = new Grid(this.hexPrototype)

for (const hex of this) {
result.#setHex(fn(hex))
}

return result
}

traverse(traversers: Traverser<T> | Traverser<T>[], options?: { bail?: boolean }): Grid<T>
traverse(hexes: Iterable<T>, options?: { bail?: boolean }): Grid<T>
traverse(grid: Grid<T>, options?: { bail?: boolean }): Grid<T>
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, options?: { bail?: boolean }): Grid<T>
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, { bail = false } = {}): Grid<T> {
const result = new Grid(this.hexPrototype)

for (const hex of this.#createHexesFromIterableOrTraversers(input)) {
const foundHex = this.getHex(hex)
if (foundHex) {
result.#setHex(foundHex)
} else if (!bail) {
return result
}
}

return result
}

forEach(fn: (hex: T) => void): this {
for (const hex of this) {
fn(hex)
}
return this
}
Expand Down Expand Up @@ -89,61 +136,46 @@ export class Grid<T extends Hex> implements Iterable<T> {
return Array.from(this)
}

// todo: implement like so:
// toMap<K = string>(createKey?: (hex: T) => K): Map<K, T> {
// return new Map(this.#hexes)
// }
toMap(): Map<string, T> {
return new Map(this.#hexes)
}
// todo: find a way to make the return type depend on the value of allowOutside
pointToHex(point: Point, { allowOutside = true } = {}): T | null {
const coordinates = pointToCube(point, this.hexPrototype)

toObject(): Record<string, T> {
const obj: Record<string, T> = {}
for (const hex of this) {
obj[hex.toString()] = hex
if (allowOutside) return this.createHex(coordinates)

const hex = this.getHex(coordinates)
if (!hex) {
return null
}
return obj

return hex
}

traverse(traversers: Traverser<T> | Traverser<T>[], bailOption?: { bail?: boolean }): T[]
traverse(hexes: Iterable<T>, bailOption?: { bail?: boolean }): T[]
traverse(grid: Grid<T>, bailOption?: { bail?: boolean }): T[]
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, bailOption?: { bail?: boolean }): T[]
traverse(input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T>, { bail = false } = {}): T[] {
const result: T[] = []
const hexes = input instanceof Grid ? this : this.#createHexesFromIterableOrTraversers(input)
distance(from: HexCoordinates, to: HexCoordinates, { allowOutside = true } = {}): number | null {
if (allowOutside) return distance(this.hexPrototype, from, to)

for (const hex of hexes) {
const hexInGrid = this.getHex(hex)
if (hexInGrid) {
result.push(hexInGrid)
} else if (bail) {
return result
}
const fromHex = this.getHex(from)
const toHex = this.getHex(to)
if (!fromHex || !toHex) {
return null
}

return result
return distance(this.hexPrototype, fromHex, toHex)
}

clone(): Grid<T> {
return new Grid(this)
}
neighborOf(hex: T, direction: CompassDirection, { allowOutside = true } = {}): T | null {
if (allowOutside) return neighborOf(hex, direction)

update(transform: (hexes: T[]) => T[]): this
update(transform: (hexes: T[]) => T[], traversers: Traverser<T> | Traverser<T>[]): this
update(transform: (hexes: T[]) => T[], hexes: Iterable<T>): this
update(transform: (hexes: T[]) => T[], grid: Grid<T>): this
update(transform: (hexes: T[]) => T[], input: Traverser<T> | Traverser<T>[] | Iterable<T> | Grid<T> = this): this {
const hexes = input instanceof Grid ? input.toArray() : this.traverse(input)
return this.setHexes(transform(hexes))
}
const foundHex = this.getHex(hex)
const neighbor = neighborOf(hex, direction)
if (!foundHex || !neighbor) {
return null
}

pointToHex(point: Point): T | undefined {
return this.getHex(pointToCube(point, this.hexPrototype))
return neighborOf(hex, direction)
}

distance(from: HexCoordinates, to: HexCoordinates): number {
return distance(this.hexPrototype, from, to)
#setHex(hex: T): void {
this.#hexes.set(hex.toString(), hex)
}

#createHexesFromIterableOrTraversers(input: Traverser<T> | Traverser<T>[] | Iterable<T>): Iterable<T> {
Expand Down

0 comments on commit e08bc46

Please sign in to comment.