import assert from 'node:assert/strict'; import { test } from 'node:test'; import { Board } from '../src/board'; import { bridgeRect, cellRect, cellShapeRects, fitBoard, hitTest } from '../src/layout'; import type { CellDef } from '../src/types'; const c = (id: string, x: number, y: number, w = 1, h = 1, clue?: number): CellDef => ({ id, x, y, w, h, clue }); test('按短边缩放并居中,受 maxUnit 限制', () => { const b = Board.fromCells([c('a', 0, 0), c('b', 1, 0), c('c', 2, 0)]); const L = fitBoard(b, 390, 600, { padding: 10, maxUnit: 96 }); assert.equal(L.unit, 96); assert.equal(L.width, 288); assert.equal(L.originX, (390 - 288) / 2); const tall = Board.fromCells([c('a', 0, 0), c('b', 0, 1), c('c', 0, 2), c('d', 0, 3), c('e', 0, 4), c('f', 0, 5)]); const L2 = fitBoard(tall, 390, 300, { padding: 0, maxUnit: 200 }); assert.equal(L2.unit, 50); }); test('命中测试:格中心命中、缝与死区不命中、空洞不命中', () => { const b = Board.fromCells([c('a', 0, 0), c('B', 1, 0, 2, 2), c('h', 0, 2)]); const L = fitBoard(b, 300, 300, { padding: 0, maxUnit: 100 }); const ra = cellRect(b, 0, L); assert.equal(hitTest(b, L, ra.x + ra.w / 2, ra.y + ra.h / 2), 0); assert.equal(hitTest(b, L, L.originX + 2, L.originY + 2), -1, '角落死区'); assert.equal(hitTest(b, L, L.originX + 150, L.originY + 150), 1, '2×2 中心'); assert.equal(hitTest(b, L, L.originX + 50, L.originY + 150), -1, '空洞 (0,1)'); assert.equal(hitTest(b, L, -5, -5), -1); }); test('桥矩形横跨缝并落在共享边中点', () => { const b = Board.fromCells([c('a', 0, 0), c('B', 1, 0, 2, 2)]); const L = fitBoard(b, 300, 200, { padding: 0, maxUnit: 100 }); const br = bridgeRect(b, 0, 1, L)!; assert.ok(br); assert.ok(Math.abs(br.x + br.w / 2 - (L.originX + 100)) < 1e-6, '中心在 x=1 的边上'); assert.ok(Math.abs(br.y + br.h / 2 - (L.originY + 50)) < 1e-6, '共享边 y∈[0,1] 的中点'); assert.equal(bridgeRect(b, 1, 1, L), null); }); test('异形格绘制矩形:单位 + 接缝', () => { // L4 占 (1,1)(1,2)(1,3)(2,3):4 个单位矩形 + 3 条内部缝(两竖一横) // 补一个 (0,0) 的格钉住原点,免得 L 被平移 const b = Board.fromCells([ { id: 'L', x: 1, y: 1, w: 2, h: 3, shape: [[0, 0], [0, 1], [0, 2], [1, 2]] }, c('z', 0, 0), ]); const L = fitBoard(b, 300, 300, { padding: 0, maxUnit: 100 }); const { units, seams } = cellShapeRects(b, 0, L); assert.equal(units.length, 4); assert.equal(seams.length, 3); const half = L.gap / 2; // 横缝:(1,3)→(2,3),跨过 x=2 的竖缝,向两侧单位内各延长一个圆角半径(盖住圆角缺口) assert.deepEqual( seams.find((r) => r.w === L.gap + 2 * L.radius), { x: L.originX + 2 * L.unit - half - L.radius, y: L.originY + 3 * L.unit + half, w: L.gap + 2 * L.radius, h: L.unit - L.gap, }, ); // 竖缝:(1,1)→(1,2),跨过 y=2 的横缝,同样上下延长 assert.deepEqual( seams.find((r) => r.h === L.gap + 2 * L.radius && r.y === L.originY + 2 * L.unit - half - L.radius), { x: L.originX + L.unit + half, y: L.originY + 2 * L.unit - half - L.radius, w: L.unit - L.gap, h: L.gap + 2 * L.radius, }, ); // 单位矩形与 cellRect 对单个 1×1 的算法一致 const u0 = units[0]; assert.deepEqual(u0, { x: L.originX + L.unit + half, y: L.originY + L.unit + half, w: L.unit - L.gap, h: L.unit - L.gap }); });