if 0 {[Richard Suchenwirth] 2005-03-01 - The puzzle "Das Haus vom Nikolaus" used to be popular with German pre-schoolers, while at the same time having a little graph-theoretical interest. [http://mini.net/files/nikolaus.gif] The task is to draw the "house of Santa Claus" in one continuous stroke, without lifting the pen, or drawing a line twice. As nodes A and B are the only ones with an odd number of incident edges, it is obvious that you must start at either, and end at the other. When drawing, it used to be habitual to say the eight-syllable phrase (because there are eight strokes to be drawn) de: Das ist das Haus vom Ni- ko- laus In other languages, these phrases might be used: en: This is the house of San- ta Claus fr: C'est la mai-son de Saint Ni- cole it: La ca- sa di San Ni- co- la nl: Dat is het huis van Sin- ter- klaas zh: Sheng Ni- gu- la di xiao fang-zi } package require Tk #-- A usage message is a good start (and will be used on "?") set about {House of Santa Claus Powered by Tcl/Tk! Richard Suchenwirth 2005 Draw the outlined house in one continuous move by clicking on the edge circles. Click "C" to reset. Click "<-" to undo the last move. Click "!" for a hint. } #-- Build the UI proc main {} { global g array set g { edges {AB AC AD BC BD CD CE DE} A {60 240} B {220 240} C {220 120} D {60 120} E {140 40} } pack [canvas .c -width 240 -height 260] .c create window 20 20 -window [button .c.? -width 2 -text ? \ -command {tk_messageBox -message $::about}] .c create window 20 50 -window [button .c.c -width 2 -text C \ -command {reset .c}] .c create window 20 80 -window [button .c.<- -width 2 -text <- \ -command {undo .c}] .c create window 20 110 -window [button .c.! -width 2 -text ! \ -command {hint .c}] foreach edge $g(edges) { foreach {from to} [split $edge ""] break .c create line [concat $g($from) $g($to)] -width 2 -fill white } foreach node [array names g ?] { node .c $node $g($node) } .c bind node <1> {clicknode .c} reset .c } #-- Back to square one :) proc reset w { global g foreach i [array names g incides,?] {unset g($i)} foreach edge $g(edges) { foreach {from to} [split $edge ""] break lappend g(incides,$from) $to lappend g(incides,$to) $from } $w delete line $w itemconfig node -fill yellow set g(stack) {} catch {unset g(last)} foreach event [after info] {after cancel $event} } #-- Draw a node as circle, with label proc node {w name pos} { foreach {x y} $pos break $w create oval [expr $x-5] [expr $y-5] [expr $x+5] [expr $y+5] \ -outline black -tag [list node $name] set tx [expr {$x<140? $x-12: $x>140? $x+12: $x}] set ty [expr {$y<120? $y-12: $y>120? $y+12: $y}] $w create text $tx $ty -text $name } #-- Called when a node is selected, by user or [[hint]] proc clicknode {w {node -}} { global g set id [$w find withtag [expr {$node eq "-"? "current": $node}]] set name [lindex [$w gettags $id] 1] if [info exists g(last)] { set last $g(last) if {$last eq "" || $last eq $name} {return 0} if {[lsearch $g(incides,$last) $name]>=0} { $w create line [concat $g($last) $g($name)] -width 5 \ -tag [list line $last$name] lappend g(stack) $last$name $name set g(last) $name $w itemconfig $last -fill yellow $w itemconfig $name -fill blue $w raise node lremove g(incides,$last) $name lremove g(incides,$name) $last if [done?] { tk_messageBox -message "Made it!" reset $w return 1 } } } else { set g(last) $name $w itemconfig $name -fill blue } return 0 } #-- Undo the last move proc undo w { global g $w itemconfig [lindex $g(stack) end] -fill yellow set g(last) [lindex $g(stack) end-2] $w itemconfig $g(last) -fill blue set lastedge [lindex $g(stack) end-1] $w delete $lastedge foreach {from to} [split $lastedge ""] break lappend g(incides,$from) $to lappend g(incides,$to) $from set g(stack) [lrange $g(stack) 0 end-2] if {[llength $g(stack)]==0} {unset g(last)} } #-- See if the puzzle is completed proc done? {} { foreach i [array names ::g incides,*] { if {[llength $::g($i)]} {return 0} } return 1 } #-- Demonstrate a possible solution proc hint w { reset $w set node [lpick {A B}] while {![done?]} { if [clicknode $w $node] break update idletasks after 1000 set node [lpick $::g(incides,$node)] if {$node eq ""} {after idle hint $w; break} } } #-- Generally useful routines: proc lremove {_list element} { upvar 1 $_list list set pos [lsearch $list $element] set list [lreplace $list $pos $pos] } proc lpick list { lindex $list [expr int(rand()*[llength $list])] } #-- Let the show begin! main #-- Debugging helpers: bind . {exec wish $argv0 &; exit} bind . {console show} if 0 { ---- [Category Toys] | [Arts and crafts of Tcl-Tk programming]}