// $Id: HanoiGame.js,v 1.16 2010/08/05 22:01:38 pwh Exp $

function Graphics ()

{
   var i

   this.leftMargin = 26
   this.topMargin = 91
   this.blockWidth = 13
   this.blockHeight = 26
   this.halfHeight = Math.round ( this.blockHeight / 2 )

   this.pegTops = new Array()
   this.pegTopShadows = new Array()
   this.pegs = new Array()
   this.pegShadows = new Array()

   for ( i = 0; i < 3; ++i ) {

	this.pegTops[i] = new Image()
	this.pegTops[i].src = "images/PegTop.gif"
	this.pegTops[i].style.position = "absolute"
	this.pegTops[i].style.zIndex = 2

	this.pegTopShadows[i] = new Image()
	this.pegTopShadows[i].src = "images/Shadow-PegTop.gif"
	this.pegTopShadows[i].style.position = "absolute"
	this.pegTopShadows[i].style.zIndex = 1

	this.pegs[i] = new Image()
        this.pegs[i].src = "images/Peg.gif"
	this.pegs[i].style.position ="absolute"
	this.pegs[i].style.zIndex = 2

	this.pegShadows[i] = new Image()
        this.pegShadows[i].src = "images/Shadow-Peg.gif"
	this.pegShadows[i].style.position ="absolute"
	this.pegShadows[i].style.zIndex = 1
   }

   this.leftBase = new Image()
   this.leftBase.src = "images/LeftBase.gif"
   this.leftBase.style.position = "absolute"
   this.leftBase.style.zIndex = 2

   this.base = new Image()
   this.base.src = "images/Base.gif"
   this.base.style.position = "absolute"
   this.base.style.zIndex = 2

   this.rightBase = new Image()
   this.rightBase.src = "images/RightBase.gif"
   this.rightBase.style.position = "absolute"
   this.rightBase.style.zIndex = 2

   this.baseShadow = new Image()
   this.baseShadow.src = "images/Shadow-Base.gif"
   this.baseShadow.style.position = "absolute"
   this.baseShadow.style.zIndex = 1

   this.ground = new Image()
   this.ground.src = "images/Ground.gif"
   this.ground.style.position = "absolute"
   this.ground.style.zIndex = 1

   this.disks = new Array()
   this.diskShadows = new Array()

   for ( i = 0; i < 12; ++i ) {

	this.disks[i] = new Image()
	this.disks[i].id = "disk" + i

	if ( i % 2 ) this.disks[i].src = "images/MagentaDisk" + i + ".gif"
	else this.disks[i].src = "images/CyanDisk" + i + ".gif"

	this.disks[i].style.position = "absolute"
	this.disks[i].style.zIndex = 3
	this.disks[i].onclick = "onclick(event)"
	this.disks[i].setAttribute ( "onclick", "puzzle.grab(" + i + ")" )

	this.diskShadows[i] = new Image()
	this.diskShadows[i].id = "shadow" + i
	this.diskShadows[i].src = "images/Shadow" + i + ".gif"
	this.diskShadows[i].style.position = "absolute"
	this.diskShadows[i].style.zIndex = 1
   }
}


var graphics = new Graphics ()


function drawDisplay ( size )

