#!/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 ] [--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();