feat: Stress it beat for Modules 6-10, Module 11 (Break your own engine), level solver tool
- lessons 8 and 9 updated for the once-per-move rule, per-object negated conversion and the new ordering tests - tools/solve-spellgrid.mjs: breadth-first search over a SpellGrid level with the TypeScript reference; --never-rule and --naive - README, harness.md and the mentor skill know the new beat and module Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
1 parent
e907f17937
commit
4cb0f3a61f
13 files changed
+771
-19
No files matched your search
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env node
|
||||
// Search a SpellGrid level (Module 10) for solutions, by brute force, with the course's
|
||||
// TypeScript reference engine. Used in Module 11 to check that a level has the solution
|
||||
// its designer meant and no other.
|
||||
//
|
||||
// node tools/solve-spellgrid.mjs [level] [--dir <dir>] [--max N] [--naive] [--never-rule "X IS Y"] [--all]
|
||||
// npm run solve:spellgrid -- l1 --never-rule "DEATHRATTLE IS DOUBLE"
|
||||
//
|
||||
// Stages the Module 10 TypeScript reference with its cards and levels, as play-spellgrid
|
||||
// does, copies tools/solver/solve.ts beside it and runs it there. `level` is a level of
|
||||
// course/10-spellgrid/levels by name or prefix (`l1`), or a path to a level file; default `l1`.
|
||||
// The other options are the solver's: see tools/solver/solve.ts.
|
||||
import { cpSync, existsSync, readdirSync } from "node:fs";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { basename, dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { fail, isMain } from "./lib.mjs";
|
||||
import { resolveLevel, stagePlay } from "./play-spellgrid.mjs";
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const WITH_VALUE = ["--max", "--never-rule"];
|
||||
const SWITCHES = ["--naive", "--all"];
|
||||
|
||||
export function parseArgs(argv) {
|
||||
const opts = { level: "l1", dir: null, solver: [] };
|
||||
const rest = [];
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
if (argv[i] === "--dir") opts.dir = argv[++i];
|
||||
else if (WITH_VALUE.includes(argv[i])) opts.solver.push(argv[i], argv[++i]);
|
||||
else if (SWITCHES.includes(argv[i])) opts.solver.push(argv[i]);
|
||||
else rest.push(argv[i]);
|
||||
}
|
||||
[opts.level = "l1"] = rest;
|
||||
return opts;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const opts = parseArgs(process.argv.slice(2));
|
||||
const dir = resolve(stagePlay("ts", opts.dir));
|
||||
const level = resolveLevel(join(dir, "levels"), opts.level);
|
||||
if (!level) {
|
||||
const names = readdirSync(join(dir, "levels")).filter((f) => f.endsWith(".json")).map((f) => basename(f, ".json"));
|
||||
fail(`no level "${opts.level}". Levels: ${names.join(", ")}`);
|
||||
}
|
||||
cpSync(join(here, "solver", "solve.ts"), join(dir, "solve.ts"));
|
||||
if (!existsSync(join(dir, "node_modules"))) {
|
||||
console.error(`Installing the TypeScript reference's dependencies in ${dir} ...`);
|
||||
const r = spawnSync("npm", ["install", "--no-audit", "--no-fund", "--loglevel=error"], { cwd: dir, stdio: ["ignore", "inherit", "inherit"] });
|
||||
if (r.status !== 0) fail("npm install failed");
|
||||
}
|
||||
const r = spawnSync("npx", ["tsx", "solve.ts", level.path, ...opts.solver], { cwd: dir, stdio: "inherit" });
|
||||
process.exit(r.status ?? 1);
|
||||
}
|
||||
|
||||
if (isMain(import.meta.url)) main();
|
||||
Reference in new issue
Block a user