Version 0 of Hanoi Towers

Updated 2005-01-04 15:03:36

Sarnold A classical recursivity example as a small game.

The goal is to move entierly a tower from one pit to another. The tower is formed by 3 to 5 discs, which reside on a pit. There are three pits, and you can move only one disc at a time, but not over a smaller disc.


#! /usr/bin/wish

 # Nous fonctionnons en mode graphique
 package require Tk

 proc stackMove {numberOfDiscs from to temporary} {
     if {$numberOfDiscs==1} {
         oneMove $from $to
         return
     }
     stackMove [expr {$numberOfDiscs-1}] $from $temporary $to
     oneMove $from $to
     stackMove [expr {$numberOfDiscs-1}] $temporary $to $from
 }

 proc drawStacks {} {
     foreach i {1 2 3} {
         DrawStack $i
     }
 }

 proc DrawStack {stackIndex} {
     # remove all existing discs
     .stacks.tower$stackIndex delete all
     .stacks.tower$stackIndex create rectangle 120 30 130 400 -tags tower -fill #777
     .stacks.tower$stackIndex create rectangle 0 380 250 400 -tags base -fill #777

     set Discs $::discs($stackIndex)
     if {[llength $Discs]==1} {
         return
     }
     set Discs [lrange $Discs 1 end]
     set discIndex 0
     foreach n $Discs {
         # n is the size of the disc
         # discIndex is the position (the highest to the top)
         .stacks.tower$stackIndex create rectangle [expr {125-$n*110/$::nbDiscs}] \
                 [expr {-$discIndex*25+375}] [expr {125+$n*110/$::nbDiscs}] \
                 [expr {-$discIndex*25+365}] -fill #55C
         incr discIndex
     }
 }

 proc init {} {
     frame .stacks -width 750 -height 500
     pack .stacks -side top
     foreach i {1 2 3} {
         set tower .stacks.tower
         canvas $tower$i -width 250 -height 400
         pack $tower$i -in .stacks -side left
         $tower$i create rectangle 120 30 130 400 -tags tower -fill #777
         $tower$i create rectangle 0 380 250 400 -tags base -fill #777
     }
     frame .command -width 750 -height 60
     pack .command -side bottom
     set ::nbDiscs 3
     label .command.labelNbDiscs -text "Nb Discs :"
     pack .command.labelNbDiscs -side left
     spinbox .command.nbDiscs -textvariable ::nbDiscs\
             -values {3 4 5} -state normal
     pack .command.nbDiscs -side left
     checkbutton .command.computer -variable ::computer
     pack .command.computer -side left
     label .command.automatic -text "automatic"
     pack .command.automatic -side left
     button .command.go -text "Go!" -command {Begin}
     pack .command.go -side right

 }

 proc discInit {} {
     set n $::nbDiscs
     incr n
     foreach stack {1 2 3} {
         set ::discs($stack) [list $n]
     }
     incr n -1
     for {set width $n} {$width>0} {incr width -1} {
         lappend ::discs(1) $width
     }
     drawStacks
 }

 proc Begin {} {
     discInit
     drawStacks
     if {$::computer} {
         stackMove $::nbDiscs 1 2 3
     }
     # Destroy .command
     # Destroy .stacks
     # init
 }

 proc Destroy {args} {
     if {[llength $args]==0} {
         return
     }
     foreach i $args {
         set children [winfo children $i]
         if {[llength $children]==0} {
             destroy $i
         } else {
             Destroy $children
             destroy $i
         }
     }
 }

 proc oneMove {from to} {
     if {[llength $::discs($from)]==1} {
         error "stack no. $from is void"
     }
     set discWidth [lindex $::discs($from) end]
     if {$discWidth>[lindex $::discs($to) end]} {
         error "disc width overflow in destination"
     }
     lappend ::discs($to) $discWidth
     set ::discs($from) [lrange $::discs($from) 0 end-1]
     update
     after 600
     drawStacks
 }
 array set discs {}
 init

Category Games