if 0 {
HJG 2016-02-16: There is a 15-puzzle in the Tk-demo. It uses buttons for the tiles and place to move the buttons around.
Other variants here on the wiki are The Classic 15 Puzzle and N-puzzle, but they are quite lengthy.
Both programs use numbers - it would be nice to have a variant that can use other symbols and/or a sliced-up picture.
Currently, there is a draft-task for the 15-puzzle at rosettacode , and I wanted
to fill in an entry for tcl, with a fairly short and simple version of this 15-puzzle.
Starting with the layout from A small calculator, I'm using a grid of buttons here,
and moving around the text on the buttons.
There is one builtin puzzle, and more puzzles can be loaded from an external file.
}
# 15puzzle_35.tcl - HaJo Gurt - 2016-02-21
# https://wiki.tcl-lang.org/14403
#: 15-Puzzle - with grid, buttons and colors,
# and more puzzle-data from source.
package require Tk
set progVersion "15-Puzzle v0.35"; # 2016-02-21
global Msg Moves PuzzNr GoalNr
set Msg $progVersion
set Moves 0
set PuzzNr 0
set GoalNr 0
set Keys { 11 12 13 14 21 22 23 24 31 32 33 34 41 42 43 44 }
set Puzz(T) "TheFifteenPuzzle"; # Title
set Goal(T) "...Fifteen......"; # Title-highlight
set Goal(0) "ABCDEFGHIKLMNOP_"; # Rows LTR / 1:E : 108
set Goal(1) "AEINBFKOCGLPDHM_"; # Cols forw. / 1:M : 114
set Puzz(0) "CAFBEGPNDLHIOKM_"; # E / 156 from Tk-demo
#set Puzz(1) "EGPNCAFBDLHIOKM_"; # moved to 4x4_puzz.tcl
if { [catch { source 4x4_puzz.tcl } ] } {
bell
set Msg "No puzzle-file"
}
set Puzzle $Puzz(T)
set Goal_ $Goal(T)
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+---
proc Move {k} {
# find the key with the empty tile:
set e -1
foreach p $::Keys {
set t [.key$p cget -text]
if { $t eq "_" } { set e $p }
}
if {$e < 0} {return 0}; # no key with empty tile found
if {$k == $e} {return 0}; # click was on the empty tile
set t [.key$k cget -text]
.key$e config -text $t
.key$k config -text "_";
return 1
}
proc Check {} {
set ok 0
set i 0
foreach k $::Keys {
set t [.key$k cget -text]
set g [string index $::Goal_ $i]
incr i
.key$k config -background white
if { $t eq $g } { .key$k config -background lightgreen; incr ok }
if { $t eq "_" } { .key$k config -background gray }
}
# Solved:
update
if { $ok > 15 && $::Moves > 0} {
foreach k $::Keys {
.key$k flash; bell;
}
}
}
proc Click {k} {
set ::Msg ""
set val [.key$k cget -text]
set ok [Move $k]
incr ::Moves $ok
wm title . "$::Moves moves"
Check
}
proc ShowKeys {} {
set i 0
foreach k $::Keys {
set t [string index $::Puzzle $i]
incr i
.key$k config -text $t -background white;
}
Check
}
proc NewGame {N} {
global Msg Moves PuzzNr GoalNr
set ::Goal_ $::Goal(0);
if { $GoalNr == 1} { set ::Goal_ $::Goal(1); }
incr PuzzNr $N
if { [catch { set ::Puzzle $::Puzz($PuzzNr)} ] } {
wm title . "No puzzle $PuzzNr !"
bell; after 333
set PuzzNr 0;
set ::Puzzle $::Puzz($PuzzNr)
}
if { $N==0 } {
set Msg "Try again"
} else {
set Msg "New game"
}
set Moves 0
ShowKeys
wm title . "$Msg #$PuzzNr"
}
#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+---
button .reset -text "Restart" -fg blue -command {NewGame 0}; # same puzzle
button .newGame -text "New Game" -fg red -command {NewGame +1}; # next puzzle
bind .newGame <3> {NewGame -1}; # Rightclick : previous puzzle
bind .newGame <Shift-Button-1> {set PuzzNr 0}; # Shift-click: select puzzle #1
foreach k $::Keys {
button .key$k -text "$k" -width 4 -command "Click $k"
}
grid .newGame x .reset x -sticky nsew -pady 4
grid .key11 .key12 .key13 .key14 -sticky nsew -padx 1 -pady 1
grid .key21 .key22 .key23 .key24 -sticky nsew -padx 1 -pady 1
grid .key31 .key32 .key33 .key34 -sticky nsew -padx 1 -pady 1
grid .key41 .key42 .key43 .key44 -sticky nsew -padx 1 -pady 1
grid configure .newGame .reset -columnspan 2 -padx 15
ShowKeys
wm title . $Msg
focus -force .
wm resizable . 0 0This optional file provides additional puzzles, and gets included in the above program via source.
If this file is missing, the main program runs with just the one builtin puzzle.
# 4x4_puzz.tcl - 2016-02-20
# https://wiki.tcl-lang.org/14403
if {![info exists Puzz]} {
error "This script only provides data for the program '15puzzle.tcl'.\n\n"
}
### Puzzle-data for 15puzzle_35.tcl :
#set Puzz(0) "CAFBEGPNDLHIOKM_"; # E / builtin, from Tk-demo
set Puzz(1) "EGPNCAFBDLHIOKM_"; # - / 116
set Puzz(2) "EONKMI_GBHLPCFAD"; # L / 133
set Puzz(3) "PGM_ELNDOKHIBCFA"; # EK / 146
set Puzz(4) "ABIKCDLMEFNOGHP_"; # ABP / 98
set Puzz(5) "IKAB_PCDLMEFNOGH"; # NO / 61
#...more puzzles...HJG The game is working fine, but there is no check if the clicked button is next to the empty tile.
I wanted to take care of that with '-state disabled' / '-state normal', but doing that resulted in some strange effects...
As-is, you have the choice to play it by 'sliding' or 'swapping'.
I think this a valid variant to play - find the perfect swapping-sequence (akin to Tower-of-Hanoi).
The button "New game" selects the next game, "Restart" starts the same puzzle again.
Rightclick on "New game" selects the previous puzzle, and a shift-click goes back to puzzle #1.
I changed from lists to strings, and there is an include-file now, with more puzzles.
A puzzle-generator (to-be-written) could just append lines to that file.
See also: