quest'n'click 4.5.0 · r1

Built for quest'n'click 4.5.0, revision r1. Whether it is published, and which one is current, is recorded at publication.json.

Solver options

Français · Type: reference · Audience: author · Context: engine repository, created project · Status: ready · Applies from: 4.1.13

Every option of solve, the solver's command, and of the tools around it: what it does, its default, and since when. What the answers mean (a winning path, a proof, truncated) is in What the solver proves.

Where to run it

The tools marked engine repository below (prove:matrix, audit:corpus, bench, replay, page:*) have no quest-n-click subcommand: a created project does not have them.

# in the engine repository
npm run solve -- --prove --profile

# in a created project
npx quest-n-click solve --prove --profile

All the options at a glance

OptionWhat it doesDefaultSince
(none)A witness: one path from "New game" to the ending
--proveEvery reachable state; each one that lost the ending is a softlockwitness
--from=<checkpoint>Start from a checkpoint of game.checkpoints instead of "New game""New game"
--chaptersOne search per checkpoint that declares goals, then from the last one to the ending
--goal=100%The goal is every objective that is not optional (GameDef.objectives), all at once, instead of the endingthe ending4.1.12
--max=<n>State budget20000
--time=<s>Time budget, in secondsnone3.5
--mem=<MB>Heap budget, in MB (Node only, read every 256 expansions)none4.1.13
--checkpoint=<file>Write the search down as it goesnone4.1.13
--checkpoint-every=<s>How often, in seconds3004.1.13
--resumeTake the search up from --checkpoint's file4.1.13
--workers=<n>|autoExpand the frontier on worker threadsnone (one thread)3.5
--batch=<n>Nodes taken from the frontier at once, with workers643.5
--profileWhat the search cost and what the states are made of
--jsonOne line of JSON on stdout, no other text
--no-cacheDo not read or write the proof cachecache on
--por=sleep|stubbornPartial-order reduction, witness onlyoff
--dominanceWitness dominance, witness onlyoff3.5
--ownership=offNo canonical owner in a proofon in a proof3.5
--symmetryFold symmetric items in a proofoff4.1.13
--representation=objectsThe 4.1.8 storage of statescompact4.1.13
--audit-abstractionsThe proof with the abstractions against the explicit search3.3.1

An unknown option is ignored: the command reads only the options it knows. --goal= with anything other than 100% is refused (exit 2).

--prove, --from, --chapters, --goal=100%

# in the engine repository
npm run solve -- --from=market --max=50000
npm run solve -- --chapters
npm run solve -- --prove --chapters
npm run solve -- --goal=100%

Long proofs: budgets, checkpoints, memory

--max (states, 20 000 by default), --time (seconds) and --mem (MB of heap) bound a search. Each one stops it as truncated, exit 2, with the budget that stopped it named in the sentence ("raise --max", "raise --time", "raise --mem, or resume from the checkpoint"). A truncated search is never a proof.

--checkpoint=<file> writes the search down every --checkpoint-every seconds (300 by default) and when a budget stops it: the store, the frontier in its order, the counters. --resume takes it up from that file, to the same verdict and the same witness as a search that never stopped (tests/checkpoint.test.ts kills a real process with SIGKILL at 30 % and resumes it). A snapshot belongs to one search: the game and the options that change what is found are its fingerprint, and another search ignores it (the profile says why). A finished search removes its file. The checkpoint covers the whole game's search; --chapters runs several searches and does not use it. It needs the compact representation.

# in the engine repository: a long proof, written down, then resumed with a bigger budget
npm run solve -- --prove --checkpoint=p.ckpt --checkpoint-every=300 --mem=3500
npm run solve -- --prove --checkpoint=p.ckpt --resume --max=200000

Guarantee · checkpoint-resume-same-proof · referenced · executed on aff6caab7514 · scope self-checked by the author (AI), not reviewed A search cut by its budget is written down, and a bigger budget takes it up to the same proof as a search that never stopped.

Workers

--workers=N expands the frontier a batch at a time (--batch, 64 by default) on worker threads; auto takes the cores but one, at most 8. The merge stays in the batch's order, so the result depends on the batch, never on the number of workers (tests/workers.test.ts, tests/partition.test.ts). Since 4.1.13 the search shares its visited table with the workers (the 64-bit hash of every stored state): a worker sends a state the search has already stored back without its engine state. The exact keys stay the authority: a worker's "already stored" that the search does not confirm is expanded again in the search's thread. Each node goes first to the worker its room hashes to, and an idle worker steals from the longest queue. A worker that stops mid-search leaves the pool, with the same proof, and the profile says so.

Workers speed an exploration up; they do not answer an explosion of states. Measured in 3.5 (5 October 2026, Benchmarks): a large proof went about 2.5 times faster with 4 workers. They do not combine with a partial-order reduction.

# in the engine repository
npm run solve -- --prove --workers=4 --batch=64 --time=60

Reading the profile

