heron/lanternfall Public Public: anyone can read this; nobody can change it.
workbenchthis machine
Files e53b98b72d84ce5feeddbe32d2586ca5df139720
| 1 | import { expect, test } from "bun:test"; |
| 2 | import { |
| 3 | DEPTHS, HEIGHT, WIDTH, at, distance, floorAt, openCells, seesFrom, |
| 4 | stepToward, visibleFrom, walkable, xy, |
| 5 | } from "./dungeon.ts"; |
| 6 | |
| 7 | test("a floor is the same floor every time it is asked for", () => { |
| 8 | for (let depth = 1; depth <= DEPTHS; depth += 1) |
| 9 | expect(floorAt(depth).tiles.join("")).toBe(floorAt(depth).tiles.join("")); |
| 10 | expect(floorAt(1).tiles.join("")).not.toBe(floorAt(2).tiles.join("")); |
| 11 | }); |
| 12 | |
| 13 | test("every floor has rooms, an entrance, and a way on", () => { |
| 14 | for (let depth = 1; depth <= DEPTHS; depth += 1) { |
| 15 | const floor = floorAt(depth); |
| 16 | expect(floor.rooms.length).toBeGreaterThanOrEqual(5); |
| 17 | expect(openCells(floor).length).toBeGreaterThan(150); |
| 18 | expect(floor.tiles[floor.entrance]).toBe("<"); |
| 19 | if (depth < DEPTHS) expect(floor.tiles[floor.descent as number]).toBe(">"); |
| 20 | else expect(floor.descent).toBeUndefined(); |
| 21 | } |
| 22 | }); |
| 23 | |
| 24 | test("the stair is reachable from the entrance", () => { |
| 25 | // A floor you cannot cross is a floor nobody can finish. Flood fill. |
| 26 | for (let depth = 1; depth < DEPTHS; depth += 1) { |
| 27 | const floor = floorAt(depth); |
| 28 | const seen = new Set<number>([floor.entrance]); |
| 29 | const queue = [floor.entrance]; |
| 30 | while (queue.length > 0) { |
| 31 | const cell = queue.shift() as number; |
| 32 | const { x, y } = xy(cell); |
| 33 | for (const [dx, dy] of [[0,-1],[0,1],[1,0],[-1,0]] as const) { |
| 34 | const nx = x + dx; |
| 35 | const ny = y + dy; |
| 36 | if (nx < 0 || ny < 0 || nx >= WIDTH || ny >= HEIGHT) continue; |
| 37 | const next = at(nx, ny); |
| 38 | if (seen.has(next) || !walkable(floor.tiles[next])) continue; |
| 39 | seen.add(next); |
| 40 | queue.push(next); |
| 41 | } |
| 42 | } |
| 43 | expect(seen.has(floor.descent as number)).toBe(true); |
| 44 | } |
| 45 | }); |
| 46 | |
| 47 | test("a wall stops the light at the wall", () => { |
| 48 | const floor = floorAt(1); |
| 49 | const wall = floor.tiles.findIndex((t) => t === "#"); |
| 50 | const lit = visibleFrom(floor, floor.entrance); |
| 51 | expect(lit).toContain(floor.entrance); |
| 52 | // Nothing lit is further than the lantern reaches. |
| 53 | for (const cell of lit) expect(distance(floor.entrance, cell)).toBeLessThanOrEqual(4); |
| 54 | // And sight into solid rock stops: a cell behind a wall is not visible. |
| 55 | const blocked = floor.tiles.findIndex( |
| 56 | (t, i) => t === "." && !seesFrom(floor, floor.entrance, i) && distance(floor.entrance, i) <= 4, |
| 57 | ); |
| 58 | if (blocked !== -1) expect(lit).not.toContain(blocked); |
| 59 | expect(wall).toBeGreaterThan(-1); |
| 60 | }); |
| 61 | |
| 62 | test("a step toward something goes around the corner it cannot go through", () => { |
| 63 | const floor = floorAt(1); |
| 64 | const from = floor.entrance; |
| 65 | const step = stepToward(floor, from, floor.descent as number); |
| 66 | expect(walkable(floor.tiles[step])).toBe(true); |
| 67 | expect(distance(step, from)).toBeLessThanOrEqual(1); |
| 68 | }); |