22 KiB
Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo
Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo
1. Workflow Overview
This workflow demonstrates how to implement the recursive algorithm for the classic Towers of Hanoi puzzle using n8n's sub-workflows. It serves as a proof of concept for recursion handling within n8n by:
- Recursively solving the Towers of Hanoi problem for a given number of discs.
- Using sub-workflows to model recursive calls.
- Maintaining and updating stacks (rods) representing disc positions.
- Logging each move and formatting the final solution as human-readable instructions.
The workflow logically separates into these blocks:
- 1.1 Input Reception and Initialization: Accepts the number of discs and initializes stacks representing rods A, B, and C.
- 1.2 Recursive Decision (Base Case and Recursive Calls): Checks if only one disc remains (base case) or recurses on smaller subproblems.
- 1.3 Disc Movement and Stack Updates: Moves discs between stacks and logs the steps.
- 1.4 Recursive Sub-workflow Invocations: Calls the main workflow recursively with updated parameters to simulate recursive calls.
- 1.5 Result Formatting: Converts the logged moves into a formatted textual solution.
2. Block-by-Block Analysis
2.1 Input Reception and Initialization
-
Overview:
This block sets the initial number of discs, enforces constraints on the allowed disc count, and initializes the stacks representing rods A, B, and C with their respective discs. -
Nodes Involved:
- Start (Manual Trigger)
- Set number of discs
- Create stacks
-
Node Details:
-
Start
- Type: Manual Trigger
- Role: Initiates workflow execution manually.
- Input/Output: No input; triggers the workflow start.
- Edge cases: None.
-
Set number of discs
- Type: Set
- Role: Defines a numeric variable
numberOfDiscs(default 4). - Configuration: Sets
numberOfDiscsto 4 initially. - Input: Triggered by Start node.
- Output: Passes data to
Create stacks. - Edge cases: None.
-
Create stacks
- Type: Code (JavaScript)
- Role: Validates
numberOfDiscs(min 1, max 5) and creates three stacks (A, B, C) with discs initialized on stack A in descending order. Also initializes an empty logs array. - Key logic:
- Limits discs between 1 and 5 to avoid recursion depth issues.
- Prepares
stackA,stackB,stackCobjects withnameanditemsarrays. stackA.itemscontains discs [numberOfDiscs ... 1].stackB.itemsandstackC.itemsare empty arrays.- Logs array is empty.
- Input: Receives
numberOfDiscsfrom previous node. - Output: Passes stacks and logs to sub-workflow
A B C. - Edge cases: Input number outside allowed range adjusted automatically.
-
2.2 Recursive Decision (Base Case and Recursive Calls)
-
Overview:
Decides whether the current recursion step is the base case (one disc) or requires further recursion. Directs flow accordingly. -
Nodes Involved:
- A B C (Execute Workflow - recursive call)
- Max 1 disc (If node)
- X to Z (Code)
- X Z Y (Execute Workflow - recursive call)
- Y X Z (Execute Workflow - recursive call)
- Update (Code)
- Update (Code)
-
Node Details:
-
A B C
- Type: Execute Workflow (Sub-workflow call)
- Role: Recursively calls the main workflow with inputs: current stacks (A, B, C), logs, and
numberOfDiscs. - Configuration: Waits for sub-workflow to finish to continue processing.
- Input: Receives initialized stacks and logs from
Create stacks. - Output: Passes results to
Max 1 disc. - Edge cases: Recursive calls can fail due to stack overflow if max recursion depth exceeded.
-
Max 1 disc
- Type: If
- Role: Checks if
numberOfDiscs≤ 1 (base case). - Configuration: Condition
numberOfDiscs <= 1. - Input: Receives current state from
A B C. - Output:
- True branch:
X to Z(move disc directly). - False branch:
X Z Y(recursive call with swapped stacks).
- True branch:
- Edge cases: Expression evaluation errors if input is missing or malformed.
-
X to Z
- Type: Code
- Role: Moves one disc from stackX to stackZ and logs the move.
- Logic:
- Pops disc from
stackX.items, pushes tostackZ.items. - Appends a string
"stackX.name stackZ.name disc"to logs.
- Pops disc from
- Input: Receives stacks and logs from
Max 1 disctrue branch. - Output: Passes updated stacks/logs to
Y X Z. - Edge cases: Popping from empty stack will cause errors; ensure input stacks are valid.
-
X Z Y
- Type: Execute Workflow (Sub-workflow call)
- Role: Recursive call with
numberOfDiscs - 1and stacks reordered to (stackX, stackZ, stackY). - Input: Receives updated stacks and logs from
Max 1 discfalse branch. - Output: Passes results to
Updatenode. - Edge cases: Recursive calls may cause stack depth issues.
-
Y X Z
- Type: Execute Workflow (Sub-workflow call)
- Role: Recursive call with
numberOfDiscs - 1and stacks reordered to (stackY, stackX, stackZ). - Input: Receives updated stacks and logs from
X to Z. - Output: Passes results to
Updatenode. - Edge cases: Same as other recursion calls.
-
Update (two instances: one after
X Z Y, one afterY X Z)- Type: Code
- Role: Corrects stack assignments and increments
numberOfDiscsto restore state after recursive calls. - Logic differences:
- One swaps stacks Y and Z to reset ordering after
X Z Y. - The other swaps stacks X and Y after
Y X Z.
- One swaps stacks Y and Z to reset ordering after
- Input: Receives results from respective sub-workflow calls.
- Output: Passes updated data back to
X to ZorSolution. - Edge cases: Errors if input data structure is unexpected.
-
2.3 Disc Movement and Stack Updates
-
Overview:
Handles the actual movement of discs between stacks and keeps a log of moves. -
Nodes Involved:
- X to Z (Code)
- X to Z (Code) [duplicate node name but different instance]
- Update (Code)
- Update (Code)
-
Node Details:
-
X to Z (first instance)
- See section 2.2 for details.
-
** X to Z** (second instance)
- Type: Code
- Role: Same as the first
X to Z, moves disc from stackX to stackZ and logs the move. - Positioned differently in the flow but functionally equivalent.
- Edge cases: Same as above.
-
Update and ** Update**
- See section 2.2 for details.
-
2.4 Result Formatting
-
Overview:
Transforms the collected logs of disc movements into a readable, formatted text output describing each move step-by-step. -
Nodes Involved:
- Solution (Code)
-
Node Details:
- Solution
- Type: Code
- Role: Converts the array of logged moves into a formatted multiline string describing each move in human-readable form.
- Logic:
- Splits each log entry into from, to, and disc.
- Formats first move with "Move disc X from A to B,".
- Subsequent moves formatted with shorthand like "then A 2→ C," etc.
- Inserts line breaks and commas for readability.
- Input: Receives logs from recursive sub-workflow results (
A B C). - Output: Provides final
solutionstring output. - Edge cases: Empty logs or malformed entries may cause formatting issues.
- Solution
2.5 Supporting Sticky Notes (Documentation)
-
Overview:
Sticky notes provide contextual explanations, input/output expectations, and algorithm details to assist users understanding the workflow. -
Nodes Involved:
- user input
- setup
- result
- solution
- swap
- move
- update
- Towers of Hanoi (main description note)
-
Node Details:
- These nodes are purely informational with no execution role.
- Contain sample inputs, outputs, algorithm description, and step explanations.
- Provide links to further resources such as GitHub and Wikipedia on recursion.
3. Summary Table
| Node Name | Node Type | Functional Role | Input Node(s) | Output Node(s) | Sticky Note |
|---|---|---|---|---|---|
| Start | Manual Trigger | Starts the workflow manually | - | Set number of discs | |
| Set number of discs | Set | Sets initial numberOfDiscs (4) | Start | Create stacks | |
| Create stacks | Code | Validates disc count and initializes stacks A, B, C with discs on A | Set number of discs | A B C | |
| A B C | Execute Workflow | Recursive call to main workflow with stacks and logs | Create stacks | Max 1 disc | |
| Max 1 disc | If | Checks base case (n ≤ 1) | A B C | X to Z (true), X Z Y (false) | |
| X to Z | Code | Moves one disc from stackX to stackZ and logs move | Max 1 disc (true) | Y X Z | |
| Y X Z | Execute Workflow | Recursive call with stacks reordered after move | X to Z | Update | |
| Update | Code | Restores stacks & increments numberOfDiscs after Y X Z | Y X Z | X to Z | "### Node steps\n- numberOfDiscs += 1\n- YXZ to XYZ\n - stack = stackY\n - stackY = stackX\n - stackX = stack" |
| X Z Y | Execute Workflow | Recursive call with stacks reordered before move | Max 1 disc (false) | Update | |
| Update | Code | Restores stacks & increments numberOfDiscs after X Z Y | X Z Y | X to Z | "### Node steps\n- numberOfDiscs += 1\n- Reset stacks (XZY to XYZ)\n - stack = stackY\n - stackY = stackZ\n - stackZ = stack" |
| X to Z | Code | Moves disc from stackX to stackZ and logs move | Update | Y X Z | "### Node steps\n- disc = stackX.pop()\n- stackZ.push(disc)" |
| Solution | Code | Formats logs into human-readable solution string | A B C | - | |
| user input | Sticky Note | Describes input: numberOfDiscs = 4 | - | - | "### Input\n- numberOfDiscs: 4 " |
| setup | Sticky Note | Describes initial stacks and discs | - | - | "### Setup\n- numberOfDiscs: 4 \n- stackA\n - name: A \n - items: 4, 3, 2, 1 \n- stackB\n - name: B \n - items: - \n- stackC\n - name: C \n - items: - \n" |
| result | Sticky Note | Shows expected final stacks after solving | - | - | "### Result\n- numberOfDiscs: 4 \n- stackX\n - name: A \n - items: - \n- stackY\n - name: B \n - items: - \n- stackZ\n - name: C \n - items: 4, 3, 2, 1" |
| solution | Sticky Note | Example formatted solution output | - | - | "### Solution\nMove disc 1 from A to B,\nthen A 2→ C,\nB 1→ C, A 3→ B,\nC 1→ A, C 2→ B,\nA 1→ B, A 4→ C,\nB 1→ C, B 2→ A,\nC 1→ A, B 3→ C,\nA 1→ B, A 2→ C,\nB 1→ C." |
| swap | Sticky Note | Explains input stack swaps in recursion | - | - | "### Node input\n- numberOfDiscs -1 \n- stackX = stackX \n- stackY = stackZ \n- stackZ = stackY " |
| move | Sticky Note | Explains disc move operations | - | - | "### Node steps\n- disc = stackX.pop()\n- stackZ.push(disc)" |
| update | Sticky Note | Explains stack resetting steps | - | - | "### Node steps\n- numberOfDiscs += 1\n- YXZ to XYZ\n - stack = stackY\n - stackY = stackX\n - stackX = stack" |
| Towers of Hanoi | Sticky Note | Full algorithm description, notes, and resource links | - | - | See section 5 for full content and links. |
4. Reproducing the Workflow from Scratch
-
Create Manual Trigger Node ("Start")
- Type: Manual Trigger
- No configuration needed.
-
Create Set Node ("Set number of discs")
- Set parameter:
numberOfDiscs= 4 (Number type) - Connect "Start" → "Set number of discs".
- Set parameter:
-
Create Code Node ("Create stacks")
- JavaScript:
- Enforce min 1 and max 5 discs.
- Initialize
stackAwith discs [numberOfDiscs ... 1]. - Initialize empty
stackBandstackC. - Initialize empty
logsarray.
- Connect "Set number of discs" → "Create stacks".
- JavaScript:
-
Create Execute Workflow Node ("A B C")
- Set to call same workflow (recursive call).
- Pass inputs:
numberOfDiscsstackX=stackAstackY=stackBstackZ=stackClogs
- Enable "Wait for sub-workflow" to true.
- Connect "Create stacks" → "A B C".
-
Create If Node ("Max 1 disc")
- Condition:
numberOfDiscs <= 1(number comparison) - Connect "A B C" → "Max 1 disc".
- Condition:
-
Create Code Node ("X to Z")
- JavaScript:
const disc = $json.stackX.items.pop(); $json.stackZ.items.push(disc); $json.logs.push(`${$json.stackX.name} ${$json.stackZ.name} ${disc}`); return $input.item; - Connect "Max 1 disc" True branch → "X to Z".
- JavaScript:
-
Create Execute Workflow Node ("Y X Z")
- Set to call same workflow recursively.
- Pass inputs:
numberOfDiscs= currentnumberOfDiscs - 1stackX= previousstackYstackY= previousstackXstackZ= previousstackZlogs
- Wait for sub-workflow result.
- Connect "X to Z" → "Y X Z".
-
Create Code Node ("Update")
- JavaScript:
$json.numberOfDiscs += 1; const stack = $json.stackY; $json.stackY = $json.stackX; $json.stackX = stack; return $input.item; - Connect "Y X Z" → "Update".
- JavaScript:
-
Create Code Node (" X to Z") (note the leading space in name)
- Same code as first "X to Z" node for moving disc and logging.
- Connect "Update" → " X to Z".
-
Create Execute Workflow Node ("X Z Y")
- Set to call same workflow recursively.
- Pass inputs:
numberOfDiscs= currentnumberOfDiscs - 1stackX= previousstackXstackY= previousstackZstackZ= previousstackYlogs
- Wait for sub-workflow result.
- Connect "Max 1 disc" False branch → "X Z Y".
-
Create Code Node ("Update") (second instance)
- JavaScript:
$json.numberOfDiscs += 1; const stack = $json.stackY; $json.stackY = $json.stackZ; $json.stackZ = stack; return $input.item; - Connect "X Z Y" → this "Update".
- JavaScript:
-
Connect "Update" → " X to Z" (the space-prefixed node).
-
Create Code Node ("Solution")
- JavaScript:
function logsToSentence(logs) { return logs.map((entry, index) => { const [from, to, disc] = entry.split(" "); let text; if (index === 0) { text = `Move disc ${disc} from ${from} to ${to}`; } else if (index === 1) { text = `then ${from} ${disc}→ ${to}`; } else { text = `${from} ${disc}→ ${to}`; } text += ","; if (index === 0 || index % 2 === 1) { text += "\n"; } else { text += " "; } return text; }).join("").slice(0, -2) + "."; } return { solution: logsToSentence($input.first().json.logs) }; - Connect "A B C" → "Solution".
- JavaScript:
-
Add Sticky Notes for documentation as needed.
5. General Notes & Resources
| Note Content | Context or Link |
|---|---|
| ## Towers of Hanoi This workflow demonstrates recursion in n8n using sub-workflows to solve the Towers of Hanoi puzzle. ### Algorithm procedure Hanoi(n, X, Y, Z):<br> if n == 1:<br> move disk from X to Z<br> else:<br> Hanoi(n-1, X, Z, Y)<br> move disk from X to Z<br> Hanoi(n-1, Y, X, Z)<br>Notes: - Variables X, Y, Z represent rods; swapping them adjusts positions. - Recursive calls return updated stacks, requiring stack resetting nodes. - Termination condition is important to avoid infinite recursion. - Links: - n8n Dashboard Restart workspace - GitHub Workflow repo - Recursion Wikipedia (Towers of Hanoi) |
Main workflow sticky note with full description and resources. |
Disclaimer: The provided text is solely derived from an automated workflow created using n8n, a workflow automation tool. This processing strictly adheres to content policies and contains no illegal, offensive, or protected material. All data handled is legal and public.