Introduction edit
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.}
Code edit
# 15puzzle_35.tcl - HaJo Gurt - 2016-02-21
# http://wiki.tcl.tk/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 0Data edit
This 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
# http://wiki.tcl.tk/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...Comments edit
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:
- The Classic 15 Puzzle - N-puzzle
- Fifteen puzzle online
(JavaScript) - ...