{
   var i
   var currentLine = graphics.topMargin

   // Take the disks out.
   if ( this.size > 0 ) {

	for ( i= 0; i < this.size; ++i ) {

		this.gameFrame.removeChild ( graphics.disks[i] )
		this.gameFrame.removeChild ( graphics.diskShadows[i] )
	}
   }

   if ( size != this.size ) {

	var frameWidth = ( 6 * size + 17 ) * graphics.blockWidth
	var frameHeight = graphics.topMargin
				+ ( 2 * size + 1 ) * graphics.blockHeight

	var pegSpace = graphics.blockWidth * ( 2 * size + 3 )
	var currentColumn = graphics.leftMargin
					+ graphics.blockWidth * ( size + 2 )

	document.getElementById ("title").style.width = frameWidth + "px"
	document.getElementById ("size").selectedIndex = size - 1

	this.gameFrame.style.height = frameHeight + "px"
	this.gameFrame.style.width = frameWidth + "px"

	for ( i = 0; i < 3; ++i ) {

		graphics.pegTops[i].style.top = currentLine + "px"
		graphics.pegTops[i].style.left = currentColumn + "px"

		graphics.pegTopShadows[i].style.top
				= ( currentLine + graphics.halfHeight ) + "px"
		graphics.pegTopShadows[i].style.left
			= ( currentColumn + 3 * graphics.blockWidth ) + "px"

		currentColumn += pegSpace
	}

	currentLine += graphics.blockWidth
	currentColumn = graphics.leftMargin + graphics.blockWidth * ( size + 2 )

	for ( i = 0; i < 3; ++i ) {

		graphics.pegs[i].style.top = currentLine + "px"
		graphics.pegs[i].style.left = currentColumn + "px"
		graphics.pegs[i].style.height = ( size * graphics.blockHeight )
									+ "px"
		graphics.pegs[i].style.width = graphics.blockWidth + "px"

		graphics.pegShadows[i].style.top
				= ( currentLine + graphics.halfHeight ) + "px"
		graphics.pegShadows[i].style.left
			= ( currentColumn + 3 * graphics.blockWidth ) + "px"
		graphics.pegShadows[i].style.height
					= ( size * graphics.blockHeight ) + "px"
		graphics.pegShadows[i].style.width = graphics.blockWidth + "px"

		currentColumn += pegSpace
	}

	currentLine += ( size * graphics.blockHeight )
	currentColumn = graphics.leftMargin

	graphics.leftBase.style.top = currentLine + "px"
	graphics.leftBase.style.left = currentColumn + "px"

	currentColumn += graphics.blockWidth

	graphics.base.style.top = currentLine + "px"
	graphics.base.style.left = currentColumn + "px"
	graphics.base.style.height = graphics.blockHeight + "px"
	graphics.base.style.width = ( 3 * pegSpace ) + "px"

	currentColumn += ( 3 * pegSpace )

	graphics.rightBase.style.top = currentLine + "px"
	graphics.rightBase.style.left = currentColumn + "px"

	currentLine += graphics.halfHeight

	graphics.baseShadow.style.top = currentLine + "px"
	graphics.baseShadow.style.left = currentColumn + "px"

	currentLine += ( graphics.blockHeight - graphics.halfHeight )

	graphics.ground.style.top = currentLine + "px"
	graphics.ground.style.left = 0
	graphics.ground.style.height = ( graphics.blockHeight * size
						- graphics.blockWidth ) + "px"
	graphics.ground.style.width = "100%"

	this.size = size
   }

   currentLine = graphics.topMargin + graphics.blockHeight - graphics.halfHeight

   // Put the disks back in.
   for ( i = 0; i < size; ++i ) {

	graphics.disks[i].style.top = currentLine + "px"
	graphics.disks[i].style.left = ( graphics.leftMargin
				+ graphics.blockWidth * ( size - i ) ) + "px"
	this.gameFrame.appendChild ( graphics.disks[i] )

	currentLine += graphics.halfHeight

	graphics.diskShadows[i].style.top = currentLine + "px"
	graphics.diskShadows[i].style.left = ( graphics.leftMargin
			+ graphics.blockWidth * ( size + 3 - i ) ) + "px"
	this.gameFrame.appendChild ( graphics.diskShadows[i] )

	currentLine += ( graphics.blockHeight - graphics.halfHeight )
   }
}


function Display ( size )

{
   var i

   this.gameFrame = document.getElementById ("gameFrame")
   this.size = 0

   this.draw = drawDisplay

   this.draw ( size )

   for ( i = 0; i < 3; ++i ) {

	this.gameFrame.appendChild ( graphics.pegTops[i] )
	this.gameFrame.appendChild ( graphics.pegTopShadows[i] )
	this.gameFrame.appendChild ( graphics.pegs[i] )
	this.gameFrame.appendChild ( graphics.pegShadows[i] )
   }

   this.gameFrame.appendChild ( graphics.leftBase )
   this.gameFrame.appendChild ( graphics.base )
   this.gameFrame.appendChild ( graphics.rightBase )
   this.gameFrame.appendChild ( graphics.baseShadow )
   this.gameFrame.appendChild ( graphics.ground )
}


function mouseCoords (e)

