Home > Previous Page >  Towers of Hanoi
Archive Search  
Towers of Hanoi
by
Ricardo Fernandes Lopes

( Towers of Hanoi game )
( by Ricardo F. Lopes - 2006 )

( Use keys 1, 2 and 3 to play )

0 VARIABLE MOV#  ( Move count )
: MOV+1 ( Increment move count )  MOV# @ 1+ MOV# ! ;

CREATE T 24 ALLOT  ( Towers array: 3 poles, 8 lines )
: T>     ( pole line -- c-adr )  3 * + T + ;
: RING@  ( pole line -- ring  )  T> C@ ;
: RING!  ( ring pole line --  )  T> C! ;

: INIT ( Initialize game )
 0 MOV# !     ( Reset move count)
 8 0          ( Set rings)
 DO
  7 I -       ( ring size)
  0 I RING!   ( Put all rings in pole 1)
  0 1 I RING! ( empty pole 2)
  0 2 I RING! ( empty pole 3)
 LOOP
;

: .FRAME ( draw poles )
 7 0             ( poles with 7 lines)
 DO
  8 SPACES       ( left margin)
  3 0            ( 3 poles)
  DO
   ." |       "
  LOOP
 LOOP
 32 0            ( bottom line)
 DO
  ASCII _ EMIT
 LOOP
 ."         1       2       3"  ( pole labels)
;

: .RING ( ring line pole -- , Draw a ring)
 1+ 16 * 3 PICK - ( x = pole+1 * 16 - ring)
 SWAP 2 * 32 +    ( y = line*2 + 32       )
 ROT 2 * 2+ 0     ( ringsize = ring*2 + 2 )
 DO               ( Plot ring)
  OVER I + OVER
  1 PLOT
 LOOP
 DROP DROP
;

: SHOW ( Draw everything)
 CLS
 .FRAME CR CR
 ." Moves: " MOV# @ . ( Show moves)
 3 0                  ( Three Poles)
 DO
  7 0                 ( Seven Lines)
  DO
   J I RING@ ?DUP
   IF                 ( Is there a ring?)
    I J .RING         ( Draw the ring)
   THEN
  LOOP
 LOOP
;

: TOP> ( pole -- pole line , Find free line over the rings in a pole)
 0                  ( dummy previous line)
 8 0                ( check 8 lines )
 DO
  DROP              ( discard previous line)
  I OVER OVER RING@ ( Get ring in current line)
  0=
  IF                ( Free line? Done )
   LEAVE
  THEN
 LOOP
;

: END?  ( -- flag , Check for end of game)
 0             ( sum = 0)
 7 0           ( 7 lines)
 DO
  2 I RING@ +  ( Sum all rings in pole 3)
 LOOP
 28 =          ( Game over if sum of rings = 28)
;

: MOV ( from to -- , Move a ring from one pole to another)
 OVER TOP> ?DUP
 IF         ( Is there a ring in the source?)
  1- RING@  ( Get top ring from source )
  OVER TOP> ?DUP
  IF                   ( Destination not empty?)
   1- RING@            ( Yes, get top ring from destination)
  ELSE                 ( The destination is empty..)
   DROP 8              ( ..assume top destination ring = 8)
  THEN
  OVER >               ( Smaller ring over bigger?)
  IF
   SWAP TOP> RING!     ( Place ring in the destination pole)
   0
   SWAP TOP> 1- RING!  ( Remove ring from source)
   MOV+1               ( Count move)
  ELSE                 ( Invalid move, do nothing)
   DROP DROP DROP
  THEN
 ELSE                  ( No rings in source, do nothing)
  DROP DROP DROP
 THEN
;

: KEY?  ( -- n , Get user input)
 BEGIN INKEY 0= UNTIL    ( Wait for key release)
 BEGIN INKEY ?DUP UNTIL  ( Wait for a keypress)
 ASCII 0 - 1 MAX 3 MIN   ( convert key 1-3 to number 1-3)
;

: HANOI ( Main word, execute it to play)
 INIT                          ( Init variables)
 SHOW                          ( Draw screen)
 BEGIN                         ( Main game loop)
  CR ." From? " KEY? DUP . 1-  ( Get user "from" input)
  CR ."   To? " KEY? DUP . 1-  ( Get user "to" input)
  MOV                          ( Move ring as commanded)
  SHOW                         ( Update screen)
  END?                         ( Check for success)
 UNTIL
;