1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! A PDDL planner library built around the [`pddl`](https://crates.io/crates/pddl) crate.
//!
//! `miniplan` is primarily a **planner** — it takes a parsed PDDL domain and problem,
//! grounds them into a state-space representation, and solves them using a collection
//! of search algorithms (planners) and heuristic functions. PDDL parsing and loading
//! utilities are re-exported from the `pddl` crate for convenience.
//!
//! # Architecture
//!
//! 1. **Parse** — use `pddl::Parser` (re-exported via [`pddl_io`]) to load PDDL.
//! 2. **Ground** — call [`ground::ground`] to convert the parsed domain and problem
//! into a [`task::Task`] with grounded facts and operators.
//! 3. **Solve** — use the [`search::Solver`] and [`search::Registry`] to pick a
//! planner and search for a plan.
//!
//! # Modules
//!
//! - [`search`] — search algorithms (BFS, A*, GBFS, bidirectional variants)
//! and the planner registry.
//! - [`heuristic`] — heuristic functions (h^add, h^max, h^FF, etc.).
//! - [`ground`] — grounding functions that convert PDDL to a state-space representation.
//! - [`task`] — grounded task representation (facts, operators, states).
//! - [`plan`] — plan representation and formatting.
//! - [`pddl_io`] — convenience wrappers around `pddl::Parser` for loading PDDL
//! from strings, files, or multiple files.
//! - [`error`] — error types for parsing, grounding, and search.
//!
//! # Getting started
//!
//! The simplest way to solve a planning task is to use the [`Solver`](search::Solver)
//! with the built-in registry:
//!
//! ```
//! use miniplan::search::{Solver, PlannerChoice, PlannerKind, SearchLimits};
//! use miniplan::pddl_io::{load_domain_str, load_problem_str};
//! use miniplan::ground::ground;
//!
//! const DOMAIN: &str = r#"
//! (define (domain test)
//! (:requirements :strips)
//! (:predicates (a) (b))
//! (:action go
//! :parameters ()
//! :precondition (a)
//! :effect (and (b) (not (a))))
//! )
//! "#;
//!
//! const PROBLEM: &str = r#"
//! (define (problem test-1)
//! (:domain test)
//! (:init (a))
//! (:goal (b)))
//! "#;
//!
//! let domain = load_domain_str(DOMAIN).expect("domain parses");
//! let problem = load_problem_str(PROBLEM).expect("problem parses");
//! let task = ground(&domain, &problem).expect("grounding succeeds");
//!
//! let solver = Solver::new();
//! let choice = PlannerChoice::new(PlannerKind::Bfs);
//! let limits = SearchLimits::default();
//!
//! match solver.solve_task(&task, &choice, &limits).expect("solve returns") {
//! miniplan::search::SearchOutcome::Plan(plan, _stats) => {
//! assert_eq!(plan.len(), 1);
//! }
//! _ => panic!("expected a plan"),
//! }
//! ```