explodorama / files / test / padTarget.test.ts
padTarget.test.ts
ebcd0a8f 2026-11-02 Pavel Safronov | 1 | import { describe, expect, it } from 'vitest' |
| 2 | import { board } from './helpers' | |
720cc9d3 2026-11-03 Pavel Safronov | 3 | import { aimStart, explainableTiles, firstTarget, jumpTarget } from '../src/ui/padTarget' |
ebcd0a8f 2026-11-02 Pavel Safronov | 4 | |
| 5 | describe('firstTarget', () => { | |
| 6 | it('is null for no tiles', () => { | |
| 7 | expect(firstTarget([], { x: 5, y: 5 })).toBeNull() | |
| 8 | }) | |
| 9 | it('is the nearest tile to the player', () => { | |
| 10 | expect(firstTarget([{ x: 9, y: 5 }, { x: 5, y: 7 }, { x: 4, y: 5 }], { x: 5, y: 5 })).toEqual({ x: 4, y: 5 }) | |
| 11 | }) | |
| 12 | it('breaks a tie by the smaller y, then the smaller x', () => { | |
| 13 | const around = [{ x: 6, y: 5 }, { x: 5, y: 6 }, { x: 4, y: 5 }, { x: 5, y: 4 }] | |
| 14 | expect(firstTarget(around, { x: 5, y: 5 })).toEqual({ x: 5, y: 4 }) | |
| 15 | expect(firstTarget(around.slice(0, 3), { x: 5, y: 5 })).toEqual({ x: 4, y: 5 }) | |
| 16 | }) | |
| 17 | }) | |
| 18 | ||
| 19 | describe('jumpTarget', () => { | |
| 20 | // The four neighbours of (5, 5); the board's y grows down, so up is the smaller y. | |
| 21 | const tiles = [{ x: 5, y: 4 }, { x: 6, y: 5 }, { x: 5, y: 6 }, { x: 4, y: 5 }] | |
| 22 | it('goes the way pressed', () => { | |
| 23 | expect(jumpTarget(tiles, tiles[0]!, 0, 1)).toEqual({ x: 5, y: 6 }) | |
| 24 | expect(jumpTarget(tiles, tiles[1]!, -1, 0)).toEqual({ x: 4, y: 5 }) | |
| 25 | expect(jumpTarget(tiles, tiles[3]!, 1, -1)).toEqual({ x: 5, y: 4 }) | |
| 26 | }) | |
| 27 | it('stays put when nothing lies that way (nothing wraps)', () => { | |
| 28 | expect(jumpTarget(tiles, tiles[0]!, 0, -1)).toEqual({ x: 5, y: 4 }) | |
| 29 | expect(jumpTarget(tiles, tiles[1]!, 1, 0)).toEqual({ x: 6, y: 5 }) | |
| 30 | }) | |
| 31 | it('prefers the tile straight along over a nearer one a row off', () => { | |
| 32 | const row = [{ x: 0, y: 0 }, { x: 1, y: 2 }, { x: 3, y: 0 }] | |
| 33 | expect(jumpTarget(row, row[0]!, 1, 0)).toEqual({ x: 3, y: 0 }) | |
| 34 | }) | |
| 35 | it('falls back to the nearest tile if the current one is not in the list', () => { | |
| 36 | expect(jumpTarget(tiles, { x: 5, y: 5 }, 1, 0)).toEqual({ x: 5, y: 4 }) | |
| 37 | }) | |
| 38 | }) | |
| 39 | ||
| 40 | describe('explainableTiles', () => { | |
| 41 | it('lists what "What is that?" explains, nearest the player first, without plain pillars or the outer wall', () => { | |
| 42 | const s = board(` | |
| 43 | ####### | |
| 44 | #P.+.B# | |
| 45 | #.#...# | |
| 46 | #.$..3# | |
| 47 | #######`) | |
| 48 | const tiles = explainableTiles(s) | |
| 49 | // The crate, the blob, the coin and the bomb; no wall of any kind, no empty floor, and not the player. | |
| 50 | const key = tiles.map((t) => `${t.x},${t.y}`) | |
| 51 | expect(key).toContain('3,1') | |
| 52 | expect(key).toContain('5,1') | |
| 53 | expect(key).toContain('2,3') | |
| 54 | expect(key).toContain('5,3') | |
| 55 | expect(key).toHaveLength(4) | |
| 56 | // Nearest first: the coin (2,3) is 2.2 away, the crate (3,1) is 2 away. | |
| 57 | expect(key[0]).toBe('3,1') | |
| 58 | }) | |
| 59 | }) | |
720cc9d3 2026-11-03 Pavel Safronov | 60 | |
| 61 | describe('Q82: pillars and walls are in the aim', () => { | |
| 62 | const s = board(` | |
| 63 | ####### | |
| 64 | #P.+.B# | |
| 65 | #.#...# | |
| 66 | #######`) | |
| 67 | it('with `plain` the pillar and the outer wall can be aimed at; the aim still starts on something else', () => { | |
| 68 | const all = explainableTiles(s, true).map((t) => `${t.x},${t.y}`) | |
| 69 | expect(all).toContain('2,2') | |
| 70 | expect(all).toContain('0,0') | |
| 71 | expect(all.length).toBeGreaterThan(explainableTiles(s).length) | |
| 72 | // The nearest tile is a wall, but the start is the crate. | |
| 73 | expect(aimStart(s, explainableTiles(s, true))).toEqual({ x: 3, y: 1 }) | |
| 74 | }) | |
| 75 | it('a d-pad press from the crate reaches the pillar below it and the wall above it', () => { | |
| 76 | const tiles = explainableTiles(s, true) | |
| 77 | expect(jumpTarget(tiles, { x: 3, y: 1 }, 0, -1)).toEqual({ x: 3, y: 0 }) | |
| 78 | expect(jumpTarget(tiles, { x: 3, y: 1 }, -1, 1)).toEqual({ x: 2, y: 2 }) | |
| 79 | }) | |
| 80 | }) | |
| 81 | ||
| 82 | describe('Q86: the aim can rest on yourself', () => { | |
| 83 | const s = board(` | |
| 84 | ####### | |
| 85 | #P.+.B# | |
| 86 | #######`) | |
| 87 | it('the full list has the player, the old list and the start do not', () => { | |
| 88 | expect(explainableTiles(s, true).some((t) => t.x === 1 && t.y === 1)).toBe(true) | |
| 89 | expect(explainableTiles(s).some((t) => t.x === 1 && t.y === 1)).toBe(false) | |
| 90 | expect(aimStart(s, explainableTiles(s, true))).toEqual({ x: 3, y: 1 }) | |
| 91 | }) | |
| 92 | it('a d-pad press from the crate goes back to the player', () => { | |
| 93 | expect(jumpTarget(explainableTiles(s, true), { x: 3, y: 1 }, -1, 0)).toEqual({ x: 1, y: 1 }) | |
| 94 | }) | |
| 95 | }) |