(*---------------------------------------------------------------------------
  :Program.    Greed.mod
  :Author.     Andreas Pahl
  :Address.    Zikadenweg 22, D-1000 Berlin 19
  :Phone.      (0)30/302 55 37
  :Version.    1.00
  :Date.       8.Juni 1989
  :Copyright.  PD
  :Language.   Modula
  :Translator. M2Amiga v3.2d
  :Contents.   Kleines Spielchen
---------------------------------------------------------------------------*)

MODULE Greed;


(* Bugs :

   - Der letzte zum Tod führende Zug wird zum Score aufaddiert,
     aber warum????

*)

(* Mögliche Erweiterungen :

   - Seed mit ausgeben
   - ev. Zuege die zum Tod führen, aber noch ein Ausweg möglich ist, nicht
     zulassen
   - Seed als Option beim Start mit angeben
   - Das Verhältnis zu Score zu allen verfügbaren Punkten ausgeben
*)


FROM SYSTEM         IMPORT ADR;
FROM Arts           IMPORT BreakPoint, TermProcedure;
FROM RandomNumber   IMPORT RND, PutSeed;
FROM FileSystem     IMPORT Lookup, File, done, Close, ReadBytes,
                           WriteBytes, SetPos, diskWriteProtected;
FROM Arguments      IMPORT GetArg, NumArgs;

(* AMOK-Module *)

FROM TextWindow     IMPORT GotoXY, WriteS, WriteC, WriteLn, ReadC,
                           WriteCard, WriteInt, ScrollOff, Title, CurOff,
                           Colour, Style, ClearWindow, ReadLn, CurOn;
FROM Beep           IMPORT Beep;
FROM TimerSupport   IMPORT OpenTimer, CloseTimer, GetTimeLong;


CONST

   MinX         =  0;                       (* Screen-Begrenzungen         *)
   MaxX         = 79;
   MaxY         = 22;
   MinY         =  0;

   Spur             = ".";                  (* Spur des Cursors            *)

   HiScoreDosname   = "Greed_HiScores";

   MaxEintraege     = 50;                   (* in die Hiscore-Tabelle      *)


TYPE

   NameString       = ARRAY [1..12] OF CHAR;

   HiScoreEintrag   = RECORD                (* Element der Hiscore-Tabelle *)
                        Name   : NameString;
                        Punkte : CARDINAL;
                        seed   : LONGINT;   (* Startzahl des Zufalls-
                                               generators                  *)
                      END;


VAR

   Matrix           : ARRAY [MinY..MaxY],[MinX..MaxX] OF INTEGER;
                                            (* zweidimensionales Feld zum  *)
                                            (* Speichern des Screens       *)
   NochMal,                                 (* Nochmal spielen ? (J/N)     *)
   Eingabe          : CHAR;                 (* Gedrückte Taste             *)
   Zufallszahl      : INTEGER;              (* vom Zufallsgenerator erzeugt*)
   Zeile,                                   (* Laufvariablen für die       *)
   Spalte,                                  (* Indices von Matrix          *)
   XPosition,                               (* X-Position des Cursors      *)
   YPosition,                               (* Y-Position des Cursors      *)
   Schrittweite,                            (* So viel Felder wird der     *)
                                            (* Cursor weiterbewegt         *)
   Laufvariable,                            (* Bei Diagonalbewegungen      *)
                                            (* errechnet sich aus dieser   *)
                                            (* Variable Zeile und Spalte.  *)
   Stelle,                                  (* Laufvariable für den Index  *)
                                            (* von HiScoreTabelle          *)
   Score            : CARDINAL;             (* Punktestand                 *)
   Titel            : ARRAY [1..80] OF CHAR; (* Titel des Screens          *)
   Ende             : BOOLEAN;              (* Solange FALSE wird gespielt *)
   Dummy,                                   (* Unbenötigte Variable aus    *)
                                            (* dem Aufruf von GetTimeLong  *)
   Seed             : LONGINT;              (* Die Anzahl der Sekunden     *)
                                            (* wird als Startwert für den  *)
                                            (* Zufallsgenerator genommen   *)
   HiScore          : File;                 (* File zum Speichern der      *)
                                            (* HiScores                    *)
   HiScoreTabelle   : ARRAY [1..MaxEintraege] OF HiScoreEintrag;
                                            (* Tabelle der HiScores        *)
   act              : LONGINT;              (* Von Read/WriteBytes benötigt*)
   TasteOben,                               (* Tasten zur Steuerung        *)
   TasteUnten,
   TasteLinks,
   TasteRechts,
   TasteLinksOben,
   TasteLinksUnten,
   TasteRechtsOben,
   TasteRechtsUnten : CHAR;
   Laengeargument   : INTEGER;              (* Länge des Arguments         *)
   argument         : ARRAY [1..63] OF CHAR;(* Argument bei Programmaufruf *)
                                            (* enthält Tastaturbelegung    *)


