How to draw Hanoi tower in Typst?

Hi All,

How to draw the following figure in Typst? Thank you.

In the Body section of How to post in the Questions category it suggests that you

Give a brief description, describe what you have tried and where you got stuck.

Please post any attempts you have made so far to do this.

If you don’t have code to share, do you have a preferred way of doing this? This could be done in pure Typst using boxes, or it could use Typst’s built in drawing functions. Or it could be achieved with a package such as CeTZ.

1 Like

Thank you for your feedback. Please see below my code. The output does not correspond to my wishes. Line A shall pass through the centers of below disks (disks of color yellow, green, red and blue). As seen below the disks are badly stacked. Thank you for your support.

#grid(
columns: (1fr, 1fr, 1fr),
stack(
  rect(width: 0.1pt, height: 150pt)[A],
  rect(width: 25%, height: 2%, radius: 10pt, fill: yellow),
  rect(width: 35%, height: 2%, radius: 10pt, fill: green),
  rect(width: 40%, height: 2%, radius: 10pt, fill: red),
  rect(width: 45%, height: 2%, radius: 10pt, fill: blue)
),
rect(width: 0.1pt, height: 205pt)[B],
rect(width: 0.1pt, height: 205pt)[C],
rect(width: 302pt, height: 0.1pt)
)

One way to continue with your implementation is to add the align property and use stroke for the bottom line:

#grid(
columns: (1fr, 1fr, 1fr),
align: center,
stroke: (bottom: 1pt),
stack(
  rect(width: 0.1pt, height: 150pt)[#pad(left: 8mm)[A]],
  rect(width: 25%, height: 2%, radius: 10pt, fill: yellow),
  rect(width: 35%, height: 2%, radius: 10pt, fill: green),
  rect(width: 40%, height: 2%, radius: 10pt, fill: red),
  rect(width: 45%, height: 2%, radius: 10pt, fill: blue)
),
rect(width: 0.1pt, height: 205pt)[#pad(left: 8mm)[B]],
rect(width: 0.1pt, height: 205pt)[#pad(left: 8mm)[B]],
)

Then you need to use #pad to align the three letters.

1 Like

Thank you very much for your support.

1 Like

Here’s another way that uses a couple functions:

#let pill(width, color) = rect(width: width, height: 20%, radius: 10pt, fill: color)

#let kebab(..args) = stack(
  line(angle: 90deg, length: 100% - (20% * args.pos().len())),
  ..args
)

#box(
  stroke: (bottom: 1pt, rest: 0pt),
  grid(
    columns: (1fr, 1fr, 1fr),
    rows: (auto, 1em, 3cm),
    align: center,

    [A], [B], [C],
    [],[],[],
    kebab(
      pill(25%, yellow),
      pill(35%, green),
      pill(40%, red),
      pill(45%, blue)
    ),
    kebab(),
    kebab()
  )
)
1 Like

And one more solution that uses the CeTZ package

// Import the CeTZ drawing library for creating geometric drawings
#import "@preview/cetz:0.4.0": *

// Function to draw the Tower of Hanoi puzzle visualization
// Parameters:
// - ..disks: Variable number of arrays representing disks on each tower (0-5)
#let hanoi(..disks) = canvas(length: 1cm, {
  import draw: *

  // Define colors for different disk sizes (6 different colors for up to 6 disks)
  // Colors range from pink to red, providing good visual distinction
  let colors = (
    rgb("#CB2A7B"), // Pink
    rgb("#00A1FF"), // Blue  
    rgb("#60D937"), // Green
    rgb("#FAE231"), // Yellow
    rgb("#FF9400"), // Orange
    rgb("#ED220D")  // Red
  )
  
  // Draw the base line where all three towers stand
  line((0, 0), (15, 0))
  
  // Draw the three towers (A, B, C)
  for tower_index in range(3) {
    // Calculate x-position for each tower (spaced 5 units apart, starting at x=3)
    let tower_x = 5 * tower_index + 3
    
    // Draw the vertical tower pole from base to top
    line((tower_x, 0), (tower_x, 3), name: "tower-" + str(tower_index))
    
    // Add tower label (A, B, C) below each tower
    content(
      "tower-" + str(tower_index) + ".start", 
      anchor: "north", 
      padding: 0.25, 
      strong(str.from-unicode(65 + tower_index)) // Convert 0,1,2 to A,B,C
    )

    // Draw all disks on the current tower
    for (disk_position, disk_size) in disks.at(tower_index).enumerate() {
      // Calculate disk width based on size (larger numbers = smaller disks)
      // Width decreases by 0.4 units for each size increment
      let disk_width = 3 - disk_size * 0.4
      
      // Calculate vertical position (stack disks on top of each other)
      let disk_y = disk_position * 0.4
      
      // Draw the disk as a colored rectangle centered on the tower
      rect(
        (tower_x - disk_width/2, disk_y),           // Bottom-left corner
        (tower_x + disk_width/2, disk_y + 0.4),    // Top-right corner  
        fill: colors.at(disk_size)                  // Color based on disk size
      )
    }
  }
})

// Example 1: Initial state - All 6 disks on tower A
#hanoi((0, 1, 2, 3, 4, 5), (), ())

// Example 2: Mid-game state - Disks distributed across all towers
#hanoi((2, 3, 4, 5), (1,), (0,))

// Example 3: Another mid-game configuration
#hanoi((4, 5), (0, 1, 2), (3,))

// Example 4: Final state - All disks moved to tower C
#hanoi((), (), (0, 1, 2, 3, 4, 5))

which results in a highly reusable code for different game states.

5 Likes

Thank you for your feedback.

1 Like

Thank you again for your support.