Backtracking assigns one variable at a time and abandons a partial assignment as soon as it cannot extend to a solution. With forward checking, placing a knight deletes its occupied and attacked squares from every remaining knight's constraint domain. An empty constraint domain rejects the branch immediately; stronger constraint propagation, such as arc consistency, can detect additional incompatibilities among future choices.
This combines early rejection with depth-first storage. It retains the current assignment and domain changes rather than the entire breadth-first layer of partial placements. A direct knight-variable implementation uses space for explicit domains, plus its assignment and restoration trail, instead of an exponentially large breadth-first search search frontier. Propagation has a computational cost and does not guarantee improvement on every input, but often prevents exploration of vast inconsistent subtrees.
The minimum remaining values heuristic selects an unassigned variable with the fewest currently legal values, exposing the likeliest failure first. The degree heuristic selects a variable constrained to the largest number of other unassigned variables, often breaking minimum remaining values heuristic ties. Degree is measured in the remaining constraint graph, not by counting assigned neighbors. In the symmetric knight-variable encoding the remaining degrees can all tie, so the heuristic need not discriminate there.[ref]
For a finite constraint satisfaction problem, remove values violating unary restrictions before starting. The following backtracking algorithm makes independent copies of constraint domains at each branch, so failed forward checking cannot contaminate a sibling branch.
SOLVE(P):
    D := domains of P after unary filtering
    if some D[X] is empty: return failure
    return SEARCH({}, D)

SEARCH(assignment, D):
    if every variable is assigned: return assignment
    X := SELECT-UNASSIGNED-VARIABLE(assignment, D, P)
    for v in ORDER-DOMAIN-VALUES(X, assignment, D, P):
        if X=v is compatible with the current assignment:
            next_assignment := assignment extended by X=v
            next_domains := a deep copy of D
            next_domains[X] := {v}
            if INFERENCES(X, v, next_assignment, next_domains, P) succeeds:
                result := SEARCH(next_assignment, next_domains)
                if result is not failure: return result
    return failure
SELECT-UNASSIGNED-VARIABLE implements minimum remaining values: choose an unassigned variable with the smallest current constraint domain. Break ties by the degree heuristic, choosing the variable constrained to the greatest number of other unassigned variables; a fixed final tie rule makes execution reproducible. ORDER-DOMAIN-VALUES may use any complete ordering; ascending values suffice here.
INFERENCES implements forward checking: for each unassigned neighbor of , remove every value incompatible with , and fail if any constraint domain becomes empty. For higher-arity constraints, reject any completely instantiated violation and use a sound chosen filtering rule; the stated neighbor test is the usual binary case. Forward checking never removes a value used by a possible extension of the current assignment. Exhausting every surviving value therefore makes this finite backtracking procedure both sound and complete.[ref][ref]
One solution is . Use ascending value order; after minimum remaining values and the degree heuristic, break any remaining variable tie alphabetically. Apply the unary restrictions first:
Minimum remaining values selects , even though has greater initial vertex degree. Set . Forward checking removes from the three neighbors :
Now is the unique minimum remaining values choice. Set . Forward checking removes from , giving
Select and set . Its remaining neighbors are , so forward checking gives
The two remaining variables tie on constraint domain size and each has one unassigned neighbor, so the degree heuristic also ties. Alphabetical order selects ; take . Since is already absent from , forward checking makes no further deletion. Finally choose . No empty constraint domain or violation occurs, so this branch needs no backtracking.
All eight binary constraints are checked explicitly by the unequal value pairs
The unary restrictions also hold. Equalities and are permitted because those pairs have no edges in the constraint graph.
The translation gives a sound and complete route through an optimized SAT solver; it trades access to general-purpose SAT machinery against encoding cost and the possible loss of constraint-specific structure. Modern SAT solvers use efficient unit propagation, adaptive branching and clause learning. Learned Boolean clauses can summarize conflicts involving many original variables, allowing later branches to avoid repeating the same failed combination. The reduction also offers a standard, reusable interface: improvements to a SAT solver can benefit many finite-domain constraint satisfaction problems.
The Boolean formula may be much larger than the original constraint satisfaction problem, especially for a succinct high-arity relation. One original variable becomes many indicators, and general branching may lose the interpretation that the minimum remaining values and degree heuristic exploit directly. Native constraint propagation can exploit a relation without enumerating all its forbidden tuples. Even in the binary case, stronger propagation does not follow just from switching to SAT.
For example, take , , and let the only allowed pairs be . The forbidden-pair encoding contains and . If becomes false, arc consistency deletes because it has no support. Yet the direct encoding has no unit Boolean clause forcing : both remain possible individually, so plain unit propagation does not make that deletion. A SAT solver can eventually derive it through additional reasoning or search. Conversely, clause learning can combine information beyond the local consistency enforced by AC-3. Neither approach uniformly dominates: performance depends on representation, propagation strength and the instance's structure; both still face exponential worst-case search on general inputs.