'===========================================================================
' Subject: RECURSIVE "TOWERS OF HANOI"        Date: 06-29-98 (16:58)       
'  Author: Dave Navarro, Jr.                  Code: PB                     
'  Origin: dave@powerbasic.com              Packet: PB.ABC
'===========================================================================
'=============================================================================
'  This  program demostrates a recursive version of the popular "Towers
'  of Hanoi" game.
'
'  In order to run this program do the following:
'    1. Load PowerBASIC by typing PB at the DOS prompt.
'    2. Load the file HANOI.BAS from the Load option of the File
'       pulldown menu.
'    3. Compile and run the program by pressing F9.
'=============================================================================

%X  = 1   ' named constants used for indexing and screen positioning
%Y  = 0
%PromptLine = 24   ' named constant indicating line for all user prompts
%MaxDisks   = 13   ' named constant indicating maximum number of disks

GLOBAL Column%
GLOBAL Disk$()
GLOBAL DisksPosition%()
GLOBAL NumberOfDisks%()
GLOBAL RecursionDepth%
GLOBAL SourceTower%()
GLOBAL TargetTower%()
GLOBAL TowerBase%  ' indicate which global vars we need
GLOBAL TowerHeight%()

SUB Init   ' This procedure is used to initialize the screen and get the number
           ' of disks to use.
  LOCAL C%                                ' declare a local variable

  CURSOR OFF
  CLS
  COLOR 4, 0
  LOCATE 1, 26
  PRINT "TOWERS OF HANOI"                 ' display the program banner
  COLOR 6, 0
  LOCATE %PromptLine, %X
  PRINT "Number of Disks (1 TO " + STR$(%MaxDisks) +  ") ";
  DO   ' get the number of disks from the user
    LOCATE %PromptLine, LEN("Number of Disks (1 TO " + STR$(%MaxDisks) + ") ") + 1
    LINE INPUT xx$
    NumberOfDisks%(1) = VAL(xx$)
    IF NumberOfDisks%(1) > %MaxDisks THEN BEEP
  LOOP UNTIL NumberOfDisks%(1) <= %MaxDisks
  TowerBase% = TowerBase% + NumberOfDisks%(1)
  COLOR 7, 0
  LOCATE %PromptLine, %X
  PRINT SPACE$(79)                        ' clear prompt line

END SUB  ' end procedure Init


SUB DisplayGameScreen  ' This procedure displays a message on the screen
  LOCATE 1, 26                          ' position the cursor and turn it on
  COLOR 4, 0                            ' set the display color
  PRINT "TOWERS OF HANOI FOR" NumberOfDisks%(1) "DISKS"
  LOCATE TowerBase% + 1, %X               ' position the cursor
  COLOR 1, 0                              ' set the display color
  PRINT STRING$(80,176);                  ' display a bar on the screen
  COLOR 7,0                               ' set the display color
END SUB  ' end procedure DisplayGameScreen


Sub MakeMoves(NumMoves%)
  ' specify the global variables we need
  ' increment level of recursion
  INCR RecursionDepth%

  ' check if we should exit routine
  IF NumberOfDisks%(RecursionDepth%) = 0 THEN
    DECR RecursionDepth%
    EXIT SUB
  END IF

  NumberOfDisks%(RecursionDepth% + 1) = NumberOfDisks%(RecursionDepth%) - 1
  SourceTower%(RecursionDepth% + 1) = SourceTower%(RecursionDepth%)
  TargetTower%(RecursionDepth% + 1) = 6 - _
       SourceTower%(RecursionDepth%) - TargetTower%(RecursionDepth%)
  CALL MakeMoves(NumMoves%)

  INCR NumMoves%
  CALL DisplayMove
  NumberOfDisks%(RecursionDepth% + 1) = NumberOfDisks%(RecursionDepth%) - 1
  SourceTower%(RecursionDepth% + 1) = 6 - _
        SourceTower%(RecursionDepth%) - TargetTower%(RecursionDepth%)
  TargetTower%(RecursionDepth% + 1) = TargetTower%(RecursionDepth%)
  CALL MakeMoves(NumMoves%)
  DECR RecursionDepth%
