- 玩法一 数字一笔:规则库、求解器、26 关、竖屏触摸 Web 原型、测试 - 玩法二 连连气球:连消畅玩版核心、Web 原型、Cocos 球槽组件与导出脚本 - 玩法三 六边一笔:规则库、求解器、10 关 + 转墙实验包、Web 原型、测试 - 产品文档、UI 设计规范、各玩法 GDD / HANDOFF - 文档移除全部外部参考作引用;删除玩法二 v0 旧稿
88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
import assert from 'node:assert/strict';
|
|
import { test } from 'node:test';
|
|
import { HexBoard } from '../src/board';
|
|
import { Game } from '../src/game';
|
|
import { PointerInput } from '../src/input';
|
|
import type { HexCellDef } from '../src/types';
|
|
|
|
const c = (id: string, q: number, r: number, clue?: number): HexCellDef => ({ id, q, r, clue });
|
|
const level13 = () => HexBoard.fromCells([c('a', 0, 0, 1), c('b', 1, 0), c('c', 0, 1), c('d', 1, 1, 4)], [['a', 'b']]);
|
|
|
|
function setup() {
|
|
const b = level13();
|
|
const g = new Game(b);
|
|
const input = new PointerInput(g);
|
|
const id = (s: string) => b.cellById(s).index;
|
|
return { b, g, input, id };
|
|
}
|
|
|
|
test('按下起点、滑到邻格追加、滑回撤销、松手保留', () => {
|
|
const { g, input, id } = setup();
|
|
assert.equal(input.down(id('a'))[0].type, 'step');
|
|
assert.ok(input.isDrawing);
|
|
assert.equal(input.move(id('c'))[0].type, 'step');
|
|
assert.deepEqual(input.move(id('c')), [], '同一格不重复触发');
|
|
assert.equal(input.move(id('a'))[0].type, 'unstep');
|
|
assert.equal(g.path.length, 1);
|
|
input.move(id('c'));
|
|
input.up();
|
|
assert.equal(g.status, 'idle');
|
|
assert.equal(g.path.length, 2);
|
|
});
|
|
|
|
test('滑向有墙的邻格 → wallHit 一次,路径不变', () => {
|
|
const { g, input, id } = setup();
|
|
input.down(id('a'));
|
|
const ev = input.move(id('b'));
|
|
assert.equal(ev.length, 1);
|
|
assert.equal(ev[0].type, 'wallHit');
|
|
assert.deepEqual(input.move(id('b')), [], '停在同一格不重复报');
|
|
assert.equal(g.path.length, 1);
|
|
// 指针逻辑上仍在末端:继续滑到 c 可追加
|
|
assert.equal(input.move(id('c'))[0].type, 'step');
|
|
});
|
|
|
|
test('点末端继续、点前一格撤销、点旁边空格追加、点墙外邻格报撞墙', () => {
|
|
const { g, input, id } = setup();
|
|
input.down(id('a'));
|
|
input.move(id('c'));
|
|
input.up();
|
|
assert.equal(input.down(id('c')).length, 0);
|
|
assert.ok(input.isDrawing);
|
|
input.up();
|
|
assert.equal(input.down(id('a'))[0].type, 'unstep');
|
|
assert.equal(g.path.length, 1);
|
|
input.up();
|
|
assert.equal(input.down(id('c'))[0].type, 'step', '点着走');
|
|
input.up();
|
|
// 路径 a,c;c 的邻居 b 无墙 → 追加;再从 b 去 a 已走 → 忽略
|
|
assert.equal(input.down(id('b'))[0].type, 'step');
|
|
input.up();
|
|
// 从 a 起手时点 b:撞墙反馈
|
|
input.cancel();
|
|
g.apply({ type: 'reset' });
|
|
input.down(id('a'));
|
|
input.up();
|
|
assert.equal(input.down(id('b'))[0].type, 'wallHit');
|
|
});
|
|
|
|
test('非法起手与空白按下', () => {
|
|
const { input, id } = setup();
|
|
assert.equal(input.down(id('b'))[0].type, 'rejectStart');
|
|
assert.ok(!input.isDrawing);
|
|
assert.deepEqual(input.down(-1), []);
|
|
assert.deepEqual(input.move(id('a')), [], '没在画,move 无效');
|
|
});
|
|
|
|
test('过关后手势失效', () => {
|
|
const { g, input, id } = setup();
|
|
input.down(id('a'));
|
|
input.move(id('c'));
|
|
input.move(id('b'));
|
|
const ev = input.move(id('d'));
|
|
assert.ok(ev.some((e) => e.type === 'cleared'));
|
|
assert.ok(!input.isDrawing);
|
|
assert.equal(g.isCleared, true);
|
|
assert.deepEqual(input.down(id('d')), []);
|
|
});
|