{
   var x = e.pageX
   var y = e.pageY

   // Internet Explorer.
   if ( ( ! e.pageX ) && ( ! e.pageY ) ) {

	x = e.clientX
	y = e.clientY

	// Change the relative coordinates to absolute.
	if ( typeof ( window.pageYOffset ) == "number" ) {

		x += window.pageXOffset
		y += window.pageYOffset

	} else if ( document.body && ( document.body.scrollLeft
						|| document.body.scrollTop ) ) {

		x += document.body.scrollLeft
		y += document.body.scrollTop

	} else if ( document.documentElement
		&& ( document.documentElement.scrollLeft
				|| document.documentElement.scrollTop ) ) {

		x += document.documentElement.scrollLeft
		y += document.documentElement.scrollTop
	}
   }

   var gameFrame = document.getElementById ("gameFrame")
   x -= gameFrame.offsetLeft
   y -= gameFrame.offsetTop

   return { x:x, y:y }
}
 

function Disk ( index, width )

{
   this.index = index			// Self reference.

   // Used to detect if the disk is over a peg or potentially, collisions.
   this.width = Math.round ( ( width - 1 ) / 2 )

   // The coordinates of the "center" of the disk.
   this.x = -1
   this.y = -1

   this.peg = 0				// Index of the peg this disk on.
   this.below = -1			// Index of the disk below this disk.
}


function clearPeg ( x, floor )

{
   this.x = x				// "Center" of the peg.
   this.floor = floor			// Location of the base or top disk.
   this.top = -1			// Index of top disk in the stack.
}


function Peg ( index, x, floor )

{
   this.clear = clearPeg

   this.index = index			// Self reference.
   this.clear ( x, floor )
}


function grabDisk ( index )

{
   if ( ! this.solving && this.activeDisk == -1 ) {

	var disk = this.disks[index]
	var peg = this.pegs[disk.peg]

	if ( peg.top == index ) {

		this.ok2drop = false
		this.activeDisk = index
		peg.top = disk.below
		peg.floor += graphics.blockHeight
		disk.below = -1
	}
   }
}


function dragDisk (e)

{
   var index = this.activeDisk

   if ( ! this.solving && index > -1 ) {

	var mouse = mouseCoords (e)
	var disk = this.disks[index]
	var y = mouse.y
	var x = disk.x
	var diskImg = graphics.disks [index]
	var shadowImg = graphics.diskShadows [index]

	if ( disk.y < this.pegTops || y < this.pegTops ) {

		x = mouse.x

		if ( ! ( y < this.pegTops ) ) y = this.pegTops - 1

	} else {

		var peg = this.pegs[disk.peg]

		if ( y > peg.floor ) y = peg.floor
	}

	disk.y = y
	disk.x = x

	x -= disk.width
	diskImg.style.top = y - graphics.halfHeight + "px"
	diskImg.style.left = x + "px"
	shadowImg.style.top = y + "px"
	shadowImg.style.left = ( x + 3 * graphics.blockWidth ) + "px"
   }
}


function pushDisk ( diskIndex, pegIndex )

{
   var peg = this.pegs[pegIndex]
   var disk = this.disks[diskIndex]

   disk.x = peg.x
   disk.y = peg.floor
   disk.peg = pegIndex
   disk.below = peg.top
   peg.top = diskIndex
   peg.floor -= graphics.blockHeight
}


function animationSpeed ( index )

{
   var speed = ( 2 * index + 2 ) * index + 4

   if ( speed != this.speed ) {

	this.speed = speed
	setCookie ( "towerSpeed", index, 30 )
   }
}


function startAnimation ()

