- 玩法一 数字一笔:规则库、求解器、26 关、竖屏触摸 Web 原型、测试 - 玩法二 连连气球:连消畅玩版核心、Web 原型、Cocos 球槽组件与导出脚本 - 玩法三 六边一笔:规则库、求解器、10 关 + 转墙实验包、Web 原型、测试 - 产品文档、UI 设计规范、各玩法 GDD / HANDOFF - 文档移除全部外部参考作引用;删除玩法二 v0 旧稿
77 lines
3.4 KiB
TypeScript
77 lines
3.4 KiB
TypeScript
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 });
|
||
});
|