Why Generic Iterative Deepening on Search Trees Matters
Suppose a puzzle solver can generate legal moves cheaply, but you do not know whether the nearest solution takes three moves or thirty. Breadth-first search stores a growing frontier; ordinary depth-first search may follow a long branch before trying a one-move answer. Iterative deepening depth-first search (IDDFS) runs depth-limited DFS repeatedly with limits $0,1,2,\ldots$, trading repeated work near the root for a small active search path.
The result is a path to a goal with the fewest edges, provided the state model is complete, branching is finite, and the search reaches the necessary limit without exhausting its resources. For equal-cost moves this also minimizes cost. With unequal move costs, the shallowest path can be expensive; depth alone does not optimize weight.
The recursion and choose/recurse/undo pattern from the prerequisites stays intact. The new ideas are a remaining-depth argument, a distinction between an exhausted branch and a cutoff branch, and an outer loop that starts each pass from a clean root. This lesson makes those decisions observable through paths, return states, and counters. The main benefit arises in large implicit search trees, where successors are generated on demand; a small explicit graph is used here so every step is reproducible.
Find an unknown-depth goal without keeping a breadth-first frontier: search to depth $0$, then $1$, then $2$, and continue only when the previous pass was cut off. Keep one active path, restore it after each child, and stop at the first successful depth.
Core Concepts and Terminology
A state describes everything that determines legal successors and whether the goal holds. A search-tree node is a particular path to a state: different paths may end at the same state. IDDFS revisits those occurrences intentionally. The root has depth $0$; a path containing $k+1$ states has depth $k$.
For a pass with limit $L$, a call at depth $k$ receives remaining depth $r=L-k$. A child is admitted only if $r\gt0$ and it is not already on the current path. In an actual tree, cycle checks are unnecessary. Our graph adapter adds current-path cycle pruning so finite cyclic graphs also terminate once all useful simple paths are exhausted. This is a legality prune; the depth boundary is a temporary resource prune.
The inputs are a start state, a deterministic ordered successor function, a goal predicate, a maximum depth, and a total node-admission budget. The output reports a found path, proven exhaustion, or an inconclusive resource limit. The diagram uses the worked tree with goal $7$ and limit $2$: a leaf at the boundary is different from a node whose child was excluded by the boundary.
| Term | Example | Role |
|---|---|---|
| State versus occurrence | State $2$ can be reached along $[0,1,2]$ or $[0,2]$ | Remaining depth depends on the occurrence, not just the state ID |
| Depth limit $L$ | At limit $2$, at most two edges are traversed from the root | Bounds one DFS pass |
| Remaining depth $r$ | At state $5$ of depth $2$, $r=0$ when $L=2$ | Decreases by one per recursive child |
FOUND | Goal $7$ is admitted at depth $3$ | Copies a witness and stops the pass |
CUTOFF | Non-goal $5$ at $r=0$ has admissible child $7$ | More depth might help; propagate upward |
FAILURE | Non-goal leaf $4$ has no admissible successor | This subtree is exhausted under the current path |
ABORT | A new admission would exceed the node budget | Stops the whole search with unknown outcome |
| Active path and marks | Path $[0,2,5]$ marks exactly $0,2,5$ | Rejects cycles and supports exact undo |
| Transposition | Two different prefixes reach state $2$ | Must not be suppressed by a naive pass-wide visited set |
| Counters | nodes, edges, cutoffs, cycles, peak | Separate admitted calls, tested adjacencies, and active depth |
How Generic Iterative Deepening on Search Trees Works, Step by Step
- Validate the graph and search limits. Clear the previous answer, path, marks, counters, and pass history. The budget counts all passes together.
- For $L=0,1,\ldots,D$, push and mark the root, then call the depth-limited search with $r=L$.
- On entry, check the global node budget. If admission is refused, return
ABORTwithout counting a node. Otherwise incrementnodes, updatepeak, and test the goal before testing the depth boundary. This finds both a root goal and a goal exactly at the limit. - If the current non-goal has $r=0$, inspect successors only until finding one not already on the active path. Such a successor proves
CUTOFF; do not enter it. If every successor is absent or cycle-blocked, returnFAILURE. Each tested adjacency increasesedges, and each blocked one increasescycles. - With $r\gt0$, inspect children in their stored order. For each allowed child, append and mark it, recurse with $r-1$, then unmark and pop it before handling the returned code. Copy the witness inside the goal call so undo cannot erase it.
- Propagate
FOUNDorABORTimmediately after undo. Otherwise remember whether any child returnedCUTOFF; returnCUTOFFif so, orFAILUREwhen all children failed. A single exhausted child does not establish exhaustion of its siblings. - The driver removes the root, verifies complete restoration, and records this pass's counter deltas.
FOUNDbecomes a successful result; rootFAILUREbecomesNO_SOLUTION;ABORTbecomesNODE_LIMIT. Only rootCUTOFFstarts another pass, unless the depth cap has been reached, in which case reportDEPTH_LIMIT.
This lesson uses an exact cutoff probe at the boundary. A simpler version may conservatively return cutoff for every non-goal boundary node and need an extra pass to establish exhaustion. Both conventions can find shallowest goals, but their cutoff counts and stopping passes differ. Use one convention consistently.
flowchart TD
A["Driver: initialize limit to zero"] --> B["Push root and call DLS"]
B --> C["Undo root and record pass"]
C --> D{"Root outcome?"}
D -->|FOUND| E["Return copied goal path"]
D -->|FAILURE| F["Return NO_SOLUTION"]
D -->|ABORT| G["Return NODE_LIMIT"]
D -->|CUTOFF| H{"Depth cap reached?"}
H -->|Yes| I["Return DEPTH_LIMIT"]
H -->|No| J["Increase limit by one"]
J --> B
subgraph SEARCH["DLS: one recursive call"]
K{"Node budget available?"} -->|No| Q["Return ABORT"]
K -->|Yes| M["Count node and update peak"]
M --> N{"Goal?"}
N -->|Yes| O["Copy path; return FOUND"]
N -->|No| P{"Remaining depth zero?"}
P -->|Yes| R{"Any child outside active path?"}
R -->|Yes| S["Return CUTOFF"]
R -->|No| T["Return FAILURE"]
P -->|No| U["Scan next child; skip cycle edges"]
U --> V{"Allowed child left?"}
V -->|Yes| W["Push and mark child; recurse"]
W --> X["Unmark and pop child"]
X --> Y{"Child outcome?"}
Y -->|FOUND or ABORT| Z["Propagate outcome"]
Y -->|CUTOFF| AA["Remember cutoff"]
AA --> U
Y -->|FAILURE| U
V -->|No| AB{"Any child cutoff?"}
AB -->|Yes| S
AB -->|No| T
end
B -. "DLS entry" .-> K
Worked Example with a Full Trace
Use the ordered adjacency lists $0:[1,2]$, $1:[3,4]$, $2:[5]$, $3:[6]$, $4:[]$, $5:[7]$, $6:[]$, $7:[]$. Start at $0$, seek goal $7$, allow maximum depth $5$, and use a node budget of $1{,}000{,}000$. There are no cycle edges. Child order is significant: the left subtree is exhausted before the right subtree is tried.
The complete admission trace follows. Each row records the active path on entry, its remaining depth, and the code that this call eventually returns. A parent's result appears on its entry row even though it is determined after the listed descendants return; DFS returns in the corresponding reverse nesting order. Undo removes each child immediately after its return. The root is also removed between passes.
| Limit | Admission in pass | Active path | Remaining | Eventual return |
|---|---|---|---|---|
| $0$ | $1$ | $[0]$ | $0$ | CUTOFF: child $1$ exists |
| $1$ | $1$ | $[0]$ | $1$ | CUTOFF: children cut off |
| $1$ | $2$ | $[0,1]$ | $0$ | CUTOFF: child $3$ exists |
| $1$ | $3$ | $[0,2]$ | $0$ | CUTOFF: child $5$ exists |
| $2$ | $1$ | $[0]$ | $2$ | CUTOFF |
| $2$ | $2$ | $[0,1]$ | $1$ | CUTOFF: child $3$ cut off |
| $2$ | $3$ | $[0,1,3]$ | $0$ | CUTOFF: child $6$ exists |
| $2$ | $4$ | $[0,1,4]$ | $0$ | FAILURE: true leaf |
| $2$ | $5$ | $[0,2]$ | $1$ | CUTOFF: child $5$ cut off |
| $2$ | $6$ | $[0,2,5]$ | $0$ | CUTOFF: child $7$ exists |
| $3$ | $1$ | $[0]$ | $3$ | FOUND |
| $3$ | $2$ | $[0,1]$ | $2$ | FAILURE: both branches exhausted |
| $3$ | $3$ | $[0,1,3]$ | $1$ | FAILURE: child $6$ exhausted |
| $3$ | $4$ | $[0,1,3,6]$ | $0$ | FAILURE: true leaf |
| $3$ | $5$ | $[0,1,4]$ | $1$ | FAILURE: true leaf |
| $3$ | $6$ | $[0,2]$ | $2$ | FOUND |
| $3$ | $7$ | $[0,2,5]$ | $1$ | FOUND |
| $3$ | $8$ | $[0,2,5,7]$ | $0$ | FOUND: copy answer before undo |
At a boundary cutoff only the first admissible edge is probed. Thus limit $0$ tests $0\to1$ but does not test $0\to2$. At limit $1$ the tested edges, in order, are $0\to1$, $1\to3$, $0\to2$, $2\to5$. At limit $2$ they are $0\to1$, $1\to3$, $3\to6$, $1\to4$, $0\to2$, $2\to5$, $5\to7$. At limit $3$ those same seven edges are tested, but their children are now admitted. This accounts for every edge-counter increment.
| Limit | Return | New nodes | New edges | New cutoff leaves | New cycle skips | Cumulative nodes / edges / cutoffs | Peak so far |
|---|---|---|---|---|---|---|---|
| $0$ | CUTOFF | $1$ | $1$ | $1$ | $0$ | $1/1/1$ | $0$ |
| $1$ | CUTOFF | $3$ | $4$ | $2$ | $0$ | $4/5/3$ | $1$ |
| $2$ | CUTOFF | $6$ | $7$ | $2$ | $0$ | $10/12/5$ | $2$ |
| $3$ | FOUND | $8$ | $7$ | $0$ | $0$ | $18/19/5$ | $3$ |
The saved witness is $[0,2,5,7]$ with $3$ edges. The active path is empty after return, every mark is false, and the total node counter is $18$, even though the graph has only $8$ distinct states. No goal exists at a smaller depth, as demonstrated by the three completed earlier passes.
Trace It by Hand
At limit $2$, the call for state $3$ returns cutoff, then the call for state $4$ returns failure. Predict the code returned by their parent $1$, and the path immediately before entering state $2$.
Reveal: parent $1$ returns CUTOFF, because at least one child was depth-limited. Undo restores the root path $[0]$, then entering $2$ produces $[0,2]$ with remaining depth $1$. The pass ultimately admits $6$ nodes and returns CUTOFF.
Now change the goal to $5$ and cap depth at $2$. The call at $[0,2,5]$ has remaining depth $0$ but returns FOUND; the totals become $10$ nodes, $11$ edges, and $4$ cutoff leaves. Finally restore goal $7$ and set the total node budget to $8$: limit $2$ admits $0,1,3,4$, then refuses admission of $2$, restores the path, and returns NODE_LIMIT. Neither resource-limit result proves there is no solution.
Implementation in Python, C++, and Java
The three programs implement the same driver and DLS logic. is_goal and successors isolate the search from the problem representation; here their adapter is an ordered adjacency list with dense integer state IDs. To search an implicit puzzle tree, replace this adapter with a complete state representation and a lazy successor generator, and adapt the current-path membership structure. If legal moves or goals depend on history, that history must be part of the state.
Input contract. Supply a finite adjacency list with $1\le V\le128$ vertices and at most $4096$ total adjacency entries; every successor and start/goal ID must be an integer in $[0,V-1]$. Rows and the goal collection must be non-null collections of integer IDs. Self-loops, duplicate edges, and duplicate goal IDs are allowed; goal collections have at most $128$ entries. Adjacency order is preserved and copied, and callers' inputs are not modified. The maximum depth satisfies $0\le D\le64$; the global budget satisfies $1\le B\le10{,}000{,}000$. These are demonstration safeguards, not mathematical restrictions of IDDFS. Invalid numeric sizes, ranges, and IDs in this contract are rejected with an exception.
run returns status, last attempted depth limit, a copied answer, statistics in the order [nodes,edges,cutoffs,cycles,peak], and per-pass histories. A history entry contains the limit, the internal DLS code, and four counter deltas. cutoffs counts boundary calls that discover an admissible child, not each propagation of the code. peak is the greatest admitted edge-depth. edges also includes a tested edge whose child admission is refused. restored=YES is printed only after the driver's restoration assertion has passed.
NO_SOLUTION means root FAILURE, including an empty goal set once the reachable search is exhausted. DEPTH_LIMIT and NODE_LIMIT are inconclusive and have empty answers. A budget is checked on the next attempted admission: exactly consuming it does not erase a goal or failure already established. Each call to run resets its own search state, so the same solver can be reused.
Run with Python 3, compile C++14 or newer, and save Java as Main.java for Java 17 or newer. The complete programs include the worked tree, boundary and root goals, a dead leaf, a cycle, a transposition, and both resource limits.
Expected standard output, identical in all three languages:
worked: FOUND, limit=3, path=[0,2,5,7]nodes=18, edges=19, cutoffs=5, cycles=0, peak=3, restored=YESpasses=[0:CUTOFF:1,1:CUTOFF:3,2:CUTOFF:6,3:FOUND:8]boundary goal: FOUND, limit=2, path=[0,2,5]nodes=10, edges=11, cutoffs=4, cycles=0, peak=2, restored=YESpasses=[0:CUTOFF:1,1:CUTOFF:3,2:FOUND:6]root goal: FOUND, limit=0, path=[0]nodes=1, edges=0, cutoffs=0, cycles=0, peak=0, restored=YESpasses=[0:FOUND:1]dead leaf: NO_SOLUTION, limit=0, path=[]nodes=1, edges=0, cutoffs=0, cycles=0, peak=0, restored=YESpasses=[0:FAILURE:1]cycle: NO_SOLUTION, limit=1, path=[]nodes=3, edges=3, cutoffs=1, cycles=1, peak=1, restored=YESpasses=[0:CUTOFF:1,1:FAILURE:2]transposition: FOUND, limit=2, path=[0,2,3]nodes=9, edges=10, cutoffs=4, cycles=0, peak=2, restored=YESpasses=[0:CUTOFF:1,1:CUTOFF:3,2:FOUND:5]depth cap: DEPTH_LIMIT, limit=2, path=[]nodes=10, edges=12, cutoffs=5, cycles=0, peak=2, restored=YESpasses=[0:CUTOFF:1,1:CUTOFF:3,2:CUTOFF:6]node cap: NODE_LIMIT, limit=2, path=[]nodes=8, edges=10, cutoffs=4, cycles=0, peak=2, restored=YESpasses=[0:CUTOFF:1,1:CUTOFF:3,2:ABORT:4]checks = PASS
Complexity, Correctness, and Invariants
Let $b$ bound branching, $d$ be the minimum goal depth, $D$ the configured cap, and $P$ the number of attempted passes. Let $N_i$ count path occurrences at depth $i$ in the search tree after current-path cycle pruning. With unlimited resources, the number of admitted calls through the first successful limit is bounded by
$$N_{\mathrm{ID}}\le\sum_{i=0}^{d}(d-i+1)N_i.$$
A depth-$i$ occurrence can be revisited in passes $i$ through $d$; the final pass may stop early. In a regular tree with constant $b\gt1$, $N_i=b^i$, giving $O(b^d)$ admitted calls. Compared with one fully explored tree through depth $d$, the ratio of the two full-sum node counts tends to $b/(b-1)$. This statement concerns that regular-tree model, not every graph or child order. On a single chain with a goal at depth $d$, the exact count is
$$1+2+\cdots+(d+1)=\frac{(d+1)(d+2)}{2}=\Theta(d^2).$$
For the actual programs, use measured work: let $N$ be all admitted calls and $E_{\mathrm{seen}}$ all tested adjacency entries. Constructor validation and graph copying cost $O(V+E)$. A run costs $O(V+P V+N+E_{\mathrm{seen}})$, including initializing flags and checking all $V$ marks after each pass; there is at most one final refused admission. Combined construction and search therefore cost $O(V+E+P V+N+E_{\mathrm{seen}})$. The node budget bounds admissions but is not an edge-check or wall-clock budget. A node with many cycle-blocked neighbors can still perform many edge checks.
Auxiliary storage in these programs is $O(V+D+P)=O(V+D)$: goal/path flags, the recursion and active/answer paths, and pass histories. The copied graph adds $O(V+E)$ storage. A generic implicit-tree implementation can use $O(d)$ active state/iterator storage with constant-size states and lazily generated successors; materializing all children in each stack frame instead gives up to $O(bd)$ storage. Large states or expensive generation must be charged explicitly.
Best, average, worst. A root goal needs one admitted call, zero tested edges, and one pass; construction and flag initialization still cost $O(V+E)$ for this adapter. There is no distribution-independent average-case bound: solution depth, branching, successor order, and transpositions determine it. Worst-case tree work reaches the full sums above through $D$ or until a resource limit stops it; cyclic graphs can have many simple-path occurrences despite small $V$. A finite goal-free tree eventually returns failure; an infinite, finitely branching goal-free tree can deepen forever without caps.
Why the result is correct. On entry, the active path is a legal simple path from the root to the current state, the marks equal exactly its states, and its depth plus remaining depth equals the pass limit. Goal detection copies a legal witness. With zero remaining depth, an admissible child proves that the bound excluded an extension; absence of one proves this branch exhausted. With positive remaining depth, induction over the child calls gives either a witness, a cutoff somewhere, or exhaustion of every admissible branch. Undo preserves the invariant between siblings and on all return paths.
If the complete-state graph has a goal-reaching walk containing a repeated state, removing the intervening cycle yields a shorter goal-reaching walk. Thus some shortest solution is simple and current-path cycle pruning preserves one. On a finite graph, there are finitely many such paths; eventually a pass either finds a goal or exhausts them. Since all smaller limits complete before the first successful one, the found path has minimum edge count. This argument assumes no ABORT, a sufficient depth cap, and no hidden history in state identity. A local FAILURE may depend on the current prefix; only exhaustion at the root warrants NO_SOLUTION.
| Method / representation | Time model | Auxiliary search space | Guarantee and suitable use |
|---|---|---|---|
| DFS on a finite implicit tree of maximum depth $m$ | $O(\sum_{i=0}^{m}b^i)$ | $O(m)$ with lazy children | Finds some goal; successor order may lead far beyond the nearest one |
| One depth-limited DFS pass | $O(\sum_{i=0}^{L}b^i)$ on a tree | $O(L)$ with lazy children | Finds a goal within the limit, possibly deeper than another; distinguishes cutoff from exhaustion |
| IDDFS on an implicit regular tree, constant $b\gt1$ | $O(b^d)$ admitted calls | $O(d)$ with lazy children; $O(bd)$ if children are stored | Shallowest goal with finite branching and sufficient resources |
| Breadth-first tree search | $O(\sum_{i=0}^{d}b^i)$ admissions through depth $d$ | $O(b^d)$ frontier for $b\gt1$ | Shallowest goal without repeated passes |
| BFS on an explicit unweighted graph | $O(V+E)$ | $O(V)$ excluding input graph | Usually preferable here; safe visited-state deduplication and shortest edge paths |
| These bounded, instrumented IDDFS programs | $O(V+E+P V+N+E_{\mathrm{seen}})$ including construction | $O(V+D)$ excluding copied graph | Observable cutoff, undo, and resource outcomes; repeats transpositions |
| Uniform-cost search / Dijkstra on an explicit graph | $O((V+E)\log V)$ with an indexed binary heap | $O(V)$ excluding graph | Minimum weighted cost for nonnegative edge weights |
| IDA* on an implicit search space | Depends on heuristic and cost-threshold sequence; often exponential | Depth-proportional with lazy children and no transposition table | Uses cost plus heuristic thresholds; optimality needs appropriate cost and admissibility assumptions |
At every admitted call, the active path is legal, its marks are exact, and depth plus remaining depth equals the pass limit; every child step is undone before return. A completed pass either finds a witness, proves its search exhausted, or identifies a depth-excluded extension, so increasing limits one at a time makes the first found witness shallowest when resources suffice.
Variants, Pitfalls, and Optimizations
Avoid naive visited sets. On edges $0\to1$, $0\to2$, $1\to2$, $2\to3$ with goal $3$ and limit $2$, DFS first reaches $2$ along $[0,1,2]$ with no depth left. It must later revisit $2$ along $[0,2]$ with one step left to find $3$. Marking $2$ visited for the entire pass loses that shallow solution; keeping visited marks between passes is also unsound. A depth-aware transposition scheme requires a proof accounting for remaining depth, completion, and path-dependent restrictions. Current-path marking is the simple safe baseline used here.
Preserve what the bound means. Increase limits by one to retain the first-found shallowest-path guarantee. Jumping from limit $2$ to $4$ can let DFS find a depth-$4$ goal before a different depth-$3$ goal. For weighted actions, use uniform-cost search or a properly specified IDA* search with thresholds on $f=g+h$, not merely the number of recursive calls. If all edges share one positive cost, minimum depth and minimum cost agree.
Choose the representation deliberately. An explicit graph with modest $V+E$ generally favors BFS; IDDFS can repeatedly enumerate exponentially many path prefixes through a small graph. For a huge implicit state space, retain only the active path and successor iterator. If puzzle actions mutate a shared state, pair every apply with undo even on success, timeout, or cancellation. The sample's ABORT handling demonstrates the same requirement, but an exception thrown by a user-provided successor hook would need its own exception-safe cleanup.
Use move ordering for speed, without changing correctness. Stable child order makes traces reproducible; reordering children can reduce the final pass's work and change which equal-depth goal is returned. Game-playing programs also repeat depth-limited searches to improve move ordering and retain the last fully completed evaluation under a time budget. Their heuristic minimax evaluations and completed-depth policy solve a different task from this first-goal IDDFS; they do not inherit a shortest-solution claim.
Make limits honest. DEPTH_LIMIT and NODE_LIMIT mean unknown, even when the answer field is empty. A finite graph with a distant goal can need a deeper cap; a shallow graph with many transpositions can consume the node budget. For a real latency bound, add time or edge checks as well as node checks, propagate cancellation separately from failure, and keep the restoration invariant. C++ and Java use 64-bit counters; even without counter overflow, the number of paths can become impractical.
Test the goal before rejecting a boundary node, and propagate cutoff if any explored child was cut off. Restore every path edit before propagating success or cancellation, avoid naive global visited marks, and never label a depth or node cap as proof that no solution exists.
Practice Problems and Real-World Use
Start with a hand trace, then compare against an independent breadth-first oracle on small graphs. Check both the minimum edge count and the exact path under stable child order; separately test restoration, repeated runs, and the meaning of a limited result. Boundary fixtures should include a root goal, a goal exactly at the cap, an exhausted leaf, an empty goal set, self-loops, duplicate edges, and a cycle that has no reachable goal.
The programs were checked on 6,923 inputs per language: all $512$ directed three-vertex graphs with all $8$ goal subsets, $1{,}500$ randomized cyclic graphs, $1{,}000$ randomized trees, $300$ transposition-rich acyclic graphs, $13$ boundary fixtures, and $14$ invalid inputs. For all $6{,}909$ valid inputs, an independent bounded-path event oracle verified outcomes, paths, every counter, and per-pass histories; a graph BFS oracle verified shallowest-path behavior. There were $54{,}514$ path-restoration audits, and C++ and Java matched Python on the full corpus and repeated runs. The C++ corpus also passed address and undefined-behavior instrumentation; these checks support this bounded implementation rather than implying a guarantee for arbitrary adapters.
For primary background, see Poole and Mackworth's iterative-deepening discussion and the AIMA authors' search chapter. They discuss the depth/memory tradeoff and repeated-pass analysis; the exact cutoff probes, instrumentation, resource contract, code, and worked trace here are specified explicitly in this lesson.
| Practice problem or application | Concrete task | Idea tested | Difficulty |
|---|---|---|---|
| Root and boundary goals | Use goals $0$, then $5$, on the worked tree | Goal-before-boundary order and depth measured in edges | Easy |
| Exhausted cycle | Trace $0\to1\to0$ with no goals | Current-path cycle pruning; failure without endless deepening | Easy |
| Unknown-depth move puzzle | Generate reversible legal moves and compare against BFS on tiny instances | Implicit state generation, copied witness, minimum move count | Medium |
| Transposition trap | Use the four-state graph in the pitfalls diagram | Remaining depth differs across prefixes; naive visited is unsound | Medium |
| Chain versus branching tree | Measure admissions on chains and full binary trees | Quadratic chain overhead versus geometric-level dominance | Medium |
| Derivation or rewrite search | Find a shortest sequence of unit-cost rules with a complete bounded state model | State identity, finite branching, reversible expansion | Hard |
| Time-bounded game-tree study | Repeat depth-limited minimax and retain only completed evaluations | Distinguish evaluation deepening from first-goal IDDFS | Hard |
| Weighted-search comparison | Construct a shallow expensive goal and a deeper cheap one | Depth optimality versus cost optimality; motivate UCS or IDA* | Hard |