{
   // Setup the next segment of the animation.
   var s
   var n

   this.timeInc = this.originalTimeInc
   this.x = this.disk.x
   this.y = this.disk.y
   this.dx = 0
   this.dy = 0

   // Make sure that we aren't already there.
   while ( this.nextStop == null && this.stops.length > 0 ) {

	this.nextStop = this.stops.pop()
	this.dx = this.nextStop.x - this.x
	this.dy = this.nextStop.y - this.y

	if ( this.dx == 0 && this.dy == 0 ) {

		delete ( this.nextStop )
		this.nextStop = null
	}
   }

   // If there is another segment, setup the move bookkeeping.
   if ( this.nextStop != null ) {

	// If you don't understand this and the corresponding animateDisk()
	// code there isn't space to explain it here.  This is based on old
	// algorithms for drawing lines and curves that only used integer
	// addition and subtraction (after the setup) avoiding costly floating
	// -point multiplication, division and square root operations.  This
	// may be a false economy here since we don't display a frame at every
	// pixel on the path, but we do from 4 to 8 additions/subtractions for
	// each pixel on the path plus 2 more additions/subtractions when a
	// frame is displayed.  The one definite benefit is that this algorithm
	// guarantees that we end up exactly at the end-point of the path and
	// the frames between the end-points are as evenly spaced as possible
	// with discrete (integer) coordinates.  If the speed parameter is
	// set to less than 3 the algorithm as implemented here may not work
	// properly.

	// Calculate the length of the segment.
	this.s2 = this.dx * this.dx + this.dy * this.dy
	s = Math.sqrt ( this.s2 )

	// Calculate the number of frames to display on this segment.
	n = Math.round ( s / this.speed )
	if ( n == 0 ) n = 1

	this.n2 = n * n

	// Adjust the time increment to keep a constant speed.
	this.timeInc = Math.round ( ( this.timeInc * s ) / ( this.speed * n ) )

	// We increment/decrement the x and y coordinates by 1.
	this.xInc = ( ( this.dx > 0 ) ? 1 : ( ( this.dx < 0 ) ? -1 : 0 ) )
	this.yInc = ( ( this.dy > 0 ) ? 1 : ( ( this.dy < 0 ) ? -1 : 0 ) )

	// Bookkeeping for calculating each pixel of the path segment.
	this.dx = Math.abs ( this.dx )
	this.dy = Math.abs ( this.dy )
	this.incX = ( ( this.dx < this.dy ) ? 0 : 1 )
	this.z = ( ( this.dx < this.dy ) ? this.dy : this.dx )

	// First moment of "distance" travelled squared.
	this.dx2 = this.n2
	this.dy2 = this.n2

	// First moment of "distance" to next frame squared.
	this.dz2 = this.s2

	this.z2 = this.dz2		// "Distance" to next frame squared.

	// Second moments of the "distances".
	this.n2 += this.n2
	this.s2 += this.s2
   }
}


function animateDisk ()

{
   var	animation = this.animation

   if ( animation.abort ) {

	this.activeDisk = animation.disk.index
	delete ( animation )
	this.animation = null

   } else {

	if ( animation.x == animation.nextStop.x
				&& animation.y == animation.nextStop.y ) {

		delete ( animation.nextStop )
		animation.nextStop = null

		animation.start ()
	}

	if ( animation.nextStop != null ) {

		var diskImg = graphics.disks [animation.disk.index]
		var shadowImg = graphics.diskShadows [animation.disk.index]
		var x
		var y

		do {
			// Calculate each pixel on this path segment.
			if ( animation.incX ) {

				animation.x += animation.xInc
				animation.z2 -= animation.dx2
				animation.dx2 += animation.n2
				animation.z -= animation.dy

				if ( ! ( animation.z > 0 ) ) {

					animation.y += animation.yInc
					animation.z2 -= animation.dy2
					animation.dy2 += animation.n2
					animation.z += animation.dx
				}

			} else {

				animation.y += animation.yInc
				animation.z2 -= animation.dy2
				animation.dy2 += animation.n2
				animation.z -= animation.dx

				if ( ! ( animation.z > 0 ) ) {

					animation.x += animation.xInc
					animation.z2 -= animation.dx2
					animation.dx2 += animation.n2
					animation.z += animation.dy
				}
			}

		// Check if we have reached the next frame.
		} while ( animation.z2 > 0 )

		// Reset frame distance oddometer.
		animation.dz2 += animation.s2
		animation.z2 += animation.dz2

		x = animation.x
		y = animation.y

		animation.disk.x = x
		animation.disk.y = y

		x -= animation.disk.width

		// Display the next frame.
		diskImg.style.top = y - graphics.halfHeight + "px"
		diskImg.style.left = x + "px"
		shadowImg.style.top = y + "px"
		shadowImg.style.left = ( x + 3 * graphics.blockWidth ) + "px"

		setTimeout ( "puzzle.animate()", animation.timeInc )

	} else {

		if ( animation.disk.peg != animation.peg.index ) {

			this.moveCount++

			document.getElementById ( "moves" ).innerHTML
							= this.moveCount
		}

		this.push ( animation.disk.index, animation.peg.index )

		delete ( animation )
		this.animation = null

		if ( this.solving ) {

			this.activeDisk = -2
			if ( this.stack ( this.size, 2 ) )
				alert ( "See how easy it was?" )

		} else if ( this.pegs[0].top != -1 || this.pegs[1].top != -1 )
							this.activeDisk = -1
		else alert ( "Congratulations!  You did it!" )
	}
   }
}


