#!/usr/local/bin/obmsh

# PUZZLE.GUI -- A simple number re-ordering puzzle, kinda like the one my
# Dad would make me play on long car trips to shut me up....
#
# History: 4/23/94 	- written (MJF)
#	   4/25/94	- Added timer and move counter labels

reset-server
appInitialize puzzle Puzzle {
    Puzzle*objects:\
	toplevel	Paned		puzzle\
	puzzle		Form		tForm\
	tForm		Label		timerLabel\
	tForm		Toggle		pauseButton\
	tForm		Label		movesLabel\
\
	puzzle		Form		pForm\
	pForm		Command		pos11\
	pForm		Command		pos12\
	pForm		Command		pos13\
	pForm		Command		pos14\
	pForm		Command		pos15\
	pForm		Command		pos21\
	pForm		Command		pos22\
	pForm		Command		pos23\
	pForm		Command		pos24\
	pForm		Command		pos25\
	pForm		Command		pos31\
	pForm		Command		pos32\
	pForm		Command		pos33\
	pForm		Command		pos34\
	pForm		Command		pos35\
	pForm		Command		pos41\
	pForm		Command		pos42\
	pForm		Command		pos43\
	pForm		Command		pos44\
	pForm		Command		pos45\
	pForm		Command		pos51\
	pForm		Command		pos52\
	pForm		Command		pos53\
	pForm		Command		pos54\
	pForm		Command		pos55\
\
	puzzle		Form		pCmd\
	pCmd		Command		newButton\
	pCmd		Command		quitButton

    *tForm*background:			bisque3
    *tForm*showGrip:			false
    *tForm*resizeable:			false
    *tForm*Label.background:		black
    *tForm*Label.foreground:		red
    *tForm*Label.shadowWidth:		0
    *tForm*Label.borderWidth:		0
    *tForm*Command.background:		grey80
    *tForm*Toggle.background:		grey80
    *pForm*background:			bisque3
    *pForm*showGrip:			false
    *pForm*resizeable:			false
    *pForm*Command.background:		grey80
    *pCmd*background:			bisque3
    *pCmd*Command.background:		grey80
    *pCmd*resizeable:			false

    *timerLabel.label:			0000
    *timerLabel.height:			30
    *timerLabel*font:                   *times-bold-r*24*
    *pauseButton.label:			Pause
    *pauseButton.height:		30
    *pauseButton.fromHoriz:		timerLabel
    *pauseButton.horizDistance:		10
    *pauseButton*font:                  *times-bold-r*18*
    *movesLabel.label:			0000
    *movesLabel.height:			30
    *movesLabel*font:                   *times-bold-r*24*
    *movesLabel.fromHoriz:		pauseButton
    *movesLabel.horizDistance:		10

    *pForm*width:			50
    *pForm*height:			50
    *pForm*horizDistance:		0
    *pForm*vertDistance:		0
    *pForm*Label.background:		bisque3
    *pForm*Label.shadowWidth:		0
    *pForm*Label.borderWidth:		0
    *pForm*font:                        *times-bold-r*24*

    *pos12.fromHoriz:			pos11
    *pos13.fromHoriz:			pos12
    *pos14.fromHoriz:			pos13
    *pos15.fromHoriz:			pos14

    *pos11.vertDistance:		2
    *pos12.vertDistance:		2
    *pos13.vertDistance:		2
    *pos14.vertDistance:		2
    *pos15.vertDistance:		2

    *pos21.fromVert:			pos11
    *pos21.horizDistance:		1
    *pos22.fromVert:			pos12
    *pos22.fromHoriz:			pos21
    *pos23.fromVert:			pos13
    *pos23.fromHoriz:			pos22
    *pos24.fromVert:			pos14
    *pos24.fromHoriz:			pos23
    *pos25.fromVert:			pos15
    *pos25.fromHoriz:			pos24

    *pos31.fromVert:			pos21
    *pos31.horizDistance:		1
    *pos32.fromHoriz:			pos31
    *pos32.fromVert:			pos22
    *pos33.fromVert:			pos23
    *pos33.fromHoriz:			pos32
    *pos34.fromVert:			pos24
    *pos34.fromHoriz:			pos33
    *pos35.fromVert:			pos25
    *pos35.fromHoriz:			pos34

    *pos41.fromVert:			pos31
    *pos41.horizDistance:		1
    *pos42.fromHoriz:			pos41
    *pos42.fromVert:			pos32
    *pos43.fromVert:			pos33
    *pos43.fromHoriz:			pos42
    *pos44.fromVert:			pos34
    *pos44.fromHoriz:			pos43
    *pos45.fromVert:			pos35
    *pos45.fromHoriz:			pos44

    *pos51.fromVert:			pos41
    *pos51.horizDistance:		1
    *pos52.fromHoriz:			pos51
    *pos52.fromVert:			pos42
    *pos53.fromVert:			pos43
    *pos53.fromHoriz:			pos52
    *pos54.fromVert:			pos44
    *pos54.fromHoriz:			pos53
    *pos55.fromVert:			pos45
    *pos55.fromHoriz:			pos54

    *pCmd*Command.height:		30
    *pCmd*Command.width:		50
    *pCmd*Command.vertDistance:		12
    *pCmd*Command*font:                 *times-bold-r*18*
    *newButton.label:			New Game
    *quitButton.fromHoriz:		newButton
    *quitButton.vertDistance:		5
    *quitButton.label:			Quit
}


