(* -------------------------------------------------------------------------
  :Program.       Hanoi
  :Author.        Kai Bolay
  :Address.       Hoffmannstraße 168, 7250 Leonberg
  :Phone.         07152/22135
  :History.       v1.00 Initial
  :Copyright.     PD
  :Language.      Modula-2
  :Translator.    M2Amiga 3.3d
  :Imports.       IntuiStruct1.3 [bne]
  :Contents.      Solving "Towers of Hanoi" problem
------------------------------------------------------------------------- *)
MODULE Hanoi;

(* FOLD: IMPORT *)
FROM SYSTEM      IMPORT ADR;
FROM Arts        IMPORT TermProcedure, Assert;
FROM Intuition   IMPORT ScreenPtr, NewScreen, OpenScreen, CloseScreen,
                        customScreen;
FROM Graphics    IMPORT ViewModes, ViewModeSet, SetRGB4, SetDrMd, RectFill,
                        DrawModes, DrawModeSet;
FROM Dos         IMPORT Delay;
FROM IntuiStruct IMPORT StructScreen;
FROM InOut       IMPORT WriteString, WriteCard, ReadLongInt, ReadCard,
                        WriteLn;
(* ENDFD *)

(* FOLD: CONST *)
CONST MaxSlices = 32;
      NumTowers = 3;
(* ENDFD *)
(* FOLD: TYPE *)
TYPE SliceNr = [0..MaxSlices];
     TowerNr = [1..NumTowers];
(* ENDFD *)
(* FOLD: VAR *)
VAR HanoiScreen    : ScreenPtr;
    Pin            : ARRAY TowerNr, SliceNr OF SliceNr;
    Depth          : ARRAY TowerNr OF SliceNr;
    NumSlices      : CARDINAL;
    DelayTime      : LONGINT;
    PileCount      : LONGCARD;
(* ENDFD *)

(* FOLD: CloseDown *)
PROCEDURE CloseDown;

BEGIN
   IF HanoiScreen # NIL THEN
      CloseScreen (HanoiScreen);
      HanoiScreen := NIL;
   END; (* IF *)
END CloseDown;
(* ENDFD *)
(* FOLD: OpenUp *)
PROCEDURE OpenUp;

VAR NewHanoiScreen : NewScreen;

BEGIN
   HanoiScreen := NIL;
   TermProcedure (CloseDown);
   StructScreen (NewHanoiScreen, 1, 0, 1, ViewModeSet {}, customScreen,
                 ADR ("Hanoi! Writen by Kai Bolay..."));
   HanoiScreen := OpenScreen (NewHanoiScreen);
   Assert (HanoiScreen # NIL, ADR ("Can't open Screen!"));
   WITH HanoiScreen^ DO
      SetRGB4 (ADR (viewPort), 0,  0,  0,  0);
      SetRGB4 (ADR (viewPort), 1, 15, 15, 15);
   END; (* WITH *)
   SetDrMd (ADR (HanoiScreen^.rastPort), DrawModeSet {complement});
END OpenUp;
(* ENDFD *)
(* FOLD: DrawSlice *)
PROCEDURE DrawSlice (Tower : TowerNr);

VAR x, y, h, w, mp, ma : INTEGER;

(**********************************
 * Piecegrößen sind anhänging von *
 * - Auflösung des Screens        *
 * - der Gesamtzahl von Pieces    *
 * - ihrer Nummer                 *
 *                                *
 *  1         -> 1/4 PieceWidth   *
 *  ...       -> ???              *
 *  NumPieces -> 1/1 PieceWidth   *
 *                                *
 *  ==> m = (3/4) / (NumSlices-1) *
 *      c = 1/4 PieceWidth        *
 **********************************)

BEGIN
   WITH HanoiScreen^ DO (* Ich glaub' kaum das es jemand versteht! *)
      mp := (width DIV (NumTowers+1)) DIV 2; (* max PieceWidth *)
      ma := (mp * (INTEGER (Pin[Tower, Depth[Tower]])-1) * 3) DIV
                  (INTEGER (NumSlices)                   * 4);

      w := (mp DIV 4) + ma;
      h := (height - 17) DIV (INTEGER (NumSlices) * 2);

      x := (2*mp) * INTEGER (Tower);
      y := 13 + h + INTEGER (NumSlices - Depth[Tower]) * (2*h);

      DEC (w); DEC (h); (* Space between *)
      RectFill (ADR (rastPort), x-w, y-h, x+w, y+h);
   END; (* WITH *)
END DrawSlice;
(* ENDFD *)
(* FOLD: Init *)
PROCEDURE Init;

VAR Tower : TowerNr;

BEGIN
   WriteString ("Die Türme von Hanoi!"); WriteLn; WriteLn;
   REPEAT
      WriteString ("Wieviele Scheiben (1-32) ? "); ReadCard (NumSlices);
   UNTIL (NumSlices >= 1) AND (NumSlices <= 32);
   REPEAT
      WriteString ("Wie Schnell (1-16) ? "); ReadLongInt (DelayTime);
   UNTIL (DelayTime >= 1) AND (DelayTime <= 16);
   DEC (DelayTime);
   DelayTime := DelayTime * 10;
   OpenUp;
   FOR Tower := 1 TO NumTowers DO
      Depth[Tower] := 0;
      IF Tower = 1 THEN
         WHILE Depth[Tower] < NumSlices DO
            INC (Depth[Tower]);
            Pin[Tower, Depth[Tower]] := NumSlices+1-Depth[Tower];
            DrawSlice (Tower);
            Delay (DelayTime);
         END; (* FOR *)
      END; (* IF *)
   END; (* FOR *)
   PileCount := 0;
END Init;
(* ENDFD *)
(* FOLD: Pile *)
PROCEDURE Pile (Height : SliceNr; Source, Dest : TowerNr);

   (* FOLD: MoveSlice *)
   PROCEDURE MoveSlice (Source, Dest : TowerNr);

   BEGIN
      DrawSlice (Source);
      INC (Depth[Dest]);
      Pin[Dest, Depth[Dest]] := Pin[Source, Depth[Source]];
      DrawSlice (Dest);
      DEC (Depth[Source]);

      INC (PileCount);
      Delay (DelayTime);
   END MoveSlice;
   (* ENDFD *)

BEGIN
   IF Height > 0 THEN
      (* Auf Zwischenspeicher (außer unterste Scheibe) *)
      Pile (Height-1, Source, 2*NumTowers-Source-Dest);

      (* unterste Scheibe zum Ziel *)
      MoveSlice (Source, Dest);

      (* Den Rest vom Zwischenspeicher zum Ziel *)
      Pile (Height-1, 2*NumTowers-Source-Dest, Dest);
   END; (* IF *)
END Pile;
(* ENDFD *)


BEGIN
   Init;
   Pile (NumSlices, 1, NumTowers);
   Delay (5 * 50);
   WriteLn;
   WriteString ("Anzahl der Züge: "); WriteCard (PileCount, 6); WriteLn;
END Hanoi.
