Skip to content

Commit

Permalink
feat(grid): improve typing of Grid methods and traverse commands
Browse files Browse the repository at this point in the history
The Grid and traverse commands now work with a type parameter to support custom hex types
  • Loading branch information
flauwekeul committed Apr 22, 2021
1 parent b7fcaf5 commit 193531d
Show file tree
Hide file tree
Showing 9 changed files with 49 additions and 36 deletions.
14 changes: 8 additions & 6 deletions playground/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface CustomHex extends Hex {

const hexPrototype = createHexPrototype<CustomHex>({
dimensions: 30,
custom: 'custom',
custom: 'custom', // fixme: adding `orientation: 'flat'` makes this an error, adding `orientation: Orientation.FLAT` doesn't
origin: (hexPrototype) => ({ x: hexPrototype.width * -0.5, y: hexPrototype.height * -0.5 }),
})
// const hex = createHex(hexPrototype, { q: 4, r: 3 })
Expand All @@ -30,8 +30,10 @@ const result = grid
.run(({ r }) => r === 2)
console.log(result)

/**
* Todo:
* - how to hold state? Probably use serialized hex coordinates
* - what to do with infinite grids and "sub grids" (like what rectangle() returns)
*/
// createSuite()
// .add('without prototype', function () {
// rectangle2(hexPrototype, { width: 5, height: 5 })
// })
// .add('with prototype', function () {
// rectangle(hexPrototype, { width: 5, height: 5 })
// })
10 changes: 10 additions & 0 deletions src/grid/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { AxialCoordinates } from '../hex'

export const DIRECTION_COORDINATES: AxialCoordinates[] = [
{ q: 1, r: 0 },
{ q: 0, r: 1 },
{ q: -1, r: 1 },
{ q: -1, r: 0 },
{ q: 0, r: -1 },
{ q: 1, r: -1 },
]
9 changes: 5 additions & 4 deletions src/grid/functions/at.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { HexCoordinates } from '../../hex'
import { createHex, Hex, HexCoordinates } from '../../hex'

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

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

export const move = (direction: PointyCompassDirection | FlatCompassDirection) =>
function* next({ q, r }: HexCoordinates) {
console.warn('fixme: this always returns the same hex coordinate')
yield { q: q + 1, r }
export const move = <T extends Hex>(direction: PointyCompassDirection | FlatCompassDirection) =>
function* next(hex: T) {
const { q, r } = DIRECTION_COORDINATES[direction]
// todo: make createHex accept hex instances or use cloneHex()?
yield createHex(Object.getPrototypeOf(hex), { q: hex.q + q, r: hex.r + r })
}
8 changes: 3 additions & 5 deletions src/grid/functions/rectangle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CubeCoordinates, DefaultHexPrototype, isPointy } from '../../hex'
import { createHex, CubeCoordinates, Hex, isPointy } from '../../hex'
import { offsetFromZero, signedModulo } from '../../utils'
import { FlatCompassDirection, PointyCompassDirection } from '../types'

Expand All @@ -20,7 +20,7 @@ const DIRECTIONS = [
['q', 's', 'r'],
] as [keyof CubeCoordinates, keyof CubeCoordinates, keyof CubeCoordinates][]

export function* rectangle<T extends DefaultHexPrototype>(
export function* rectangle<T extends Hex>(
hexPrototype: T,
{
width,
Expand All @@ -45,9 +45,7 @@ export function* rectangle<T extends DefaultHexPrototype>(
[secondCoordinate]: second + start[secondCoordinate],
[thirdCoordinate]: -first - second + start[thirdCoordinate],
} as unknown
// todo: leave it to the consumer to create a hex?
// yield createHex(hexPrototype, nextCoordinates as CubeCoordinates)
yield nextCoordinates as CubeCoordinates
yield createHex(hexPrototype, nextCoordinates as CubeCoordinates)
}
}
}
6 changes: 3 additions & 3 deletions src/grid/functions/repeat.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { HexCoordinates } from '../../hex'
import { Hex } from '../../hex'
import { GridGenerator } from '../types'

export const repeat = (amount: number, command: (hex: HexCoordinates) => GridGenerator) =>
function* next(hex: HexCoordinates) {
export const repeat = <T extends Hex>(amount: number, command: (hex: T) => GridGenerator<T>) =>
function* next(hex: T) {
for (let i = 0; i < amount; i++) {
const hexes = command(hex)
for (hex of hexes) {
Expand Down
21 changes: 10 additions & 11 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { DefaultHexPrototype, HexCoordinates } from '../hex'
import { Hex } from '../hex'
import { rectangle, RectangleOptions } from './functions'
import { GridGenerator } from './types'

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

// fixme: probably not `T extends DefaultHexPrototype`, but `T extends Hex`
export class Grid<T extends DefaultHexPrototype> {
static of<T extends DefaultHexPrototype>(hexPrototype: T, traverser?: (this: Grid<T>) => GridGenerator) {
export class Grid<T extends Hex> {
static of<T extends Hex>(hexPrototype: T, traverser?: (this: Grid<T>) => GridGenerator<T>) {
return new Grid(hexPrototype, traverser)
}

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

[Symbol.iterator]() {
return this.traverser()
Expand All @@ -32,7 +31,7 @@ export class Grid<T extends DefaultHexPrototype> {
// return ((grid: Grid<T>) => fns.reduce((prev, fn) => fn(prev), grid))(this)
// }

each(fn: (hex: HexCoordinates) => void) {
each(fn: (hex: T) => void) {
const traverser = function* (this: Grid<T>) {
for (const hex of this) {
fn(hex)
Expand All @@ -42,7 +41,7 @@ export class Grid<T extends DefaultHexPrototype> {
return this.clone(traverser)
}

map(fn: (hex: HexCoordinates) => HexCoordinates) {
map(fn: (hex: T) => T) {
const traverser = function* (this: Grid<T>) {
for (const hex of this) {
yield fn(hex)
Expand All @@ -52,7 +51,7 @@ export class Grid<T extends DefaultHexPrototype> {
}

// todo: other/more args?
run(stopFn: (hex: HexCoordinates) => boolean = () => false) {
run(stopFn: (hex: T) => boolean = () => false) {
for (const hex of this) {
if (stopFn(hex)) {
return this
Expand All @@ -61,12 +60,12 @@ export class Grid<T extends DefaultHexPrototype> {
return this // or clone()? todo: when to return clone and when not?
}

traverse(...commands: ((hex: HexCoordinates) => GridGenerator)[]) {
traverse(...commands: ((hex: T) => GridGenerator<T>)[]) {
// todo: move this inside generator?
if (commands.length === 0) {
return this.clone()
}
let nextHex = this.traverser().next().value || { q: 0, r: 0 }
let nextHex = this.traverser().next().value || ({ q: 0, r: 0 } as T)

const traverser = function* (this: Grid<T>) {
for (const command of commands) {
Expand Down
1 change: 1 addition & 0 deletions src/grid/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './constants'
export * from './functions'
export * from './grid'
export * from './types'
4 changes: 2 additions & 2 deletions src/grid/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HexCoordinates } from '../hex'
import { Hex } from '../hex'

export enum CompassDirection {
E,
Expand Down Expand Up @@ -29,4 +29,4 @@ export enum FlatCompassDirection {
NE,
}

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

0 comments on commit 193531d

Please sign in to comment.