Visual solution to the classic Towers of Hanoi puzzle.
import Diagrams.Backend.SVG.CmdLine
{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Prelude
import Data.List
import Data.Colour.SRGB (sRGB24read)
type Dia = Diagram B
First, some colors for our disks, and types to represent the data structures involved.
= cycle $ map sRGB24read [ "#9FB4CC", "#CCCC9F", "#DB4105", "#FFF8E3", "#33332D"]
colors
type Disk = Int
type Stack = [Disk]
type Hanoi = [Stack]
type Move = (Int,Int)
To render a single disk, draw a rectangle with width proportional to
its disk number, using a color selected from the colors
list.
renderDisk :: Disk -> Dia
= rect (fromIntegral n + 2) 1
renderDisk n # lc black
# lw thin
# fc (colors !! n)
To render a stack of disks, just stack their renderings on top of a
drawing of a peg. We use alignB
to place stack of disks at the
bottom of the peg.
renderStack :: Stack -> Dia
= disks `atop` post
renderStack s where disks = (vcat . map renderDisk $ s)
# alignB
= rect 0.8 6
post # lw none
# fc black
# alignB
Finally, to render a collection of stacks, lay them out
horizontally, using the Distrib
method so the pegs end up spaced
evenly no matter the width of the disks on any particular peg.
renderHanoi :: Hanoi -> Dia
= hcat' (with & catMethod .~ Distrib & sep .~ 7) . map renderStack renderHanoi
Now some code to actually solve the puzzle, generating a list of moves which are then used to simulate the solution and generate a list of configurations.
solveHanoi :: Int -> [Move]
= solveHanoi' n 0 1 2
solveHanoi n where solveHanoi' 0 _ _ _ = []
= solveHanoi' (n-1) a c b ++ [(a,c)]
solveHanoi' n a b c ++ solveHanoi' (n-1) b a c
doMove :: Move -> Hanoi -> Hanoi
= h''
doMove (x,y) h where (d,h') = removeDisk x h
= addDisk y d h'
h'' = (head (h!!x), modList x tail h)
removeDisk x h = modList y (d:)
addDisk y d
= let (xs,(y:ys)) = splitAt i l in xs ++ (f y : ys)
modList i f l
hanoiSequence :: Int -> [Hanoi]
= scanl (flip ($)) [[0..n-1], [], []] (map doMove (solveHanoi n)) hanoiSequence n
Finally, we render a sequence of configurations representing a solution by laying them out vertically.
renderHanoiSeq :: [Hanoi] -> Dia
= vcat' (with & sep .~2) . map renderHanoi
renderHanoiSeq
= pad 1.1 $ renderHanoiSeq (hanoiSequence 4) # centerXY example
= mainWith (example :: Diagram B) main