END SUB ' end procedure MakeMoves


SUB DisplayMove
  ' specify the global variables we need

  ' based on current tower, set display column
  IF TargetTower%(RecursionDepth%) = 1 THEN
    Column% = 1
  ELSEIF TargetTower%(RecursionDepth%) = 2 THEN
    Column% = 27
  ELSEIF TargetTower%(RecursionDepth%) = 3 THEN
    Column% = 54
  END IF

  ' go to the position of the next disk to move
  LOCATE DisksPosition%(NumberOfDisks%(RecursionDepth%),%Y), _
         DisksPosition%(NumberOfDisks%(RecursionDepth%),%X)  
  COLOR 7,0
  PRINT SPACE$(26)      ' erase current disk

  ' increment the height of the tower the disk is moving to
  INCR TowerHeight%(SourceTower%(RecursionDepth%))

  ' position cursor at top of destination tower

  LOCATE TowerHeight%(TargetTower%(RecursionDepth%)), Column%  

  ' get the color
  COLOR NumberOfDisks%(RecursionDepth%) MOD 14 + 1,0
  PRINT Disk$(NumberOfDisks%(RecursionDepth%));   ' display the disk

  COLOR 7,0

  ' update the current position of this disk
  DisksPosition%(NumberOfDisks%(RecursionDepth%),%Y) = _
                                   TowerHeight%(TargetTower%(RecursionDepth%))
  DisksPosition%(NumberOfDisks%(RecursionDepth%),%X) = Column%

  ' decrement the height of the tower the disk came from
  TowerHeight%(TargetTower%(RecursionDepth%)) = _
                               TowerHeight%(TargetTower%(RecursionDepth%)) - 1
END SUB ' end procedure DisplayMove

FUNCTION PbMain() AS LONG

' global variable declarations
DIM NumberOfDisks%(1:%MaxDisks + 1), SourceTower%(1:%MaxDisks + 1)
DIM TargetTower%(1:%MaxDisks + 1), Disk$(1:%MaxDisks + 1)
DIM DisksPosition%(%MaxDisks, 1), TowerHeight%(1 :3)

NumberOfMoves% = 0               ' used to keep track of number of moves made
BottomLine%    = 24              ' used to indicate bottom line of display
TowerBase%     = 2
' start of main program
CALL Init

' initialize the array of disks
FOR X% = 1 TO NumberOfDisks%(1)        ' for the number of disks
  Disk$(X%) = STRING$(26,32)  ' fill the array with spaces
  ' put the correct size disk in the array element
  ' 219  ( Û ) is character used to make disks
  MID$(Disk$(X%), %MaxDisks + 1 - X%, X% * 2 - 1) = STRING$(30,219)
NEXT X%

' display the initial disks
Top% = TowerBase% - NumberOfDisks%(1)
FOR X% = 1 TO NumberOfDisks%(1)
  DisksPosition%(X%,%Y) = Top% + X%      ' assign row display
  DisksPosition%(X%,%X) = 1              ' assign column display
  LOCATE Top% + X%, 1         ' position cursor
  COLOR X% MOD 14 + 1,0       ' change color
  PRINT Disk$(X%);            ' display the current disk
NEXT X%

CALL DisplayGameScreen         ' display game screen

TowerHeight%(1) = Top%              ' initialize global variables
TowerHeight%(2) = TowerBase%
TowerHeight%(3) = TowerBase%
SourceTower%(1) = 1
TargetTower%(1) = 3
RecursionDepth% = 0

LOCATE 1, 1 : PRINT "Start time: " TIMER

CALL MakeMoves(NumberOfMoves%) ' start game

LOCATE 2, 1 : PRINT "Stop time : " TIMER

LOCATE %PromptLine, 26
PRINT "DONE IN" NumberOfMoves% "MOVES";
CURSOR ON

END FUNCTION
