| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| package require Tk 8.5 |
|
|
| |
| proc ValidMoves {square} { |
| set moves {} |
| foreach pair {{-1 -2} {-2 -1} {-2 1} {-1 2} {1 2} {2 1} {2 -1} {1 -2}} { |
| set col [expr {($square % 8) + [lindex $pair 0]}] |
| set row [expr {($square / 8) + [lindex $pair 1]}] |
| if {$row > -1 && $row < 8 && $col > -1 && $col < 8} { |
| lappend moves [expr {$row * 8 + $col}] |
| } |
| } |
| return $moves |
| } |
|
|
| |
| proc CheckSquare {square} { |
| variable visited |
| set moves 0 |
| foreach test [ValidMoves $square] { |
| if {[lsearch -exact -integer $visited $test] == -1} { |
| incr moves |
| } |
| } |
| return $moves |
| } |
|
|
| |
| |
| proc Next {square} { |
| variable visited |
| set minimum 9 |
| set nextSquare -1 |
| foreach testSquare [ValidMoves $square] { |
| if {[lsearch -exact -integer $visited $testSquare] == -1} { |
| set count [CheckSquare $testSquare] |
| if {$count < $minimum} { |
| set minimum $count |
| set nextSquare $testSquare |
| } elseif {$count == $minimum} { |
| |
| |
| set nextSquare [Edgemost $nextSquare $testSquare] |
| } |
| } |
| } |
| return $nextSquare |
| } |
|
|
| |
| proc Edgemost {a b} { |
| set colA [expr {3-int(abs(3.5-($a%8)))}] |
| set colB [expr {3-int(abs(3.5-($b%8)))}] |
| set rowA [expr {3-int(abs(3.5-($a/8)))}] |
| set rowB [expr {3-int(abs(3.5-($b/8)))}] |
| return [expr {($colA * $rowA) < ($colB * $rowB) ? $a : $b}] |
| } |
|
|
| |
| proc N {square} { |
| return [format %c%d [expr {97 + $square % 8}] \ |
| [expr {$square / 8 + 1}]] |
| } |
|
|
| |
| proc MovePiece {dlg last square} { |
| variable visited |
| variable delay |
| variable continuous |
| $dlg.f.txt insert end "[llength $visited]. [N $last] .. [N $square]\n" {} |
| $dlg.f.txt see end |
| $dlg.f.c itemconfigure [expr {1+$last}] -state normal -outline black |
| $dlg.f.c itemconfigure [expr {1+$square}] -state normal -outline red |
| $dlg.f.c moveto knight {*}[lrange [$dlg.f.c coords [expr {1+$square}]] 0 1] |
| lappend visited $square |
| set next [Next $square] |
| if {$next ne -1} { |
| variable aid [after $delay [list MovePiece $dlg $square $next]] |
| } else { |
| $dlg.tf.b1 configure -state normal |
| if {[llength $visited] == 64} { |
| variable initial |
| if {$initial == $square} { |
| $dlg.f.txt insert end "Closed tour!" |
| } else { |
| $dlg.f.txt insert end "Success\n" {} |
| if {$continuous} { |
| after [expr {$delay * 2}] [namespace code \ |
| [list Tour $dlg [expr {int(rand() * 64)}]]] |
| } |
| } |
| } else { |
| $dlg.f.txt insert end "FAILED!\n" {} |
| } |
| } |
| } |
|
|
| |
| proc Tour {dlg {square {}}} { |
| variable visited {} |
| $dlg.f.txt delete 1.0 end |
| $dlg.tf.b1 configure -state disabled |
| for {set n 0} {$n < 64} {incr n} { |
| $dlg.f.c itemconfigure $n -state disabled -outline black |
| } |
| if {$square eq {}} { |
| set coords [lrange [$dlg.f.c coords knight] 0 1] |
| set square [expr {[$dlg.f.c find closest {*}$coords 0 65]-1}] |
| } |
| variable initial $square |
| after idle [list MovePiece $dlg $initial $initial] |
| } |
|
|
| proc Stop {} { |
| variable aid |
| catch {after cancel $aid} |
| } |
|
|
| proc Exit {dlg} { |
| Stop |
| destroy $dlg |
| } |
|
|
| proc SetDelay {new} { |
| variable delay [expr {int($new)}] |
| } |
|
|
| proc DragStart {w x y} { |
| $w dtag selected |
| $w addtag selected withtag current |
| variable dragging [list $x $y] |
| } |
| proc DragMotion {w x y} { |
| variable dragging |
| if {[info exists dragging]} { |
| $w move selected [expr {$x - [lindex $dragging 0]}] \ |
| [expr {$y - [lindex $dragging 1]}] |
| variable dragging [list $x $y] |
| } |
| } |
| proc DragEnd {w x y} { |
| set square [$w find closest $x $y 0 65] |
| $w moveto selected {*}[lrange [$w coords $square] 0 1] |
| $w dtag selected |
| variable dragging ; unset dragging |
| } |
|
|
| proc CreateGUI {} { |
| catch {destroy .knightstour} |
| set dlg [toplevel .knightstour] |
| wm title $dlg "Knights tour" |
| wm withdraw $dlg |
| set f [ttk::frame $dlg.f] |
| set c [canvas $f.c -width 240 -height 240] |
| text $f.txt -width 10 -height 1 -background white \ |
| -yscrollcommand [list $f.vs set] -font {Arial 8} |
| ttk::scrollbar $f.vs -command [list $f.txt yview] |
|
|
| variable delay 600 |
| variable continuous 0 |
| ttk::frame $dlg.tf |
| ttk::label $dlg.tf.ls -text Speed |
| ttk::scale $dlg.tf.sc -from 8 -to 2000 -command [list SetDelay] \ |
| -variable [namespace which -variable delay] |
| ttk::checkbutton $dlg.tf.cc -text Repeat \ |
| -variable [namespace which -variable continuous] |
| ttk::button $dlg.tf.b1 -text Start -command [list Tour $dlg] |
| ttk::button $dlg.tf.b2 -text Exit -command [list Exit $dlg] |
| set square 0 |
| for {set row 7} {$row != -1} {incr row -1} { |
| for {set col 0} {$col < 8} {incr col} { |
| if {(($col & 1) ^ ($row & 1))} { |
| set fill tan3 ; set dfill tan4 |
| } else { |
| set fill bisque ; set dfill bisque3 |
| } |
| set coords [list [expr {$col * 30 + 4}] [expr {$row * 30 + 4}] \ |
| [expr {$col * 30 + 30}] [expr {$row * 30 + 30}]] |
| $c create rectangle $coords -fill $fill -disabledfill $dfill \ |
| -width 2 -state disabled |
| } |
| } |
| if {[tk windowingsystem] ne "x11"} { |
| catch {eval font create KnightFont -size -24} |
| $c create text 0 0 -font KnightFont -text "\u265e" \ |
| -anchor nw -tags knight -fill black -activefill "#600000" |
| } else { |
| |
| |
| set pts { |
| 2 25 24 25 21 19 20 8 14 0 10 0 0 13 0 16 |
| 2 17 4 14 5 15 3 17 5 17 9 14 10 15 5 21 |
| } |
| $c create polygon $pts -tag knight -offset 8 \ |
| -fill black -activefill "#600000" |
| } |
| $c moveto knight {*}[lrange [$c coords [expr {1 + int(rand() * 64)}]] 0 1] |
| $c bind knight <ButtonPress-1> [namespace code [list DragStart %W %x %y]] |
| $c bind knight <Motion> [namespace code [list DragMotion %W %x %y]] |
| $c bind knight <ButtonRelease-1> [namespace code [list DragEnd %W %x %y]] |
|
|
| grid $c $f.txt $f.vs -sticky news |
| grid rowconfigure $f 0 -weight 1 |
| grid columnconfigure $f 1 -weight 1 |
|
|
| grid $f - - - - - -sticky news |
| set things [list $dlg.tf.ls $dlg.tf.sc $dlg.tf.cc $dlg.tf.b1] |
| if {![info exists ::widgetDemo]} { |
| lappend things $dlg.tf.b2 |
| if {[tk windowingsystem] ne "aqua"} { |
| set things [linsert $things 0 [ttk::sizegrip $dlg.tf.sg]] |
| } |
| } |
| pack {*}$things -side right |
| if {[tk windowingsystem] eq "aqua"} { |
| pack configure {*}$things -padx {4 4} -pady {12 12} |
| pack configure [lindex $things 0] -padx {4 24} |
| pack configure [lindex $things end] -padx {16 4} |
| } |
| grid $dlg.tf - - - - - -sticky ew |
| if {[info exists ::widgetDemo]} { |
| grid [addSeeDismiss $dlg.buttons $dlg] - - - - - -sticky ew |
| } |
|
|
| grid rowconfigure $dlg 0 -weight 1 |
| grid columnconfigure $dlg 0 -weight 1 |
|
|
| bind $dlg <Control-F2> {console show} |
| bind $dlg <Return> [list $dlg.tf.b1 invoke] |
| bind $dlg <Escape> [list $dlg.tf.b2 invoke] |
| bind $dlg <Destroy> [namespace code [list Stop]] |
| wm protocol $dlg WM_DELETE_WINDOW [namespace code [list Exit $dlg]] |
|
|
| wm deiconify $dlg |
| tkwait window $dlg |
| } |
|
|
| if {![winfo exists .knightstour]} { |
| if {![info exists widgetDemo]} { wm withdraw . } |
| set r [catch [linsert $argv 0 CreateGUI] err] |
| if {$r} { |
| tk_messageBox -icon error -title "Error" -message $err |
| } |
| if {![info exists widgetDemo]} { exit $r } |
| } |
|
|