feat: undo in Module 9 and stackAt from Module 8; Lesson 8 quote matches the reference
Baba.undo() restores the level before the last step or move (board snapshot: objects in age order with ids, the id counter, and won; rules recompiled). place clears the history. SpellGrid's undo throws/panics 'not supported'. stackAt / stack_at lists a tile's kinds oldest first, on Baba and SpellGrid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
21fb40c6f5
commit
820b378929
27 files changed
+530
-30
No files matched your search
@@ -7,8 +7,12 @@
|
||||
//
|
||||
// A turn (Module 9) runs the phases of `TURN` in order: movement, parse,
|
||||
// conversion, parse, content's hazards, the win check, parse.
|
||||
//
|
||||
// Undo (Module 9): before each move or turn the level saves the board (every
|
||||
// object's id, kind and position, oldest first, and the id counter) and `won`.
|
||||
// The rules are not saved: undo restores the board and reparses.
|
||||
|
||||
import { Board, Kind, type LevelDef, type ObjectId } from "./grid";
|
||||
import { Board, Kind, type BoardState, type LevelDef, type ObjectId } from "./grid";
|
||||
import { delta, Planner, type Dir, type Plan, type Response } from "./movement";
|
||||
import { isConversion, isIdentity, parse, type Rule } from "./parse";
|
||||
import { resolve } from "./resolve";
|
||||
@@ -31,6 +35,8 @@ export class Level {
|
||||
private compiled: Rule[] = [];
|
||||
private active: Rule[] = [];
|
||||
private hasWon = false;
|
||||
/** What `undo` puts back: one entry per move or turn, the latest last (Module 9). */
|
||||
private history: { board: BoardState; won: boolean }[] = [];
|
||||
|
||||
/**
|
||||
* A level from its definition, the words content provides, and content's
|
||||
@@ -50,6 +56,7 @@ export class Level {
|
||||
|
||||
place(kind: string, x: number, y: number): ObjectId {
|
||||
const id = this.board.place(Kind.parse(kind), x, y);
|
||||
this.history = [];
|
||||
this.reparse();
|
||||
return id;
|
||||
}
|
||||
@@ -110,12 +117,14 @@ export class Level {
|
||||
* only: no conversion, hazard or win.
|
||||
*/
|
||||
moveYou(dir: Dir): void {
|
||||
this.save();
|
||||
this.movement(dir);
|
||||
this.reparse();
|
||||
}
|
||||
|
||||
/** Play one turn: the phases of `TURN`, in order. */
|
||||
step(dir: Dir): void {
|
||||
this.save();
|
||||
for (const phase of TURN) {
|
||||
switch (phase) {
|
||||
case "movement":
|
||||
@@ -137,6 +146,23 @@ export class Level {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the level back as it was before the last `moveYou` or `step` (Module 9),
|
||||
* and reparse. False, and no change, if there is none; `place` forgets them all.
|
||||
*/
|
||||
undo(): boolean {
|
||||
const last = this.history.pop();
|
||||
if (last === undefined) return false;
|
||||
this.board.restore(last.board);
|
||||
this.hasWon = last.won;
|
||||
this.reparse();
|
||||
return true;
|
||||
}
|
||||
|
||||
private save(): void {
|
||||
this.history.push({ board: this.board.snapshot(), won: this.hasWon });
|
||||
}
|
||||
|
||||
/**
|
||||
* The `YOU` objects move one at a time, the one furthest ahead in `dir` first
|
||||
* (oldest first on a tie); each one's move is planned in full, then committed,
|
||||
|
||||
Reference in new issue
Block a user