PROCEDURE ScoreSchreiben();

(* Schreibt nach jeder Bewegung den aktuellen Punktestand                  *)

CONST

   PosX     =  MinX + 12;
   PosY     =  MaxY +  2;

BEGIN
   GotoXY(PosX,PosY);
   WriteCard(Score,6);
END ScoreSchreiben;



PROCEDURE HiScoreTabelleLaden();

(* Lädt die HiScores aus dem File in den Speicher                          *)

BEGIN
   Lookup(HiScore,HiScoreDosname,2048,FALSE);
   IF HiScore.res = done THEN               (* File vorhanden              *)
      SetPos(HiScore,0);
      ReadBytes(HiScore,ADR(HiScoreTabelle),SIZE(HiScoreTabelle),act);
   ELSE
      Lookup(HiScore,HiScoreDosname,2048,TRUE);
      IF HiScore.res = done THEN            (* File nicht vorhanden        *)
         FOR Stelle := 1 TO MaxEintraege DO
            WITH HiScoreTabelle[Stelle] DO
               Name := "Big Brother";       (* Tabelle wird initialisiert  *)
               seed := 0;
               Punkte := 0;
            END;
         END;
      ELSE                                  (* Für alle Fälle              *)
         HALT;
      END;
   END;
   Close(HiScore);
END HiScoreTabelleLaden;


PROCEDURE HiScoreEintragen();

(* Eintrag in die HiScore-Tabelle und Ausgabe der Tabelle                  *)
VAR

   name   : NameString;                     (* Name für die Hiscore-Liste  *)
   Index  : CARDINAL;                       (* Position des Eintrags in    *)
                                            (* HiScore-Tabelle             *)
   Zeile,                                   (* Zähler für die Ausgabe der  *)
   Spalte : CARDINAL;                       (* HiScore-Tabelle             *)
   term   : CHAR;                           (* Für ReadLn                  *)


