Last updated: January 10, 2007
Evolve 4.0 - Towers of Hanoi
VOLVE    4.0

Towers of Hanoi

;
; a "Towers of Hanoi" creature
; ============================
; (c) 2007, Ken Stauffer
;
; This creature builds a pile of disks, and then
; plays 'towers of hanoi' with them.
;
; Add this organism to a blank universe (no barriers).
;
; NOTE:
; These routines refer to three piles for storing disks.
; Piles are encoded as follows:
;
;	-1 =	Left pile
;	 0 =	Middle pile
;	 1 =	Right pile
;

main:
{
	8	; <=== number of disks to play with

	R8!
	measure_universe call
	pop R9!
	R8 make_disks call
	R8 -1 0 1 play_towers_of_hanoi call

	{ 1 ?loop } call

}

;
; ( -- width height)
;
; Measure the universe, return the width and height.
;
; Assumes:
;	* Universe is empty, except for itself.
;	* No "oval barrier" was used to create the universe.
;
measure_universe:
{
	{ -1 0 OMOVE ?loop } call
	{  0 -1 OMOVE ?loop } call

	0 { 1+ 1 0 OMOVE ?loop } call
	0 { 1+ 0 1 OMOVE ?loop } call
}

;
; ( disks -- )
;
; Create the initial pile of disks on left-hand side of
; the universe.
;
make_disks:
{
	{ -1 0 OMOVE ?loop } call
	{  0 1 OMOVE ?loop } call

	jj: {
		?dup {
			dup make_disk call
			1-
			0 -1 OMOVE pop
			{ -1 0 OMOVE ?loop } call
			jj call
		} if
	} call
	
}

;
; ( size -- )
;
; Make a single disk. 'size' is how big the disk is.
;
make_disk:
{
	1 0 OMOVE pop
	-1 0 1 MAKE-SPORE pop
	1- ?dup
	?loop
}

;
; (pile size -- )
;
; Put a disk down on 'pile'.
;
; 'pile' is where to put the disk.
; 'size' is the size of the disk we are putting.
;
; Assumes we are on top of a pile, and that
; size is greater than 0.
;
put_disk:
{
	swap
	dup
	goto_pile call

	dup
	0 = { pop 1 } { negate } ifelse

	swap
	{
		swap
		dup 0 OMOVE pop
		dup negate 0 1 MAKE-SPORE pop
		swap

		1-

		?dup ?loop
	} call

	pop

}

;
; (pile -- size )
;
; Pick up a disk from 'pile'
;
; 'pile' is where we will pick up a disk
; 'size' is how big of a disk we picked up.
;
take_disk:
{
	dup
	goto_pile call

	dup
	0 = { pop 1 } { negate } ifelse

	0
	{
		0 1 EAT

		0 > {
			1+
			swap
			dup 0 OMOVE pop
			swap
			1
		} {
			0
		} ifelse

		?loop
	} call
	swap pop
}


;
; (pile -- )
;
; Go to 'pile'.
;
goto_pile:
{
	;
	; go up a little (above any disks)
	;
	R8 10 + { 0 -1 OMOVE pop 1- ?dup ?loop } call

	dup
	0 = {
		; go to left
		{ -1 0 OMOVE ?loop } call

		; go to middle
		R9 2 /
		{
			1 0 OMOVE pop
			1-
			?dup ?loop
		} call
	} {
		dup 0 OMOVE ?loop
	} ifelse

	pop

	; go all the way down
	{ 0 1 OMOVE ?loop } call

}

;
; ( from-pile to-pile -- )
;
; Move whatever disk is on top of 'from-pile' and
; place it on top of 'to-pile'.
;
move_disk:
{
	swap
	take_disk call
	put_disk call
}

;
; (n src aux dst -- )
;
; Solve Tower Hanoi problem.
;
; Implements this algorithm:
;
;	Solve(N, Src, Aux, Dst)
;	{
;		if N is 0 exit
;		Solve(N-1, Src, Dst, Aux)
;		Move from Src to Dst
;		Solve(N-1, Aux, Src, Dst)
;	}
;
;
; The first invocation of this routine should be:
;
;	N -1 0 1 play_towers_of_hanoi call
;
;	(where N is the number of disks)
;
play_towers_of_hanoi:
{

	3 pick				; (n src aux dst n)
	0 >
	{
		3 pick 1-		; (n src aux dst  n-1)
		3 pick			; (n src aux dst  n-1 src)
		2 pick			; (n src aux dst  n-1 src dst)
		4 pick			; (n src aux dst  n-1 src dst aux)
		
		play_towers_of_hanoi call

		2 pick			; (n src aux dst  src)
		1 pick			; (n src aux dst  src dst)
		move_disk call

		3 pick 1-		; (n src aux dst  n-1)
		2 pick			; (n src aux dst  n-1 aux)
		4 pick			; (n src aux dst  n-1 aux src)
		3 pick                  ; (n src aux dst  n-1 aux src dst)

		play_towers_of_hanoi call

	} if

	pop pop pop pop
}