npm run solve -- --profile (the Studio's Check tab, "Solver health"; the solve MCP tool with profile: true) says why a search is slow or big. With --prove, since 4.1.13, it also gives the explosion profile. Here is an excerpt on the demo game (engine repository, main, 23 October 2026):

SOLVER PROFILE
  states explored       3480
  engine runs           56588  (107244 tries changed nothing, 79588 of them not run: the no-op memo knew; 25455 landed on a known state)
  actions not run       713640  (no rule could answer them)
  actions per state     44.7 on average, 51 at most (market, 3 items in the bag: 1 Give, 2 Map, 3 Switch changed something)

Abstractions (what each one did, or why it is off):
  canonical character   3048 switches folded, 0 kept explicit
  mobility regions      off (no move of this game can be silent)
  witness dominance     off (off in proofs: it changes softlock verdicts on generated games (tests/dominance.test.ts))
  symmetric items       off (not asked for (--symmetry))
  representation        compact, 427 KB of tables

Explosion: what multiplies the 3480 states measured (states that would merge without it)
  positions        2904  (18 distinct values over 3 dimension(s))
  inventories      3042  (76 distinct values over 2 dimension(s))
  flags            3000  (78 distinct values over 13 dimension(s))
…
What splits the states (states that would merge without it):
    2940  pos biscuit  (44 values)
    2400  pos hero  (30 values)
…
Use / give combinations per room (over its expansions):
  house: 84096 candidates, 465 answered by a rule, 81021 could only fall back (96% useless)
Independent dimensions (their combinations multiply the states; a checkpoint between them would cut it):
  ⚠ nth house.take-teacup.nth, flag lou_has_key, prop house.armchair: 8 of 8 combinations seen
Monotonic (never lost once gained): 10 flags, 1 items (shell_phone)

How to read it:

The heat map on the puzzle graph (Check tab) shows the same numbers on the rules and rooms; "why is this live?" on a card explains why the solver keeps a thing in its state. docs/dev/PROOF-PROFILE.md is this profile for each instance of the proof matrix.

--json and the output

Without --json, the output is for people and other scripts read it as is: the verdict line, the path found (or the path to the last state explored), the rooms reached, the map places unlocked, the flags set, the items obtained but never used, the items never obtained, up to five dead ends, up to ten softlock causes, the invariants broken and the engine errors.

--json prints one line of JSON and nothing else: status, exit, headline, mode, cached, finished, states, truncated, path (labelled for people), steps (the session entries npm run e2e replays), softlockCount, softlockCauses, roomsReached, unlockedReached, flagsReached, itemsNeverUsed, unusedItems, softlocks, assumptions, errors, broken, profile. With --prove --chapters: status, exit, headline, ms and chapters.

The proof cache: --no-cache, PROOF_CACHE, PROOF_CACHE_DIR

A solver run depends only on the engine's code, the game (content, layouts, custom commands, and the sources of the game folder) and the options. solve (every mode), --chapters and lint keep each result in .cache/proofs/ and give it back when none of that changed, saying so: (from the proof cache, key …), and cached in --json. The build, verify:game, prove:game and release-check ask the same questions several times; on the demo a warm proof took 0.17 s instead of 2.4 s cold when the cache arrived (3.3.0, CHANGELOG.md).

--por, --dominance, --ownership=off, --symmetry, --representation=objects

What each of these changes in a verdict is in What the solver proves.

--audit-abstractions and audit:corpus

# in the engine repository, or npx quest-n-click solve --audit-abstractions in a created project
npm run solve -- --audit-abstractions

The proof with the abstractions (every no-op memo hit run anyway and compared) against the explicit search (canonical character, mobility regions, canonical owner and memo off). Exit 0 same, 1 diverged, 2 the explicit search did not fit --max (the verdicts were not compared). npm run release-check runs it.

# in the engine repository only
npm run audit:corpus -- --seeds=500                              # every night in CI: exit 1 on a divergence
npm run audit:corpus -- --from=126 --seeds=125 --json=s.json     # one shard: tried, compared, partial, diverged per kind
npm run audit:corpus -- --shard=1/4 --total=500 --json=s1.json   # shard 1 of 4 over seeds 1-500, split evenly
npm run audit:corpus -- --merge s0.json s1.json --total=500      # shards added up; fails on a seed missed or run twice

audit:corpus runs the same audit on random games of three kinds (plain, with free items, with three characters and free items). partial (the explicit search hit --max) is not a failure: those verdicts are just not compared, and the counts say so.

prove:matrix and bench (engine repository)

# in the engine repository only
npm run prove:matrix -- --only=c11,o21 --time=600 --mem=4096 --json=m.json --profile=docs/dev/PROOF-PROFILE.md
npm run bench -- --matrix --eras --max=20000
npm run bench -- --rooms=40 --prove --v3

The measures are in Benchmarks.

The solver's heavy tests (engine repository)

npm test runs the Node tests of the engine, the tools and the selected game, without the CPU-bound solver tests. npm run test:heavy runs those (the abstraction audits, the canonical owner, the memo and ownership proofs, the reference chapter's proof, the solver oracle, dominance, partitions, the checkpoint matrix): every night, minutes each. See Which tests run when.


Edit the source of this page · Report an error