BEGIN
   ClearWindow;
   IF Score > HiScoreTabelle[MaxEintraege].Punkte THEN
                                            (* Mehr Punkte als der letzte  *)
                                            (* in der Tabelle              *)
      GotoXY(5,8);
      WriteS("Du hast es geschafft, unter die Top 50 zu kommen. Gib Deinen Namen ein !");
      WriteLn;
      WriteLn;
      CurOn();
      WriteS(">>>>>>>");
      name := 0C;
      ReadLn(name,term);
      Index := MaxEintraege;

    (* Unsauber programmiert, aber es ist spät und ich habe keine Lust,
       es elegant zu programmieren. Die Reihenfolge der Bedingungen bei
       der IF-Anweisung ist jedenfalls wichtig,
       sonst kommt es zu einem Rangefehler, wenn der Score an Nummer 1
       eingetragen wird. Index-1 ist dann 0, aber die logische Verknüpfung
       wird von links nach rechts abgearbeitet und die zweite Bedingung
       wird gar nicht mehr abgearbeitet *)

      WHILE (Index > 1) AND (HiScoreTabelle[Index-1].Punkte < Score) DO
                                            (* Index wird solange dekremen-*)
                                            (* tiert, solange der Score    *)
         DEC(Index);                        (* in der Tabelle kleiner ist  *)
      END;
      FOR Stelle := MaxEintraege TO Index+1 BY -1 DO
                                            (* Alle schlechteren Scores    *)
                                            (* eins nach hinten schieben   *)
         HiScoreTabelle[Stelle] := HiScoreTabelle[Stelle-1];
      END;
      WITH HiScoreTabelle[Index] DO         (* Score + Namen eintragen     *)
         Name   := name;
         Punkte := Score;
         seed   := Seed;
      END;
   END;

   ClearWindow;
   GotoXY(0,0);

   WriteS(" Platz    Name               Punkte     ");
   WriteS(" Platz    Name                Punkte    ");

   FOR Zeile := 2 TO 26 DO                  (* 25-zeilige Ausgabe          *)
      FOR Spalte := 0 TO 1 DO               (* mit 2 Spalten               *)
         WITH HiScoreTabelle[(Zeile-1)+25*Spalte] DO
            IF (Zeile-1)+25*Spalte = Index THEN
               GotoXY(Spalte*40,Zeile);
               WriteS(">");                 (* Markierung des Eintrags     *)
            END;
            GotoXY(Spalte*40+2,Zeile);
            WriteCard((Zeile-1)+(Spalte*25),3);
            GotoXY(Spalte*40+8,Zeile);
            WriteS(Name);
            GotoXY(Spalte*40+29,Zeile);
            WriteInt(Punkte,5);
         END;
      END;
   END;

   CurOff();

END HiScoreEintragen;


PROCEDURE Initialisierung;

(* Initialisiert Tasten und Screen                                         *)

BEGIN
   TasteOben        := "W";                 (* Normale Tastenbelegung      *)
   TasteUnten       := "X";
   TasteLinks       := "A";
   TasteRechts      := "D";
   TasteLinksOben   := "Q";
   TasteLinksUnten  := "Y";
   TasteRechtsOben  := "E";
   TasteRechtsUnten := "C";

   ScrollOff();       (* Scrolling und Cursor ausschalten, Titel schreiben *)
   CurOff();
   Titel := " Greed V1.00                  © Andreas Pahl                30.Juni 1989";
   Title(Titel);
END Initialisierung;


PROCEDURE InitialisiereSchleife;

(* Initialisiert Variablen bei jedem Spielbeginn innerhalb der Schleife    *)

BEGIN
      OpenTimer(TRUE);                      (* Neue Seed-Zahl,zur Zeit wird*)
      GetTimeLong(Seed,Dummy);              (* die Sekundenzahl genommen   *)
      CloseTimer;
      GotoXY(MinX+50,MaxY+2);
      WriteS("Seed : ");
      WriteInt(Seed,10);
      PutSeed(Seed);

      FOR Zeile := MinY TO MaxY DO   (* Spielfeld mit Zufallszahlen füllen *)
         GotoXY(0,Zeile);
         FOR Spalte := MinX TO MaxX DO
            Zufallszahl := RND(9) + 1;      (*  0 < Zufallszahl < 10       *)
            Matrix[Zeile,Spalte] := Zufallszahl;
            WriteInt(Zufallszahl,1);
         END;
      END;

      Score := 0;                           (* Score unten hinschreiben    *)
      GotoXY(5,MaxY+2);
      WriteS("Score : ");

      XPosition := RND(MaxX);      (* AnfangsPosition per Zufall ermitteln *)
      YPosition := RND(MaxY);
      Matrix[YPosition,XPosition] := 0;
END InitialisiereSchleife;



