diff --git a/workflows/Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo-8656/readme-8656.md b/workflows/Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo-8656/readme-8656.md new file mode 100644 index 000000000..c776c26ba --- /dev/null +++ b/workflows/Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo-8656/readme-8656.md @@ -0,0 +1,372 @@ +Implement Recursive Algorithms with Sub-workflows: Towers of Hanoi Demo + +https://n8nworkflows.xyz/workflows/implement-recursive-algorithms-with-sub-workflows--towers-of-hanoi-demo-8656 + + +# 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 `numberOfDiscs` to 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`, `stackC` objects with `name` and `items` arrays. + - `stackA.items` contains discs [numberOfDiscs ... 1]. + - `stackB.items` and `stackC.items` are empty arrays. + - Logs array is empty. + - Input: Receives `numberOfDiscs` from 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). + - 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 to `stackZ.items`. + - Appends a string `"stackX.name stackZ.name disc"` to logs. + - Input: Receives stacks and logs from `Max 1 disc` true 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 - 1` and stacks reordered to (stackX, stackZ, stackY). + - Input: Receives updated stacks and logs from `Max 1 disc` false branch. + - Output: Passes results to `Update` node. + - Edge cases: Recursive calls may cause stack depth issues. + + - **Y X Z** + - Type: Execute Workflow (Sub-workflow call) + - Role: Recursive call with `numberOfDiscs - 1` and stacks reordered to (stackY, stackX, stackZ). + - Input: Receives updated stacks and logs from `X to Z`. + - Output: Passes results to `Update` node. + - Edge cases: Same as other recursion calls. + + - **Update** (two instances: one after `X Z Y`, one after `Y X Z`) + - Type: Code + - Role: Corrects stack assignments and increments `numberOfDiscs` to 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`. + - Input: Receives results from respective sub-workflow calls. + - Output: Passes updated data back to `X to Z` or `Solution`. + - 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 `solution` string output. + - Edge cases: Empty logs or malformed entries may cause formatting issues. + +--- + +#### 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 + +1. **Create Manual Trigger Node ("Start")** + - Type: Manual Trigger + - No configuration needed. + +2. **Create Set Node ("Set number of discs")** + - Set parameter: `numberOfDiscs` = 4 (Number type) + - Connect "Start" → "Set number of discs". + +3. **Create Code Node ("Create stacks")** + - JavaScript: + - Enforce min 1 and max 5 discs. + - Initialize `stackA` with discs [numberOfDiscs ... 1]. + - Initialize empty `stackB` and `stackC`. + - Initialize empty `logs` array. + - Connect "Set number of discs" → "Create stacks". + +4. **Create Execute Workflow Node ("A B C")** + - Set to call same workflow (recursive call). + - Pass inputs: + - `numberOfDiscs` + - `stackX` = `stackA` + - `stackY` = `stackB` + - `stackZ` = `stackC` + - `logs` + - Enable "Wait for sub-workflow" to true. + - Connect "Create stacks" → "A B C". + +5. **Create If Node ("Max 1 disc")** + - Condition: `numberOfDiscs <= 1` (number comparison) + - Connect "A B C" → "Max 1 disc". + +6. **Create Code Node ("X to Z")** + - JavaScript: + ```js + 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". + +7. **Create Execute Workflow Node ("Y X Z")** + - Set to call same workflow recursively. + - Pass inputs: + - `numberOfDiscs` = current `numberOfDiscs - 1` + - `stackX` = previous `stackY` + - `stackY` = previous `stackX` + - `stackZ` = previous `stackZ` + - `logs` + - Wait for sub-workflow result. + - Connect "X to Z" → "Y X Z". + +8. **Create Code Node ("Update")** + - JavaScript: + ```js + $json.numberOfDiscs += 1; + const stack = $json.stackY; + $json.stackY = $json.stackX; + $json.stackX = stack; + return $input.item; + ``` + - Connect "Y X Z" → "Update". + +9. **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". + +10. **Create Execute Workflow Node ("X Z Y")** + - Set to call same workflow recursively. + - Pass inputs: + - `numberOfDiscs` = current `numberOfDiscs - 1` + - `stackX` = previous `stackX` + - `stackY` = previous `stackZ` + - `stackZ` = previous `stackY` + - `logs` + - Wait for sub-workflow result. + - Connect "Max 1 disc" False branch → "X Z Y". + +11. **Create Code Node ("Update")** (second instance) + - JavaScript: + ```js + $json.numberOfDiscs += 1; + const stack = $json.stackY; + $json.stackY = $json.stackZ; + $json.stackZ = stack; + return $input.item; + ``` + - Connect "X Z Y" → this "Update". + +12. **Connect "Update" → " X to Z"** (the space-prefixed node). + +13. **Create Code Node ("Solution")** + - JavaScript: + ```js + 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". + +14. **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):
if n == 1:
move disk from X to Z
else:
Hanoi(n-1, X, Z, Y)
move disk from X to Z
Hanoi(n-1, Y, X, Z)
```
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](https://app.n8n.cloud/manage)
- [GitHub Workflow repo](https://github.com/adibaba/n8n.workflows)
- [Recursion Wikipedia (Towers of Hanoi)](https://en.wikipedia.org/w/index.php?title=Recursion_(computer_science)&oldid=1301600240#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. \ No newline at end of file