SNAKE game v0.3: round screen, swipe control
- 16x16 grid, 320px board centered on round 466 screen, designWidth 466 - pure snake logic in src/common/snake.js (step/turn/rotateDir/spawnFood, TDD'd) - for-directive segment rendering, show-gated game-over overlay, 300ms tick - swipe direction input (crown rotation confirmed unsupported on Vela S4) - tests: snake logic + page wiring
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
// Page wiring test: drives the VM methods extracted from index.ux.
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const assert = require('node:assert/strict');
|
||||
const snake = require('../src/common/snake.js');
|
||||
|
||||
const ux = fs.readFileSync('src/pages/index/index.ux', 'utf8');
|
||||
const body = ux.match(/<script>([\s\S]*?)<\/script>/)[1]
|
||||
.replace(/import\s+snake\s+from\s+'[^']*';?/, '')
|
||||
.replace(/^const\s+\w+\s*=[^\n]*\n/gm, '')
|
||||
.replace('export default', '');
|
||||
|
||||
const sandbox = { snake, Date, Math, String, Number, console, setInterval: () => 0, clearInterval: () => {},
|
||||
CELL: 20, GRID: 16, OFFSET: 73 };
|
||||
vm.createContext(sandbox);
|
||||
const spec = vm.runInContext('(' + body + ')', sandbox);
|
||||
|
||||
function mk() {
|
||||
const o = Object.assign({}, spec, JSON.parse(JSON.stringify(spec.private)));
|
||||
o.$element = () => null;
|
||||
spec.onInit.call(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
let c = mk();
|
||||
assert.equal(c.model.length, 3, 'initialized to length 3');
|
||||
assert.equal(c.snake.length, 3, 'rendered 3 segments');
|
||||
assert.equal(c.snake[0].cls, 'seg head', 'head flagged');
|
||||
assert.equal(c.snake[1].cls, 'seg', 'body flagged');
|
||||
assert.equal(c.scoreText, 'SCORE 0');
|
||||
assert.equal(c.snake[0].left, (73 + 6 * 20 + 1) + 'px', 'head x mapped to px');
|
||||
|
||||
for (let i = 0; i < 5; i++) c.tick();
|
||||
assert.equal(c.model[0].x, 11, 'head moved right 5 steps from x=6');
|
||||
assert.equal(c.over, false);
|
||||
|
||||
c.onSwipe({ direction: 'down' });
|
||||
c.tick();
|
||||
assert.equal(c.dir.x, 0, 'dir x');
|
||||
assert.equal(c.dir.y, 1, 'direction turned down');
|
||||
assert.equal(c.model[0].y, 8, 'head moved down');
|
||||
|
||||
c.onSwipe({ direction: 'up' });
|
||||
c.tick();
|
||||
assert.equal(c.dir.x, 0, 'reversal keeps x');
|
||||
assert.equal(c.dir.y, 1, 'reversal blocked');
|
||||
|
||||
// run into the wall to trigger game over (GRID=16, last column x=15)
|
||||
c.model = [{ x: 15, y: 5 }, { x: 14, y: 5 }, { x: 13, y: 5 }];
|
||||
c.dir = { x: 1, y: 0 };
|
||||
c.pending = null;
|
||||
c.tick();
|
||||
assert.equal(c.over, true, 'game over on wall hit');
|
||||
|
||||
c.restart();
|
||||
assert.equal(c.over, false);
|
||||
assert.equal(c.model.length, 3);
|
||||
assert.equal(c.scoreText, 'SCORE 0');
|
||||
|
||||
// template uses documented directives/events, no transforms, no crown remnants
|
||||
assert.match(ux, /for="\{\{ seg in snake \}\}"/);
|
||||
assert.match(ux, /show="\{\{ over \}\}"/);
|
||||
assert.match(ux, /onswipe="onSwipe"/);
|
||||
assert.doesNotMatch(ux, /crownrotationchanged/i);
|
||||
assert.doesNotMatch(ux, /transform\s*:/);
|
||||
|
||||
console.log('PAGE WIRING PASS (movement, turn, reversal, game over, restart, directives)');
|
||||
Reference in New Issue
Block a user