'===========================================================================
' Subject: SOLVE TOWERS OF HANOI PROBLEM      Date: 07-31-99 (16:12)       
'  Author: Bernt Figaro                       Code: QB, QBasic, PDS        
'  Origin: bernt.figaro@swipnet.se          Packet: ALGOR.ABC
'===========================================================================
DECLARE SUB Init ()
DECLARE SUB Incr (Value%)
DECLARE SUB Decr (Value%)
DECLARE SUB DisplayMove ()
DECLARE SUB GetRowCol (Row%, Col%, X%, Y%)
DECLARE SUB DisplayScreen ()
DECLARE SUB DoMoves (NumMoves%)
REM
REM +++++++++++++++++++++++++++++++
REM + bernt.figaro@swipnet.se     +
REM + Tower of Hanoi              +
REM + An example from Turbo Basic +
REM + translated to QBAsic        +
REM +++++++++++++++++++++++++++++++
REM
REM $STACK 32767 ' allocate plenty of stack space since it's a recursive program

DIM SHARED X%
DIM SHARED Y%
X% = 1                   ' named constants used for indexing and screen positioning
Y% = 0
CONST PromptLine% = 24   ' named constant indicating line for all user prompts
CONST MaxDisks% = 13     ' named constant indicating maximum number of disks
CONST CsrOff% = 0

' global variable declarations
DIM DisksTotal%(MaxDisks% + 10), SourceTower%(MaxDisks% + 10)
DIM TargetTower%(MaxDisks% + 10), Disk$(MaxDisks% + 10)
DIM DisksPos%(MaxDisks% + 10, MaxDisks% + 10), TowerHgth%(13)

