#!/usr/bin/wish
# puzzle.tcl

set backgroundcolor #b8a58e
set bordercolor #cdb89e
lappend buttoncolors #d6beb6 #d6c5b6 #d6cdb6 #d6d4b6 #d0d6b6
lappend buttoncolors #c9d6b6 #c1d6b6 #bad6b6 #b6d6ba #b6d6c1
lappend buttoncolors #b6d6c9 #b6d6d0 #b6d4d6 #b6cdd6 #b6c5d6

######################################################################
##  P R O C E D U R E S                                             ##
######################################################################

######################################################################
#                                                         LIST SHUFFLE
# shuffles the elements of a list
#
proc lshuffle {list} {
  set len [llength $list]
  while {$len > 0} {
    set rand [expr int(rand()*$len)]
    lappend res [lindex $list $rand]
    set list [lreplace $list $rand $rand]
    incr len -1
  }
  return $res
}

######################################################################
#                                                        PLACE BUTTONS
# places all buttons in a given order
# 0 may stand for the empty space
#
proc placebuttons {order} {
  global pos
  set pos(0,x) 0.75
  set pos(0,y) 0.75
  for {set i 0} {$i < 16} {incr i} {
    set num [lindex $order $i]
    set pos($num,x) [expr ($i%4)*.25]
    set pos($num,y) [expr ($i/4)*.25]
    if {$num > 0} {
      place .frame.$num -relx $pos($num,x) -rely $pos($num,y) \
        -relwidth .25 -relheight .25
    }
  }
}

######################################################################
#                                                        SWITCH BUTTON
# invoked when the user clicks on a particular button
# if the button is next to the empty space, it is moved into it
#
proc switchbutton {num} {
  global pos
  if {(($pos($num,y) >= ($pos(0,y) - .01))
   && ($pos($num,y) <= ($pos(0,y) + .01))
   && ($pos($num,x) >= ($pos(0,x) - .26))
   && ($pos($num,x) <= ($pos(0,x) + .26)))
  || (($pos($num,x) >= ($pos(0,x) - .01))
   && ($pos($num,x) <= ($pos(0,x) + .01))
   && ($pos($num,y) >= ($pos(0,y) - .26))
   && ($pos($num,y) <= ($pos(0,y) + .26)))} {
    set tmp $pos(0,x)
    set pos(0,x) $pos($num,x)
    set pos($num,x) $tmp
    set tmp $pos(0,y)
    set pos(0,y) $pos($num,y)
    set pos($num,y) $tmp
    place .frame.$num -relx $pos($num,x) -rely $pos($num,y)
  }
}

######################################################################
##  M A I N                                                         ##
######################################################################

wm title . "Puzzle"
wm resizable . 0 0
. config -bg $bordercolor

# create frame
frame .frame -width 120 -height 120 \
  -relief sunken -borderwidth 1 -bg $backgroundcolor
pack .frame -side top -pady 15 -padx 15

# create buttons
for {set i 1} {$i < 16} {incr i} {
  set col [lindex $buttoncolors [expr $i -1]]
  label .frame.$i -text $i -relief raised -borderwidth 1 \
    -cursor hand2 -background $col -activebackground $col
  bind .frame.$i <Button-1> "switchbutton $i"
}

set numbers {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
bind . <F7> {placebuttons $numbers}
bind . <F8> {placebuttons [lshuffle $numbers]}
bind . <Alt-q> exit

placebuttons [lshuffle $numbers]

# END