function Animation ( disk, peg, pegTops, speed, timeInc )

{
   // Method to setup the next segment of the animation.
   this.start = startAnimation

   // Properties.
   this.speed = speed
   this.originalTimeInc = timeInc
   this.disk = disk			// The disk being moved.
   this.peg = peg			// Destination peg.
   this.abort = false			// Interrupt.
   this.nextStop = null			// End point of active segment.
   this.stops = new Array()		// The end-points of the segments.

   // Final destination.
   this.stops.push ( { x:peg.x, y:peg.floor } )

   if ( disk.x != peg.x ) {

	// Top of the destination peg.
	this.stops.push ( { x:peg.x, y:pegTops - 1 } )

	if ( ! ( disk.y < pegTops ) ) {

		// Top of the source peg.
		this.stops.push ( { x:disk.x, y:pegTops - 1 } )
        }
   }

   // Setup the first segment of the animation.
   this.start ()
}


function dropDisk (e)

{
   var index = this.activeDisk

   if ( this.ok2drop && ! this.solving && index > -1 ) {

	var mouse = mouseCoords (e)
	var pegIndex = -1
	var disk = this.disks[index]

	if ( Math.abs ( this.pegs[0].x - mouse.x ) < disk.width ) pegIndex = 0
	else if ( Math.abs ( this.pegs[1].x - mouse.x ) < disk.width )
								pegIndex = 1
	else if ( Math.abs ( this.pegs[2].x - mouse.x ) < disk.width )
								pegIndex = 2

	if ( pegIndex > -1 ) {

		var topDisk = this.pegs[pegIndex].top

		if ( topDisk == -1 || topDisk > index ) {

			var peg = this.pegs [pegIndex]

			this.activeDisk = -2

			if ( pegIndex != disk.peg ) this.undoMoves.push (
					{ src:pegIndex, dest:disk.peg } )

			this.animation = new Animation ( disk, peg,
							this.pegTops, 16, 12 )

			if ( this.animation.nextStop != null ) {

				setTimeout ( "puzzle.animate()",
						this.animation.timeInc )

			} else {

				this.push ( disk.index, peg.index )
				delete ( this.animation )
				this.animation = null
				this.activeDisk = -1
			}

		} else alert
		( "You may not place a bigger disk on top of a smaller disk." )

	} else alert ( "Put the disk over a peg before dropping it." )
   }

   this.ok2drop = true
}


function moveDisk ( src, dest )

{
   var disk

   if ( this.activeDisk < 0 ) {

	var peg = this.pegs[src]

	disk = this.disks[peg.top]

	this.activeDisk = disk.index
	peg.top = disk.below
	peg.floor += graphics.blockHeight
	disk.below = -1

   } else {

	disk = this.disks[this.activeDisk]

	if ( disk.peg != src ) dest = disk.peg
   }

   this.animation = new Animation ( disk, this.pegs[dest], this.pegTops,
						this.speed, this.timeInc )

   if ( this.animation.nextStop != null ) {

	this.activeDisk = -2
	setTimeout ( "puzzle.animate()", this.animation.timeInc )

   } else {

	this.activeDisk = -1
	this.push ( this.animation.disk.index, this.animation.peg.index )
	delete ( this.animation )
	this.animation = null
   }

   return ( false )
}


function buildTower ( height, dest_peg )

{
   var	status = true

   if ( height > 0 ) {

	--height

	var	src_peg = this.disks[height].peg

	if ( src_peg != dest_peg ) {

		status = ( this.stack ( height, 3 - src_peg - dest_peg )
				&& this.move ( src_peg, dest_peg ) )
	}

	if ( status ) status = this.stack ( height, dest_peg )
   }

   return ( status )
}


function solvePuzzle ()

{
   this.solving = true

   if ( this.animation ) {

	this.animation.abort = true
	setTimeout ( "puzzle.solve()", this.timeInc )

   } else this.stack ( this.size, 2 )
}


function undoMove ()

