Skip to content

Commit

Permalink
feat(grid): add map() method to Grid
Browse files Browse the repository at this point in the history
map() behaves the same as each(), except that the function supplied to map() is called with a cloned
hex so that the user may mutate it at her heart's content without affecting the hexes in the source
grid.
  • Loading branch information
flauwekeul committed Apr 22, 2021
1 parent 8475491 commit 3716e6c
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 8 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ These methods exist in v3 and they need to be considered for v4.
- [ ] **Assertion**:
- [ ] ? grid.some() whether any hex passes predicate
- [ ] ? grid.every() whether all hexes pass predicate
- [ ] **Mutation/reduction**?:
- [ ] **Mutation/reduction**:
- [ ] `grid.update((grid) => void)`
```typescript
// the passed grid is already a clone, similar to Immer
Expand All @@ -360,10 +360,10 @@ These methods exist in v3 and they need to be considered for v4.
grid.store = new Map(grid.hexes().map((hex) => [hex.toString(), hex]))
})
```
- [ ] `grid.reduce<T>((T, hex, grid) => T, T)`
- [ ] `grid.toArray()`
- [ ] `grid.toJSON()`
- [ ] `grid.toString()` / `grid.serialize()`
- [ ] `grid.reduce<R>((R, hex, grid) => R, R): R`
- [ ] `grid.toArray(): T[]`
- [ ] ~~`grid.toJSON()`~~
- [ ] ~~`grid.toString()` / `grid.serialize()`~~
- [ ] ~~`grid.toLinkedList()`~~
- [ ] ~~`grid.toRecord()`~~
- [ ] ~~`grid.toMap()`~~ (just use `grid.store`)
Expand Down
81 changes: 78 additions & 3 deletions src/grid/grid.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ describe('getHex()', () => {
})

describe('each()', () => {
test('returns a new grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.each(jest.fn())

expect(result).not.toBe(grid)
})

test('iterates over each hex from the previous iterator/traverser', () => {
const callback = jest.fn()
const grid1 = new Grid(hexPrototype, [at({ q: 1, r: 2 }), at({ q: 3, r: 4 })]).each(callback).run()
Expand All @@ -208,7 +215,55 @@ describe('each()', () => {
})
})

describe('map()', () => {
interface TestHex extends Hex {
a: number
}

test('returns a new grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.map(jest.fn())

expect(result).not.toBe(grid)
})

test('creates a clone of each hex and passes it to the callback', () => {
const hexPrototype = createHexPrototype<TestHex>()
const mapCallback = jest.fn((hex) => hex.clone({ a: 1 }))
const runCallback = jest.fn()
const hex = createHex(hexPrototype, { q: 1, r: 2 })
const grid = new Grid(hexPrototype, () => [hex]).map(mapCallback)
const runGrid = grid.run(runCallback)

expect(mapCallback.mock.calls).toEqual([[hex, grid]])
expect(runCallback.mock.calls[0][0]).not.toBe(hex)
expect(runCallback.mock.calls).toEqual([[createHex(hexPrototype, { q: 1, r: 2, a: 1 }), runGrid]])
})

test(`the passed callback doesn't have to return a hex`, () => {
const hexPrototype = createHexPrototype<TestHex>()
const mapCallback = jest.fn((hex) => {
hex.a = 2
})
const runCallback = jest.fn()
const hex = createHex(hexPrototype, { q: 1, r: 2 })
const grid = new Grid(hexPrototype, () => [hex]).map(mapCallback)

grid.run(runCallback)

expect(mapCallback.mock.calls[0][0]).toEqual(createHex(hexPrototype, { q: 1, r: 2, a: 2 })) // hex is mutated
expect(runCallback.mock.calls[0][0]).toEqual(createHex(hexPrototype, { q: 1, r: 2, a: 2 }))
})
})

describe('filter()', () => {
test('returns a new grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.filter(jest.fn())

expect(result).not.toBe(grid)
})

test('filters hexes', () => {
const callback = jest.fn()
const grid = new Grid(hexPrototype, [at({ q: 1, r: 1 }), at({ q: 2, r: 2 }), at({ q: 3, r: 3 })])
Expand All @@ -223,6 +278,13 @@ describe('filter()', () => {
})

describe('takeWhile()', () => {
test('returns a new grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.takeWhile(jest.fn())

expect(result).not.toBe(grid)
})

test('stops when the passed predicate returns false', () => {
const callback = jest.fn()
const grid = new Grid(hexPrototype, [at({ q: 1, r: 1 }), at({ q: 2, r: 2 }), at({ q: 3, r: 3 })])
Expand All @@ -234,6 +296,13 @@ describe('takeWhile()', () => {
})

describe('traverse()', () => {
test('returns a new grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.traverse([])

expect(result).not.toBe(grid)
})

test('accepts a single traverser', () => {
const traverser = jest.fn(() => [])
const grid = new Grid(hexPrototype)
Expand Down Expand Up @@ -295,7 +364,14 @@ describe('traverse()', () => {
})

describe('run()', () => {
test('runs all iterators recursively and returns itself', () => {
test('returns the same grid', () => {
const grid = new Grid(hexPrototype)
const result = grid.run()

expect(result).toBe(grid)
})

test('runs all iterators recursively', () => {
const eachCallback = jest.fn()
const filterCallback = jest.fn((hex) => hex.q > 1)
const runCallback = jest.fn()
Expand All @@ -305,9 +381,8 @@ describe('run()', () => {

expect(eachCallback).not.toBeCalled()

const result = grid.run(runCallback)
grid.run(runCallback)

expect(result).toBe(grid)
expect(eachCallback.mock.calls).toEqual([
[createHex(hexPrototype, { q: 1, r: 2 }), grid],
[createHex(hexPrototype, { q: 3, r: 4 }), grid],
Expand Down
18 changes: 18 additions & 0 deletions src/grid/grid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,27 @@ export class Grid<T extends Hex> {
}
return prevHexState
}

return this._clone(each)
}

map(callback: Callback<T, T | void>) {
const map: GetHexState<T> = (currentGrid) => {
const nextHexes: T[] = []
const prevHexState = this._getPrevHexState(currentGrid)
let cursor = prevHexState.cursor

for (const hex of prevHexState.hexes) {
cursor = hex.clone()
nextHexes.push(callback(cursor, currentGrid) || cursor)
}

return { hexes: nextHexes, cursor }
}

return this._clone(map)
}

filter(predicate: Callback<T, boolean>) {
const filter: GetHexState<T> = (currentGrid) => {
const nextHexes: T[] = []
Expand Down

0 comments on commit 3716e6c

Please sign in to comment.