MoveNumbers% = 0       ' used to keep track of number of moves made
BottomRow% = 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 DisksTotal%(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% - DisksTotal%(1)
FOR X% = 1 TO DisksTotal%(1)
  DisksPos%(X%, Y%) = Top% + X%          ' assign row display
  DisksPos%(X%, X%) = 1                  ' assign column display
  LOCATE Top% + X%, 1, CsrOff%           ' position cursor
  COLOR X% MOD 14 + 1, 0                 ' change color
  PRINT Disk$(X%);                       ' display the current disk
NEXT X%

CALL DisplayScreen                     ' display game screen

TowerHgth%(1) = Top%                   ' initialize global variables
TowerHgth%(2) = TowerBase%
TowerHgth%(3) = TowerBase%
SourceTower%(1) = 1
TargetTower%(1) = 3
RecursionDepth% = 0

LOCATE 1, 1, CsrOff%: PRINT "Start time: "; TIME$

CALL DoMoves(MoveNumbers%) ' start game

LOCATE 2, 1, CsrOff%: PRINT "Stop time : "; TIME$
LOCATE 3, 1, CsrOff%: PRINT "Done in"; MoveNumbers%; "moves"
LOCATE 5, 1, CsrOff%: PRINT "Hit any key !"
l$ = INPUT$(1)

END  ' end of program

SUB Decr (Value%) STATIC
Value% = Value% - 1
END SUB

SUB DisplayMove STATIC
  ' specify the global variables we need
  SHARED Column%, RecursionDepth%, DisksTotal%(), SourceTower%()
  SHARED TowerHgth%(), DisksPos%()
  SHARED TargetTower%(), Disk$()

  ' 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
  CALL GetRowCol(Row%, Col%, X%, Y%)
  'LOCATE DisksPos%(DisksTotal%(RecursionDepth%),Y%), DisksPos%(DisksTotal%(RecursionDepth%),X%), CsrOff%
  LOCATE 1, 76: PRINT Col%
  ' go to the position of the next disk to move
  IF Col% = 0 THEN Col% = 1
  LOCATE Row%, Col%, CsrOff%
  COLOR 7, 0
  PRINT SPACE$(26)      ' erase current disk

  ' increment the height of the tower the disk is moving to
  CALL Incr(TowerHgth%(SourceTower%(RecursionDepth%)))

  ' position cursor at top of destination tower
  LOCATE TowerHgth%(TargetTower%(RecursionDepth%)), Column%, CsrOff%

  ' get the color
  COLOR DisksTotal%(RecursionDepth%) MOD 14 + 1, 0
  PRINT Disk$(DisksTotal%(RecursionDepth%));   ' display the disk

  COLOR 7, 0

  ' update the current position of this disk
  DisksPos%(DisksTotal%(RecursionDepth%), Y%) = TowerHgth%(TargetTower%(RecursionDepth%))
  DisksPos%(DisksTotal%(RecursionDepth%), X%) = Column%

  ' decrement the height of the tower the disk came from
  TowerHgth%(TargetTower%(RecursionDepth%)) = TowerHgth%(TargetTower%(RecursionDepth%)) - 1
END SUB ' end procedure DisplayMove

SUB DisplayScreen STATIC
REM
REM +++++++++++++++++++++++++++++++++++++++++++++++++++
REM + This procedure displays a message on the screen +
REM +++++++++++++++++++++++++++++++++++++++++++++++++++
REM
  SHARED DisksTotal%(), TowerBase%     ' indicate which global vars we need

  LOCATE 1, 26, CsrOff%                ' position the cursor and turn it on
  COLOR 4, 0                              ' set the display color
  PRINT USING "Tornen of ™rebro for ## DISKS"; DisksTotal%(1)
  LOCATE TowerBase% + 1, X%, CsrOff%   ' 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 DisplayScreen

SUB DoMoves (NumMoves%) STATIC
  ' specify the global variables we need
  SHARED RecursionDepth%, DisksTotal%(), SourceTower%()
  SHARED TargetTower%(), TowerHgth%()
  ' increment level of recursion
  CALL Incr(RecursionDepth%)

  ' check if we should exit routine
  IF DisksTotal%(RecursionDepth%) = 0 THEN
    CALL Decr(RecursionDepth%)
    EXIT SUB
  END IF

  DisksTotal%(RecursionDepth% + 1) = DisksTotal%(RecursionDepth%) - 1
  SourceTower%(RecursionDepth% + 1) = SourceTower%(RecursionDepth%)
  TargetTower%(RecursionDepth% + 1) = 6 - SourceTower%(RecursionDepth%) - TargetTower%(RecursionDepth%)
  CALL DoMoves(NumMoves%)

  CALL Incr(NumMoves%)
  CALL DisplayMove
  DisksTotal%(RecursionDepth% + 1) = DisksTotal%(RecursionDepth%) - 1
  SourceTower%(RecursionDepth% + 1) = 6 - SourceTower%(RecursionDepth%) - TargetTower%(RecursionDepth%)
  TargetTower%(RecursionDepth% + 1) = TargetTower%(RecursionDepth%)
  CALL DoMoves(NumMoves%)
  CALL Decr(RecursionDepth%)
END SUB ' end procedure DoMoves

SUB GetRowCol (Row%, Col%, X%, Y%) STATIC
SHARED DisksPos%()
SHARED DisksTotal%()
SHARED RecursionDepth%
Row% = DisksPos%(DisksTotal%(RecursionDepth%), Y%)
Col% = DisksPos%(DisksTotal%(RecursionDepth%), X%)
END SUB

SUB Incr (Value%) STATIC
Value% = Value% + 1
END SUB

SUB Init STATIC
' This procedure is used to initialize the screen and get the number
           ' of disks to use.
  SHARED DisksTotal%(), TowerBase%  ' indicate which global vars we need
  STATIC C%                                ' declare a local variable

  KEY OFF                                 ' turn key display off
  COLOR 7, 0                              ' initialize screen color
  CLS
  COLOR 4, 0
  LOCATE 1, 26, CsrOff%
  PRINT "Tower of ™rebro"                 ' display the program banner
  COLOR 6, 0
  LOCATE PromptLine%, X%, CsrOff%
  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, CsrOff%
    INPUT DisksTotal%(1)
    IF DisksTotal%(1) > MaxDisks% THEN BEEP
  LOOP UNTIL DisksTotal%(1) <= MaxDisks%
  TowerBase% = TowerBase% + DisksTotal%(1)
  COLOR 7, 0
  CLS
  'LOCATE PromptLine%, X%, CsrOff%
  'PRINT SPACE$(79)                        ' clear prompt line
END SUB  ' end procedure Init