# Crank 'er up...
createObjects
activate

# Should be a better way to randomize the start.
set space   	[ expr ( [pid] % 23 ) ]
set random  	"17 8 13 19 22 21 12 18 15 9 23 5 3 20 6 4 1 7 24 10 14 16 2 11"

# Quit the game.
proc Quit args { 
    global timerId

    if {[send pauseButton get state] == 0} {
        deleteTimedCallback $timerId
    }
    send client gkey q
    deactivate unmap 
} ; send quitButton addCallback Quit


# Initialize the game board.
proc Init args {
    global space random

    set k $space
    set ispace [ expr ($space / 5 + 1) ]
    set jspace [ expr ($space % 5 + 1) ]

    for { set i 1 } { $i<=5 } { incr i } {
        for { set j 1 } { $j<=5 } { incr j } {
	    set val [lindex $random $k]
	    if {[expr [expr $i == $ispace] && [expr $j == $jspace]]} {
		send pForm unmanage pos$i$j
	    } else {
	        send pos$i$j "set label $val"
	        set k [ expr ($k == 23) ? 0 : [expr $k+1] ]
	    }
        }
    }
} ; Init


# Define the game buttons and positions.
set buttons { pos11 pos12 pos13 pos14 pos15
	      pos21 pos22 pos23 pos24 pos25
	      pos31 pos32 pos33 pos34 pos35
	      pos41 pos42 pos43 pos44 pos45
	      pos51 pos52 pos53 pos54 pos55
}

set indx 0
for { set i 1 } { $i<=5 } { incr i } {
    for { set j 1 } { $j<=5 } { incr j } {
	set bpos(pos$i$j) "$i $j $indx"
	incr indx
    }
}

# Take action when hitting a button.
proc Move { widget args } {
    global space buttons bpos

    # If we've paused the game then don't move anything.
    if {[send pauseButton get state] == 1} {
	return
    }

    # Get the position of the empty space.
    set ispace [ expr ($space / 5 + 1) ]
    set jspace [ expr ($space % 5 + 1) ]

    # Get the position of the current widget.
    set i [ lindex $bpos($widget) 0]
    set j [ lindex $bpos($widget) 1]

    # Now see if the current widget is next to the empty space, but not on
    # a corner.
    set xdiff [expr abs ([expr $ispace-$i])]
    set ydiff [expr abs ([expr $jspace-$j])]
    if { ! [expr [expr $xdiff == 1] && [expr $ydiff == 1]]} {
        if {[expr [expr $xdiff < 2] && [expr $ydiff < 2]]} {

	    # Manage the empty space.
	    manage_space $space 

	    # Set space label to current label.
	    set lab [send $widget get label]
	    send [lindex $buttons $space] "set label $lab"

	    # Unmanage current space.
	    unmanage_space [lindex $bpos($widget) 2]
	    set space [lindex $bpos($widget) 2]

	    # Update the number of times we've made a move.
	    update_nmoves
        }
    }

} ; foreach item $buttons { send $item addCallback Move }


# Start a new game.
proc New_Game args {
    global space time nmoves timerId

    # If we've paused the game then don't do anything.
    if {[send pauseButton get state] == 1} {
        return
    }
    manage_space $space
    new_space
    Init
    set nmoves -1 ; update_nmoves
    set time 0
    deleteTimedCallback $timerId
    send timerLabel set label 0000
    set timerId [postTimedCallback timer 1000]
} ; send newButton addCallback New_Game


# Get a new empty space.
proc new_space args {
    global space random
    set space [lindex $random [ expr ($space >= 23) ? 0 : $space ] ]
    if {$space == 24} { set space 5 }
}


# Show a puzzle piece.
proc manage_space { space args } {
    set ispace [ expr ($space / 5 + 1) ]
    set jspace [ expr ($space % 5 + 1) ]
    send pForm manage pos$ispace$jspace
}


# Remove a puzzle piece.
proc unmanage_space { space args } {
    set ispace [ expr ($space / 5 + 1) ]
    set jspace [ expr ($space % 5 + 1) ]
    send pForm unmanage pos$ispace$jspace
}


# Install a timer so we can keep score of our best times.
set timerId 0
set time 0
proc timer args {
    global timerId time

    send timerLabel set label [ format "%04d" $time ]
    incr time
    set timerId [postTimedCallback timer 1000]
}; set timerId [postTimedCallback timer 1000]


# Count the number of times we move a piece.
set nmoves 0
proc update_nmoves args {
    global nmoves

    incr nmoves
    send movesLabel set label [ format "%04d" $nmoves ]
}


# Define a 'pause' command in case the boss walks in...
proc pause_toggle args {
    global timerId

    if {[send pauseButton get state] == 0} {
        set timerId [postTimedCallback timer 1000]
    } else {
	deleteTimedCallback $timerId
    }
} ; send pauseButton addCallback pause_toggle
