The Cave of Regrets — Understanding Backtracking Through Failed Paths
In a glowing cavern of dead-ends and second chances, Arjun discovers that it’s not enough to move forward — you must also know how to safely undo a move.
Characters: Arjun, Guru Bodhi · Arc: End of Gurukul · Pattern: Backtracking, Search Trees, Try / Undo / Retry
📜 Sutra of Regret — When Every Choice Feels Dangerous
Deep beneath Chintan, Arjun entered the Cave of Regrets. The floor was carved with glowing runes, each marking a path someone had taken — and then abandoned.
Some paths ended at sheer cliffs. Others at locked stone doors. A few stopped abruptly in darkness, the carvings scratched away in frustration.
“This is exactly how my code feels,” Arjun muttered. “Sudoku solvers, maze solvers, combinatorics problems… I keep trying options, getting stuck, and then restarting from scratch. I don’t know how to systematically explore and undo choices.”
📜 Sutra of Re-tries — The Discipline of Undoing
A familiar lantern-light appeared beside him. Guru Bodhi traced a glowing symbol in the air — a branching tree whose lines dimmed and brightened as he moved his hand.
“Backtracking,” he continued, “is the art of exploring a decision tree without panic. You:
- choose an option,
- explore it fully,
- and if it fails, you undo that choice and try the next one.
“No rage-quits. No ‘start over from the beginning.’ Only structured search.”
🧠 Sutra of the Pattern — Try, Explore, Undo, Repeat
In code, most backtracking algorithms share the same skeleton:
- Choose — pick a candidate move / value.
- Explore — recurse with that choice applied.
- Undo — if it doesn’t work, revert the state and try the next candidate.
The computer walks a path down the decision tree, and whenever it hits a dead end, it calmly walks back up to the previous junction.
🧭 Backtracking Scroll I — Escaping the Cave (Maze Path)
First, Guru Bodhi gave Arjun a simple problem:
find a path from entrance to exit in a grid maze. Some cells are walls (#), others are free (.).
def find_path(maze, r, c, end_r, end_c, path):
"""Backtracking: find ONE path from (r, c) to (end_r, end_c)."""
rows, cols = len(maze), len(maze[0])
# 1) Out-of-bounds or hit a wall or visited again → dead end
if r < 0 or c < 0 or r >= rows or c >= cols:
return False
if maze[r][c] != '.':
return False
# 2) Choose: mark current cell as part of the path
maze[r][c] = '*'
path.append((r, c))
# 3) Goal reached?
if (r, c) == (end_r, end_c):
return True
# 4) Explore neighbors (up, right, down, left)
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
for dr, dc in directions:
if find_path(maze, r + dr, c + dc, end_r, end_c, path):
return True
# 5) Undo: backtrack – unmark and remove from path
path.pop()
maze[r][c] = '.'
return False
Notice the rhythm: mark → recurse → unmark. That’s the heartbeat of backtracking.
🧮 Backtracking Scroll II — Generating All Subsets
Next, Guru Bodhi turned the cave into a combinatorics puzzle: given a set, list all its subsets.
At each step we decide: include this element or skip it. That forms a binary decision tree.
def subsets(nums):
result = []
def backtrack(index, current):
# Reached the end – record the current subset
if index == len(nums):
result.append(current.copy())
return
# 1) Choose: include nums[index]
current.append(nums[index])
backtrack(index + 1, current)
# 2) Undo: remove last choice
current.pop()
# 3) Explore alternative: skip nums[index]
backtrack(index + 1, current)
backtrack(0, [])
return result
Same rhythm: do, recurse, undo, then try the alternative branch.
🌿 Where Backtracking Shows Up in Real Problems
Backtracking is useful whenever you:
- need to search through combinations or permutations,
- must obey constraints (no duplicate numbers, must fit rules, etc.),
- and are okay with exploring many possibilities but not repeating work or losing your place.
Classic examples:
- Sudoku solvers.
- N-Queens on a chessboard (placing queens so they don’t attack).
- Word-search / crossword / crossword fill-in.
- Finding all valid paths / colorings / assignments under rules.
Backtracking doesn’t guarantee efficiency, but it guarantees correctness and completeness: you will either find all solutions or prove none exist.