Last updated: February 21th, 2023
Evolve 5.0
VOLVE    5.0

Towers of Hanoi

;
; a Tower of Hanoi creature
; =========================
; (c) 2007 - 2022, Ken Stauffer
;
; This is revised to work with Evolve 5.0
; 1. PICK instruction changed
; 2. inverted cooridinates to match new coordinate system. (0,0) lower-left
; 3. New kforth is limited to 16-bit ints and a smaller
;   stack size (64 elements) so 14 disks is max for this version
;
; Add this creature to a blank universe (no barriers)
; It will create a bunch of disks and then move them from
; one side of the screen to the other side.
;
; NOTE: These routines refer to three piles for storing disk.
; Piles are encoded as follows:
;
;   -1 =    Left pile
;    0 =    Middle pile
;    1 =    Right pile
;

main:
{
    14  ; <=== number of disks to play with (14 is max)

    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 disk 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.
;
put_disk:
{
    swap
    dup
    goto_pile call

    dup
    0 = { pop 1 } { negate } ifelse

    swap
    {                   ; (dir cursize)
        swap                ; (cursize dir)
        dup 0 OMOVE pop
        dup negate 0 1 MAKE-SPORE pop
        swap                ; (dir cursize)

        1-              ; (dir cursize-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
    {               ; (dir cursize)
        0 -1 EAT

        0 > {
            1+      ; (dir cursize+1)
            swap        ; (cursize+1 dir)
            dup 0 OMOVE pop ; (cursize+1 dir dir 0 -- cursize+1 dir)
            swap        ; (dir cursize+1)
            1       ; (dir cursize+1 1)
        } {
            0       ; (dir cursize 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
    { 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:
{

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

        -3 peek         ; (n src aux dst  src)
        -2 peek         ; (n src aux dst  src dst)
        move_disk call

        -4 peek 1-      ; (n src aux dst  n-1)
        -3 peek         ; (n src aux dst  n-1 aux)
        -5 peek         ; (n src aux dst  n-1 aux src)
        -4 peek         ; (n src aux dst  n-1 aux src dst)

        play_towers_of_hanoi call

    } if

    pop pop pop pop
}