A classical AI planner in Common Lisp — PDDL-flavored, Lisp-powered, and REPL-friendly.
Wouldwork is a classical planning / search system: you describe a world (objects, relations, actions, constraints, goals…), and it searches for plans that reach your goal. It’s “old-school AI” in the best way — but with a modern, developer-friendly workflow: stage → solve → tweak → refresh.
If you like planners, constraint-y domains, debugging search, or just want to feel the joy of a Lisp REPL doing AI… you’re home.
Wouldwork supports a bunch of “classic planner” features, but with a Lisp-native problem language:
- Objects, types, relations (dynamic + static)
- Actions with preconditions/effects
- Goals, constraints, and derived relations
- Functions (including on-the-fly and even recursive computations)
- Exogenous events (things that happen independently of the agent)
- Temporal planning (action schedules / timestamps)
- Search control: depth cutoffs, tree vs graph search, randomization, branching
- Solution modes: first solution, shortest plan, min-time, min/max objective, enumerate all, …
- Optional parallel search (SBCL is the sweet spot)
- Diagnostics & debugging hooks (including step-through search)
For a gentle narrative intro, see the Medium article: https://medium.com/@davypough/traditional-ai-problem-solving-with-wouldwork-fcb0c4a71226
Wouldwork is available via Quicklisp, Ultralisp, and GitHub / Roswell.
(ql:quickload :wouldwork)
(in-package :ww)
(test) ; sanity check
(help) ; see available commands(ql-dist:install-dist "http://dist.ultralisp.org/" :prompt nil)
(ql:quickload :wouldwork)
(in-package :ww)
(test) ; sanity check
(help) ; see available commandsInstall Roswell (examples):
# macOS
brew install roswell
# Debian/Ubuntu
sudo apt update && sudo apt install libcurl4-openssl-dev automake
# (or use your distro’s roswell package)Install SBCL + Quicklisp:
ros install sbcl
ros install quicklisp(Optional but recommended: nicer REPL history/editing)
# macOS
brew install rlwrap
# Debian/Ubuntu
sudo apt install rlwrapInstall Wouldwork from GitHub:
ros install davypough/wouldworkRun SBCL and load Wouldwork:
rlwrap ros runInside SBCL:
(ql:quickload :wouldwork)
(in-package :ww)
(test) ; sanity checks
(help) ; see available commandsStart SBCL, then:
(ql:quickload :wouldwork)
(in-package :ww)
(help) ; shows the REPL command menu
(test) ; runs a bunch of example problems
(run "blocks3") ; stage + solve a sample domainThat’s it. You’ll see a plan printed, plus search diagnostics depending on your settings.
Wouldwork is designed for iterative development: write a problem file, run it, tweak it, re-run it.
(stage <problem-name>) ; load a problem spec (compile + prepare), don't solve yet
(solve) ; solve the currently staged problem
(refresh) ; recompile/reload the current problem after edits
(run <problem-name>) ; stage + solve in one goUseful helpers:
(list-all-problems) ; (probs) in the REPL help text
(display-current-parameters) ; (params) in the REPL help text
(test) ; solve the included test problemsIf anything gets into a weird state:
(ww-reset) ; reinitialize Wouldwork after an error (and continue)Wouldwork settings are “earmuffed” globals, and you change them with ww-set:
(ww-set *solution-type* 'first) ; default
(ww-set *solution-type* 'min-length)
(ww-set *solution-type* 'min-time)
(ww-set *solution-type* 'min-value)
(ww-set *solution-type* 'max-value)
(ww-set *solution-type* 'every)
(ww-set *tree-or-graph* 'graph) ; or 'tree
(ww-set *depth-cutoff* 0) ; 0 = no limit (go as deep as needed)
(ww-set *progress-reporting-interval* 100000)
(ww-set *randomize-search* t) ; random DFS
(ww-set *branch* -1) ; -1 = all branchesDebugging knobs:
(ww-set *debug* 5) ; step through search one expansion at a time
;; *debug* = 0..4 gives increasing levels of debug outputPractical tip: set most domain-specific parameters in your problem file,
then useww-setat the REPL while experimenting. It’s a nice rhythm.
Wouldwork keeps your working context in a small file (vals.lisp) inside the system directory.
That means when you restart your Lisp image, it can restore:
- the last problem you staged
- your parameter choices (algorithm, search settings, debug level, …)
This is deliberate: planning is exploratory, and retyping 12 parameters every session is pain.
There’s also a small but important design choice: when you call refresh, Wouldwork avoids clobbering the values you just set at the REPL (so your live tuning stays live).
If you ever want to wipe state clean and go back to defaults, use the reset command from the REPL.
Wouldwork can search for different notions of “best,” depending on what you’re doing:
first— find the first goal-satisfying plan encountered (fastest feedback)min-length— shortest number of actionsmin-time— least total time (when actions have durations)min-value/max-value— optimize an objective function (even without a goal)every— enumerate solutions (careful: state spaces explode)
Worth knowing:
- Tree search vs graph search matters a lot for
every. - With graph search, repeated states are pruned (so alternative paths to the same state may be skipped).
- There’s a hybrid mode when
every+ graph search + a positive depth cutoff are combined, to enumerate distinct action sequences up to that bound while staying memory-conscious.
After a run:
*solutions*contains all found solution paths.*unique-solutions*contains one solution path per unique goal state.
When you’re writing action rules, the hardest part is usually not “search” — it’s domain consistency.
Wouldwork gives you a very practical tool for that: invariants, both local (inside updates) and global (checked across generated states). When an invariant fails, Wouldwork can drop you into a troubleshooting environment that helps you see what happened and why.
The user manual has several good examples (including invariant checks like “this list must be a set” and “cargo cannot be both held and located”).
During development you can also sprinkle in diagnostics:
(ut::prt some-expression other-expression)…and later remove them once the domain is stable.
Wouldwork’s problem files are PDDL-inspired, but they’re not trying to be PDDL.
The point is: you get a familiar planning structure plus the ability to use Lisp where it helps.
Example (high-level sketch):
define-types— objects and typesdefine-dynamic-relations/define-static-relationsdefine-actions— actions with preconditions/effectsdefine-invariant— global safety conditionsdefine-query/ functions — computed relations / heuristics / bounds
And yes: you can include arbitrary Common Lisp where it makes the domain clearer or faster.
Wouldwork was originally designed to take advantage of SBCL performance features and can optionally use parallelism there. It also runs on CCL via more generic libraries, but expect large problems to be noticeably slower.
If you run into memory limits for bigger searches, give SBCL more heap:
sbcl --dynamic-space-size 10000(Scale as needed: 2048, 4096, 8192, 24000, … depending on your machine.)
- User Manual:
Wouldwork User Manual(26.7).docx(in this repo) - Parameter loading notes (for the curious):
parameter loading sequence.txt - Medium intro: https://medium.com/@davypough/traditional-ai-problem-solving-with-wouldwork-fcb0c4a71226
If you like “plans as timelines” and multi-agent-ish traces, check out the recorder / windtunnel artifacts in this repo (there’s a full integrated sequence table showing how a solution can be post-processed into recording/playback phases).
For questions, feedback, or domain-wrangling war stories, contact the author:
Dave Brown — davypough@gmail.com
For questions or suggestions for improvements of the user interface, you can also alternatively poke:
Gwang-Jin Kim - gwang.jin.kim.phd@gmail.com
- If you have a question, idea, or bug report: open a GitHub issue. Please search first (including closed issues) to avoid duplicates. Tip: use GitHub search filters like:
is:issue is:closed <your-keywords>
- Collaborators are welcome. Reasonable, well-scoped suggestions and PRs are appreciated — especially when they come with a small repro, a test, or a concrete example domain.
(See the LICENSE file.)