BEGIN  (* Hauptprogramm *)


 (* Argument überprüfen, sonst Standardtastenbelegung beibehalten *)

   IF NumArgs() > 0 THEN
      GetArg(1,argument,Laengeargument);
      IF Laengeargument > 7 THEN
         TasteLinksOben   := CAP(argument[1]);
         TasteOben        := CAP(argument[2]);
         TasteRechtsOben  := CAP(argument[3]);
         TasteLinks       := CAP(argument[4]);
         TasteRechts      := CAP(argument[5]);
         TasteLinksUnten  := CAP(argument[6]);
         TasteUnten       := CAP(argument[7]);
         TasteRechtsUnten := CAP(argument[8]);
      END;
   END;

   Initialisierung;
   HiScoreTabelleLaden();

 (* Solange gewünscht wiederholen *)

   REPEAT

      Ende := FALSE;
      InitialisiereSchleife;

    (* Hauptschleife *)

      WHILE NOT Ende DO

         Colour(8,8,inverse);       (* aktuelle Position invers darstellen *)
         GotoXY(XPosition,YPosition);
         WriteC(Spur);
         Colour(8,8,plain);

         ScoreSchreiben();

         Eingabe := ReadC();

         GotoXY(XPosition,YPosition);       (* aktuelle Position wieder    *)
         WriteC(Spur);                      (* normal schreiben            *)

         IF CAP(Eingabe) = TasteOben THEN   (* Tastendruck auswerten       *)

                      (* Exemplarisch für die anderen Fälle mitkommentiert *)

                   IF YPosition = MinY THEN (* Falls am Rand, dann Ende    *)
                      Ende := TRUE;
                   ELSE                     (* Schrittweite ermitteln      *)
                      Schrittweite := Matrix[YPosition-1,XPosition];
                      IF YPosition < Schrittweite THEN
                         Ende := TRUE;     (* Schießt über den Rand hinaus *)
                      ELSE
                         Score := Score + Schrittweite;
                                          (* Spur zeichnen                 *)
                         FOR Zeile := YPosition-1 TO YPosition-Schrittweite BY -1 DO
                            GotoXY(XPosition,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,XPosition] # 0 THEN
                               Matrix[Zeile,XPosition] := 0;
                            ELSE          (* Alte Spur getroffen           *)
                               Ende := TRUE;
                            END;
                         END;
                         YPosition := YPosition - Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteUnten THEN
                   IF YPosition = MaxY THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition+1,XPosition];
                      IF YPosition > MaxY-Schrittweite THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Zeile := YPosition+1 TO YPosition+Schrittweite DO
                            GotoXY(XPosition,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,XPosition] # 0 THEN
                               Matrix[Zeile,XPosition] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         YPosition := YPosition + Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteLinks THEN
                   IF XPosition = MinX THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition,XPosition-1];
                      IF XPosition < Schrittweite THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Spalte := XPosition-1 TO XPosition-Schrittweite BY -1 DO
                            GotoXY(Spalte,YPosition);
                            WriteC(Spur);
                            IF Matrix[YPosition,Spalte] # 0 THEN
                               Matrix[YPosition,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition - Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteRechts THEN
                   IF XPosition = MaxX THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition,XPosition+1];
                      IF XPosition > MaxX-Schrittweite THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Spalte := XPosition+1 TO XPosition+Schrittweite DO
                            GotoXY(Spalte,YPosition);
                            WriteC(Spur);
                            IF Matrix[YPosition,Spalte] # 0 THEN
                               Matrix[YPosition,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition + Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteLinksOben THEN
                   IF (XPosition = MinX) OR (YPosition = MinY) THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition-1,XPosition-1];
                      IF (XPosition < Schrittweite) OR (YPosition < Schrittweite) THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                                            (* Bei Diagonalbewegungen wird *)
                                            (* sowohl der Zeilen- als auch *)
                                            (* der Spaltenindex verändert. *)
                                            (* Deshalb habe ich eine Lauf- *)
                                            (* variable genommen und be-   *)
                                            (* rechne daraus die Indices   *)
                         FOR Laufvariable := 1 TO Schrittweite  DO
                            Zeile  := YPosition - Laufvariable;
                            Spalte := XPosition - Laufvariable;
                            GotoXY(Spalte,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,Spalte] # 0 THEN
                               Matrix[Zeile,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition - Schrittweite;
                         YPosition := YPosition - Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteLinksUnten THEN
                   IF (XPosition = MinX) OR (YPosition = MaxY) THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition+1,XPosition-1];
                      IF (XPosition < Schrittweite) OR (YPosition > MaxY-Schrittweite) THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Laufvariable := 1 TO Schrittweite  DO
                            Zeile  := YPosition + Laufvariable;
                            Spalte := XPosition - Laufvariable;
                            GotoXY(Spalte,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,Spalte] # 0 THEN
                               Matrix[Zeile,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition - Schrittweite;
                         YPosition := YPosition + Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteRechtsOben THEN
                   IF (XPosition = MaxX) OR (YPosition = MinY) THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition-1,XPosition+1];
                      IF (XPosition > MaxX-Schrittweite) OR (YPosition < Schrittweite) THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Laufvariable := 1 TO Schrittweite  DO
                            Zeile  := YPosition - Laufvariable;
                            Spalte := XPosition + Laufvariable;
                            GotoXY(Spalte,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,Spalte] # 0 THEN
                               Matrix[Zeile,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition + Schrittweite;
                         YPosition := YPosition - Schrittweite;
                      END;
                   END;
         ELSIF CAP(Eingabe) = TasteRechtsUnten THEN
                   IF (XPosition = MaxX) OR (YPosition = MaxY) THEN
                      Ende := TRUE;
                   ELSE
                      Schrittweite := Matrix[YPosition+1,XPosition+1];
                      IF (XPosition > MaxX-Schrittweite) OR (YPosition > MaxY-Schrittweite) THEN
                         Ende := TRUE;
                      ELSE
                         Score := Score + Schrittweite;
                         FOR Laufvariable := 1 TO Schrittweite  DO
                            Zeile  := YPosition + Laufvariable;
                            Spalte := XPosition + Laufvariable;
                            GotoXY(Spalte,Zeile);
                            WriteC(Spur);
                            IF Matrix[Zeile,Spalte] # 0 THEN
                               Matrix[Zeile,Spalte] := 0;
                            ELSE
                               Ende := TRUE;
                            END;
                         END;
                         XPosition := XPosition + Schrittweite;
                         YPosition := YPosition + Schrittweite;
                      END;
                   END;
         ELSE
            Beep(TRUE);
         END;  (* CASE *)
      END;  (* WHILE NOT Ende DO *)

      Beep(FALSE);
      GotoXY(0,MaxY+4);
      WriteS("Das Spiel ist zu Ende");
      Eingabe := ReadC();                    (* Nur auf Tastendruck warten *)

      HiScoreEintragen();

      Eingabe := ReadC();                    (* Nur auf Tastendruck warten *)
      ClearWindow;

      GotoXY(5,5);
      WriteS("Willst du nochmal spielen ? (J/N)");
      NochMal := ReadC();

   UNTIL CAP(NochMal) = "N";

   Lookup(HiScore,HiScoreDosname,2048,TRUE);
   IF HiScore.res = diskWriteProtected THEN
      WriteLn;
      WriteLn;
      WriteS("Zu meinem Bedauern musste ich feststellen, daß die Diskette");
      WriteLn;
      WriteS("schreibgeschuetzt ist und ich somit deinen tollen Score");
      WriteLn;
      WriteS("nicht speichern konnte.");
      WriteLn;
      WriteLn;
      WriteS("Einen Versuch hast du noch, sonst beende ich das Spiel,");
      WriteLn;
      WriteS("ohne, daß dein Score abgespeichert wird!!!");
      Eingabe := ReadC();                    (* Nur auf Tastendruck warten *)
      Lookup(HiScore,HiScoreDosname,2048,TRUE);
      IF HiScore.res = diskWriteProtected THEN
         Close(HiScore);
         HALT;
      END;
   END;

   SetPos(HiScore,0);                       (* HiScores abspeichern        *)
   WriteBytes(HiScore,ADR(HiScoreTabelle),SIZE(HiScoreTabelle),act);
   Close(HiScore);
END Greed.

