Skip to content

Commit

Permalink
feat(grid): a grid can now be created with an optional traverser
Browse files Browse the repository at this point in the history
A grid without traverser is pretty useless. So an optional traverser can now be passed as the 2nd
argument to the Grid constructor (or `Grid.of()`) and will be converted to internal "hex state".
Made the clone method private because I don't think people will use it and it requires an internal
function (GetHexState) is passed.
  • Loading branch information
flauwekeul committed Apr 22, 2021
1 parent 37811fa commit e56ced8
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 50 deletions.
16 changes: 13 additions & 3 deletions playground/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,15 @@
import { at, CompassDirection, createHexPrototype, Grid, Hex, inStore, move, Orientation, setStore } from '../dist'
import {
at,
CompassDirection,
createHexPrototype,
Grid,
Hex,
inStore,
move,
Orientation,
rectangle,
setStore,
} from '../dist'
import { createSuite } from './benchmark'
import { render } from './render'

Expand Down Expand Up @@ -42,8 +53,7 @@ const hexPrototype = createHexPrototype<CustomHex>({
// const hex = createHex(hexPrototype, { q: 4, r: 3 })

const store = new Map<string, CustomHex>()
const grid = Grid.of(hexPrototype, store)
.rectangle({ start: { q: 0, r: 0 }, width: 10, height: 10 })
const grid = Grid.of(hexPrototype, rectangle({ start: { q: 0, r: 0 }, width: 10, height: 10 }), store)
.each(setStore())
.traverse(at({ q: 9, r: 0 }), move(CompassDirection.SE, 4), move(CompassDirection.SW, 4))
.filter(inStore())
Expand Down
81 changes: 43 additions & 38 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { createHex, Hex, HexCoordinates } from '../hex'
import { rectangle, RectangleOptions } from './traversers'
import { Callback, GetHexState, Traverser } from './types'
import { Callback, Traverser } from './types'

export class Grid<T extends Hex> {
static of<T extends Hex>(hexPrototype: T, store?: Map<string, T>, getPrevHexState?: GetHexState<T>) {
return new Grid<T>(hexPrototype, store, getPrevHexState)
static of<T extends Hex>(hexPrototype: T, traverser?: Traverser<T> | null, store?: Map<string, T>) {
return new Grid<T>(hexPrototype, traverser, store)
}

get [Symbol.toStringTag]() {
Expand All @@ -13,45 +12,44 @@ export class Grid<T extends Hex> {

store?: Map<string, T>

constructor(
public hexPrototype: T,
// fixme: it makes no sense to create a grid without it having hexes (in the form of a traverser or store)
store?: Map<string, T>,
private getPrevHexState: GetHexState<T> = () => ({ hexes: [], cursor: null }),
) {
private _getPrevHexState: GetHexState<T> = () => ({ hexes: [], cursor: null })

constructor(public hexPrototype: T, traverser?: Traverser<T> | null, store?: Map<string, T>) {
if (traverser) {
this._getPrevHexState = () => {
const hexes = Array.from(traverser(this.getHex(), this.getHex.bind(this)))
return { hexes, cursor: hexes[hexes.length - 1] }
}
}
this.store = store && new Map(store)
}

*[Symbol.iterator]() {
for (const hex of this.getPrevHexState(this).hexes) {
for (const hex of this._getPrevHexState(this).hexes) {
yield hex
}
}

clone(getPrevHexState = this.getPrevHexState) {
return new Grid(this.hexPrototype, this.store, getPrevHexState)
}

getHex(coordinates?: HexCoordinates) {
const hex = createHex(this.hexPrototype).clone(coordinates) // clone to enable users to make custom hexes
return this.store?.get(hex.toString()) ?? hex
}

each(callback: Callback<T, void>) {
const each: GetHexState<T> = (currentGrid) => {
const prevHexState = this.getPrevHexState(currentGrid)
const prevHexState = this._getPrevHexState(currentGrid)
for (const hex of prevHexState.hexes) {
callback(hex, currentGrid)
}
return prevHexState
}
return this.clone(each)
return this._clone(each)
}

filter(predicate: Callback<T, boolean>) {
const filter: GetHexState<T> = (currentGrid) => {
const nextHexes: T[] = []
const prevHexState = this.getPrevHexState(currentGrid)
const prevHexState = this._getPrevHexState(currentGrid)
let cursor = prevHexState.cursor

for (const hex of prevHexState.hexes) {
Expand All @@ -64,13 +62,13 @@ export class Grid<T extends Hex> {
return { hexes: nextHexes, cursor }
}

return this.clone(filter)
return this._clone(filter)
}

takeWhile(predicate: Callback<T, boolean>) {
const takeWhile: GetHexState<T> = (currentGrid) => {
const nextHexes: T[] = []
const prevHexState = this.getPrevHexState(currentGrid)
const prevHexState = this._getPrevHexState(currentGrid)
let cursor = prevHexState.cursor

for (const hex of prevHexState.hexes) {
Expand All @@ -84,22 +82,7 @@ export class Grid<T extends Hex> {
return { hexes: nextHexes, cursor }
}

return this.clone(takeWhile)
}

run(until?: Callback<T, void>) {
for (const hex of this.getPrevHexState(this).hexes) {
until && until(hex, this)
}
return this
}

// todo: maybe remove this method? What's wrong with just calling grid.traverse(rectangle({ ... }))?
// todo: add in docs: only 90° corners for cardinal directions
rectangle(options: RectangleOptions): Grid<T>
rectangle(cornerA: HexCoordinates, cornerB: HexCoordinates): Grid<T>
rectangle(optionsOrCornerA: RectangleOptions | HexCoordinates, cornerB?: HexCoordinates) {
return this.traverse(rectangle(optionsOrCornerA as HexCoordinates, cornerB as HexCoordinates))
return this._clone(takeWhile)
}

traverse(...traversers: Traverser<T>[]) {
Expand All @@ -109,7 +92,7 @@ export class Grid<T extends Hex> {

const traverse: GetHexState<T> = (currentGrid) => {
const nextHexes: T[] = []
let cursor = this.getPrevHexState(currentGrid).cursor ?? this.getHex()
let cursor = this._getPrevHexState(currentGrid).cursor ?? this.getHex()

for (const traverser of traversers) {
for (const nextCursor of traverser(cursor, this.getHex.bind(this))) {
Expand All @@ -121,6 +104,28 @@ export class Grid<T extends Hex> {
return { hexes: nextHexes, cursor }
}

return this.clone(traverse)
return this._clone(traverse)
}

run(until?: Callback<T, void>) {
for (const hex of this._getPrevHexState(this).hexes) {
until && until(hex, this)
}
return this
}

private _clone(getHexState: GetHexState<T>) {
const newGrid = new Grid(this.hexPrototype, null, this.store)
newGrid._getPrevHexState = getHexState
return newGrid
}
}

interface GetHexState<T extends Hex> {
(grid: Grid<T>): HexState<T>
}

interface HexState<T extends Hex> {
hexes: T[]
cursor: T | null
}
9 changes: 0 additions & 9 deletions src/grid/types.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
import { Hex, HexCoordinates } from '../hex'
import { Grid } from './grid'

export interface HexState<T extends Hex> {
hexes: T[]
cursor: T | null
}

export interface GetHexState<T extends Hex> {
(grid: Grid<T>): HexState<T>
}

export interface Traverser<T extends Hex> {
(cursor: T, getHex: (coordinates: HexCoordinates) => T): Iterable<T>
}
Expand Down

0 comments on commit e56ced8

Please sign in to comment.