{
   if ( this.activeDisk > -2 ) {

	if ( this.activeDisk > -1 ) {

		if ( this.animation ) {

			this.animation.abort = true
			setTimeout ( "puzzle.undo()", this.timeInc )

		} else {

			this.move ( -1, -1 )
		}

	} else if ( this.undoMoves.length > 0 ) {

		var lastMove = this.undoMoves.pop()

		this.moveCount -= 2
		this.move ( lastMove.src, lastMove.dest )
	}
   }
}


function resetPuzzle ( size )

{
   if ( this.animation ) {

	this.animation.abort = true
	setTimeout ( "puzzle.reset(" + size + ")", this.timeInc )

   } else {

	var index = 0
	var pegX = graphics.leftMargin
	+ Math.round ( ( graphics.blockWidth * ( size + size + 5 ) - 1 ) / 2 )
	var width = graphics.blockWidth * ( size + size + 3 )

	this.activeDisk = -2
	this.floor = graphics.topMargin + graphics.blockHeight * size
	this.moveCount = 0

	document.getElementById ( "moves" ).innerHTML = this.moveCount
	this.display.draw ( size )

	if ( size != this.size ) setCookie ( "towerSize", size, 30 )

	this.size = size

	// Clear the pegs.
	while ( index < 3 ) {

		this.pegs[index].clear ( pegX, this.floor )
		pegX += width
		++index
	}

        index = size

	while ( index > 0 ) {		// Stack disks.

		index--
		this.push ( index, 0 )
	}

	// Empty the move history.
	while ( this.undoMoves.length > 0 ) {

		this.undoMoves.pop()
	}

	this.activeDisk = -1
	this.solving = false		// Computer or player is in control.
   }
}


function Puzzle ( size )

{
   var cookie = getCookie ( "towerSize" )
   var speedIndex = 2
   var ok2drop = true

   if ( cookie ) size = parseInt ( cookie )

   cookie = getCookie ( "towerSpeed" )

   if ( cookie ) {

	speedIndex = parseInt ( cookie )
	document.getElementById ("speed").selectedIndex = speedIndex
   }

   var pegX = graphics.leftMargin
	+ Math.round ( ( graphics.blockWidth * ( size + size + 5 ) - 1 ) / 2 )
   var width = 3 * graphics.blockWidth

   // Methods.
   this.reset = resetPuzzle
   this.grab = grabDisk			// Player grabs a disk.
   this.drag = dragDisk			// Player drags a disk.
   this.drop = dropDisk			// Player drops a disk.
   this.push = pushDisk			// Stack a disk on a peg.
   this.animate = animateDisk		// Display a disk move or drop.
   this.solve = solvePuzzle		// Display the puzzle solution.
   this.stack = buildTower		// Build a stack of disks on a peg.
   this.move = moveDisk			// Move a disk (solution logic only).
   this.undo = undoMove			// Undo the previous move.
   this.setSpeed = animationSpeed

   this.display = new Display ( size )

   // Properties.
   this.size = size			// Number of disks in the puzzle.
   this.floor = graphics.topMargin + graphics.blockHeight * size

					// Top of the pegs, bottom of the
					// area where a disk can be dragged.
   this.pegTops = graphics.topMargin + graphics.halfHeight
							- graphics.blockHeight
   this.activeDisk = -1			// Disk in transit.
   this.moveCount = 0
   this.pegs = new Array()		// Peg states.
   this.disks = new Array()		// Disk states.
   this.undoMoves = new Array()		// Previous moves.

   // Create the disks.
   for ( size = 0; size < 12; ++size ) {

	width += ( graphics.blockWidth + graphics.blockWidth ) 
	this.disks[size] = new Disk ( size, width )
   }

   this.pegs[0] = new Peg ( 0, pegX, this.floor )	// Left peg.

   size = this.size

   while ( size > 0 ) {			// Create disks and stack them on the
					// left peg.
	size--
	this.push ( size, 0 )
   }

   size = this.size
   width = graphics.blockWidth * ( size + size + 3 )
   pegX += width
   this.pegs[1] = new Peg ( 1, pegX, this.floor )	// Center peg.

   pegX += width
   this.pegs[2] = new Peg ( 2, pegX, this.floor )	// Right peg.
   this.animation = null
   this.speed = 16			// Move 16 pixels per frame.
   this.timeInc = 12			// Wait 12 milliseconds between frames.
   this.solving = false			// Computer or player is in control.

   this.setSpeed ( speedIndex )

   loaded = true
}
