Eine komplette Implementation des Spiels Black Box aus dem Jahre 1986,
in der ALGOL-ähnlichen Programmiersprache ELAN mit primitiver ASCII-Graphik

PACKET spielregel DEFINES gib spielregel:   (* --------------------------------
                             Version 1986-02-12, Eingabeprüfung korr.88-01-07.
Steinesuchen -- ein Kombinationsspiel für einen (oder mehrere) Spieler. Das
Spiel heißt auch "Ko-Code" (in Holland), "Black Box" (in Deutschland) ,
ursprünglich "Ordo". Es ist verwandt mit "Superhirn" (Master Mind).

Der Rechner versteckt vier oder fünf gleiche Spielsteine auf einem Spielbrett.
Die Steine bestimmen die möglichen Wege auf dem Spielbrett. Der Spieler (oder
die Spielerin) nennt den Anfang eines Weges und erfährt vom Rechner nur, wo
der Weg das Brett wieder verläßt oder wie der Weg auf dem Brett endet. Aus den
Antworten auf möglichst wenige Fragen ist auf die Lage der Steine zu schließen.

Durch Eingabe von R1 ... R8 erreicht man folgende Kapitel der Spielregel:
R1 Allgemeines, Verlauf des Spiels
R2 Fragen, probesetzen, einen Tip abgeben
R3 Zeichenerklärung
R4 Die Regel für das Abknicken des Weges
R5 Die Regel für Treffer und Unfälle
R6 Punktwertung
R7 Was man alles eingeben kann
R8 Steinesuchen für zwei oder mehr Spieler

Eingabe:     R1 (Kapitel 1);  R2, ..., R8 (Kapitel 2, ..., 8);  sp (spielen);
                       drucken: /PRINT $WEGNER.STEINE.ELAN, CHARS=YW, LINES=66
Gegeben ein Spielbrett mit acht Spalten (von a bis h) und
acht Zeilen (von 1 bis 8), also 8 x 8 Feldern von a1 bis h8.
Der Rechner versteckt darauf vier oder fünf Spielsteine.
Der Spieler stellt dem Rechner Fragen, indem er den Anfang eines Weges nennt,
zum Beispiel "von Westen in Zeile 3 hinein" (im Spiel: W3) oder "von Norden in
Spalte c hinein" (im Spiel: Nc). Der Rechner antwortet zum Beispiel "Treffer"
oder nennt die Stelle, wo der Weg das Brett wieder verläßt. (Wenn die beiden
obigen Wege gerade hindurchgehen, dann lauten die Antworten O3 und Sc).
Der Spieler soll nun die Lage der Steine raten. Jede Frage kostet einen oder
zwei Punkte, jeder falsch geratene Stein kostet fünf Punkte. Ziel des Spiels
ist es, die Gesamtzahl der Punkte je Spiel möglichst niedrig zu halten. --

Der Spieler wird nach einigen Antworten probeweise einige Steine auf das Brett
setzen. Der Rechner zeigt jedesmal das Brett mit allen bisherigen Antworten
und probegesetzten Steinen. Er markiert diejenigen Antworten mit Rufzeichen,
die mit den probegesetzten Steinen nicht verträglich sind. Wenn der Spieler
mit seiner Probesetzung zufrieden ist, kann er sie als Tip abgeben. Der Rechner
vergleicht dann den Tip mit der wirklichen Lage der versteckten Steine und
fragt zurück, wenn der Tip nicht stimmen kann. Am Ende des Spiels addiert er
für jeden falsch geratenen Stein die genannten fünf Punkte. --
Eingabe:  R2 (Fortsetzung);  sp (spielen);
                                       spielen:      /EXEC $WEGNER.STEINE.EXEC
Die Spieler können Wege erfragen, Steine probesetzen und ihren Tip abgeben.
F r a g e n   heißt den Anfangspunkt eines Weges nennen,
also einen Eingang in eine Zeile (1..8) oder Spalte (a..h).
Die Lage der Steine bestimmt die möglichen Wege auf dem Spielbrett.
Wenn ein Weg genau auf einen Stein führt, meldet der Rechner einen Treffer (T).
Zielt ein Weg unmittelbar an einem Stein vorbei, so knickt er    .  .  I  .  .
vom Stein weg, kurz bevor er die Höhe des Steines erreicht.      .  .  I  .  .
Einem durchgehenden Weg kann man also nicht ansehen, von welcher .  .  I  .  .
Seite aus der Spieler ihn erfragt hat. Ein Beispiel findet sich  .  .  '- -  -
nebenstehend: Die Neun (9) bezeichnet die Lage des Steines,      .  9  .  .  .
'-  markiert den Knick, die Zeichen I und - markieren den Weg,   .  .  .  .  .
Punkte markieren die anderen Felder. (Der Weg ist nur hier
in diesem Beispiel markiert; die Knicke werden nur bei offenem Spiel gezeigt.)
Bei jeder Frage verfolgt der Rechner einen Weg über das Brett und antwortet am
Rande des Brettes, wo der Weg das Brett wieder verläßt oder wie der Weg auf
dem Brett endet. (Mit der Zeit entwickelt man den nötigen Billard-Blick dafür.)
P r o b e s e t z e n   heißt eine Anzahl vermuteter Steine placieren
oder wieder zurücknehmen.
A b g e b e n   heißt die Probe-Placierung verbindlich machen. Der Rechner
setzt dann Sterne dort, wo die Steine versteckt waren. --
Eingabe:  R3 (Fortsetzung);  sp (spielen);

Eine Frage besteht aus einem Buchstaben für die Himmelsrichtung, aus welcher
der Weg ins Spielfeld eintreten soll, und der Bezeichnung der Zeile oder
Spalte, also Na .. Nh, O1 .. O8, Sa .. Sh oder W1 .. W8.
Die Umrandung des Spielfeldes auf dem Bildschirm besteht genau aus diesen
Bezeichnungen aller 32 möglichen Fragen (Darstellung im nächsten Kapitel).
Unmittelbar außerhalb dieser Berandung notiert der Rechner die Antworten.
Jede Antwort besteht aus der gleichartigen Angabe, wo der Weg das Spielfeld
wieder verläßt, oder aus einem T (für Treffer) oder aus einem U (für
Unfall, siehe Kapitel R5), dazu ein Rufzeichen, wenn die Antwort es nötig
macht, den bisherigen Tip zu überdenken.
Sobald der Spieler die Lage eines Steines raten kann, wird er sie dem Rechner
mitteilen: Der Spieler tippt die vermutete Position und eine Ziffer zwischen
1 (sehr unsicher) und 9 (sicher), z.B.  e31 e49  für die Positionen e3 und e4.
(Man kann statt einer 5 hinter der Position auch ein Leerzeichen tippen.)
Der Unterschied zwischen den Ziffern hat nur Merkwert für den Spieler, aber
keine Wirkung auf das Spiel. -- Man nimmt einen probegesetzten Stein wieder
weg, indem man hinter der Position ein anderes Zeichen als 1 .. 9 tippt,
zum Beispiel ein Leerzeichen. Man kann auch Merkzeichen setzen, z.B. e3? e4a.
Die Eingabe von Position und Leerzeichen wechselt also zwischen Setzen und Weg-
nehmen.- Die genauen Regeln für die Wege folgen auf den Bildschirmen R4 und R5.
Eingabe: R4 (Fortsetzung);  sp (spielen);

a) Solange kein Stein im Wege ist und auch kein Stein auf einem Feld liegt,
  das dem Weg unmittelbar benachbart ist, führt der Weg geradeaus.
b) Ist das nächste Feld frei, aber das Feld links voraus ist besetzt,
  und rechts voraus ist kein besetztes Feld,
  dann knickt auf der Stelle der Weg nach rechts um.
c) Dasselbe wie b, aber rechts und links vertauscht.
d) Wenn der Weg das Brett verläßt, meldet der Rechner beide Enden des Weges.
Beispiel (bei "9" liegt ein Stein, beim Komma oder Apostroph knickt ein Weg):
        Nd    Nb    Ng Nf         <=== hier stehen Antworten!
     Na Nb Nc Nd Ne Nf Ng Nh      Bisher 12 Punkte
  W8  . '-  . -'  . '- -'  . O8
  W7  9  .  .  .  9  .  .  9 O7    (N=Nord, O=Ost, S=Süd, W=West)
  W6  . ,-  . -,  . ,- -,  . O6  
O5 W5  .  .  .  .  .  .  .  . O5 W5 (dieser Weg geht geradeaus, aber
O4 W4  . -'  . '-  .  .  .  . O4 W4         ... dieser nur scheinbar.)
  W3  .  .  9  .  .  .  .  . O3    (Sb-W2 knickt nur einmal, aber
Sb W2  . -,  .  .  . '-  .  . O2 Sg         ... dieser Weg knickt dreimal.)
  W1  .  .  .  .  9  .  .  . O1  
     Sa Sb Sc Sd Se Sf Sg Sh       (Leider zeigt das Komma in b6 nicht genau
        W2             O2                  auf den Apostroph in b4.)
Eingabe:  R5 (Fortsetzung);  sp (spielen);
                              /* hierunter das Beispiel mit 26 T+U nehmen ? */
e) Ist das nächste Feld in Richtung des Weges besetzt, dann
  meldet der Rechner einen Treffer (T), und der Weg endet.
f) Ist das nächste Feld frei, aber die beiden Felder links voraus
  und rechts voraus sind besetzt, dann meldet der Rechner
  einen Unfall (U), und der Weg endet.
g) Würde nach Regel b oder c der Weg vor Eintritt in das Spielfeld
  umknicken, so meldet der Rechner ebenfalls einen Unfall (U).
Beispiel:
      T  U  T  U  T     T      
     Na Nb Nc Nd Ne Nf Ng Nh      Bisher 24 Punkte
T W8  .  .  9  .  .  .  .  . O8  T
  W7  .  .  . ,-  .  .  .  . O7  U   <=== Unfall in d6.
  W6  .  .  .  .  .  .  .  . O6  
T W5  .  .  9  .  9  .  .  . O5  T
  W4  .  .  .  .  . ,-  .  . O4  T   <=== O4 trifft auf a2.
U W3  .  .  .  .  .  .  .  . O3  
T W2  9  .  .  .  . -'  .  . O2  
U W1  . ,-  .  .  .  .  9  . O1  T
     Sa Sb Sc Sd Se Sf Sg Sh              (Alle Treffer und Unfälle
      T  T  T  U  T  U  T  U               sind eingetragen.)
Eingabe:  R6 (Fortsetzung);  sp (spielen);

Treffer und Unfälle kosten je einen Punkt. Jeder andere Weg kommt irgendwo
wieder heraus, liefert zwei Antworten und kostet deshalb zwei Punkte.
Eine Frage kann also einen Punkt oder zwei Punkte kosten.
Jeder falsch geratene Stein kostet fünf Punkte. --

Man kann die angezeigte Punktzahl jederzeit wie folgt nachprüfen:
Jeder Antwortvermerk am Rande kostet einen Punkt, und wenn das Spiel
zuende ist, kommen für jeden falsch gesetzten Stein fünf Punkte hinzu.
Wenn zuwenige oder zuviele Steine gesetzt wurden, gilt entweder die Zahl der
an falscher Stelle gesetzten oder die Zahl der nicht getroffenen Steine,
je nachdem, welche der beiden Anzahlen größer ist. Kann oder will man
nicht alle Steine lokalisieren, so rät man deshalb die letzten Steine;
man spart nichts, wenn man weniger Steine setzt, als zu suchen sind. --

Mit Erfahrung und viel Sorgfalt ist bei 5 Steinen ein Durchschnitt von
16 Punkten erreichbar. Im günstigsten Fall liegt mit 5 Antwortpunkten
die Position aller 5 Steine fest; im ungünstigsten Fall
kann sich der fünfte Stein auf einem von sechs oder vielleicht
noch mehr Plätzen so verstecken, daß keine Frage ihn erreicht. --

Eingabe:  R7 (Fortsetzung);  sp (spielen);
                         /* später vielleicht Punkte für verstrichene Zeit! */
Anfänger spielen mit 4 Steinen, Fortgeschrittene mit 5 (möglich sind 1 bis 9).
>>   Anfänger sollten zur Übung einige Male    o f f e n    spielen:     <<
>>   04 oder 05 tippen (null-vier, nicht oh-vier), dann die Taste DÜ1.   <<
Zuerst Z tippen und alle Wege verfolgen, bis man jede Antwort versteht.
Später einzelne Antworten vorhersagen, dann fragen und vergleichen. --
Gleich hinter der Eingabe der Steinezahl und auch später darf man
die folgende Wünsche als Serie in   e i n e r   Zeile eingeben:
-- Frage:       Na .. Nh, O1 .. O8, Sa .. Sh, W1 .. W8
-- Probesetzen: a1 .. h8 gefolgt von einer der Ziffern 1 bis 9
-- Wegnehmen:   a1 .. h8 gefolgt von einem anderen Zeichen (z.B. Punkt)
-- Abgeben:     z wie "zeigen" (wenn der Tip nicht offensichtlich unmöglich);
               es gelten dann nur 1 .. 9; erst danach geht E (Ende).
Probesetzen und Wegnehmen geht nicht bei offenem Spiel.
Zwischen je zwei Wünsche bitte ein Leerzeichen tippen. --
Man kann jederzeit das zuletzt gezeigte Brett dokumentieren: Man tippt als Be-
ginn einer Eingabe ein Rufzeichen (!) und dahinter einen kommentierenden Text.
Dann wird das Brett mit dem Text in eine Datei STEINE.BRETTER gespeichert. --
Für Spiele mit 5 Steinen wird eine Ergebnis-Statistik geführt. Bei Spiel-Ende
(E) wird die Statistik in eine Datei STEINE.ZAHLEN2 protokolliert. (Die darf
man ganz oder teilweise löschen; nur die letzte Zeile wird ausgewertet.) --
Eingabe:  R (Inhaltsverzeichnis dieser Anleitung); sp (spielen);

Das Spiel zu mehreren ist ein reiner Wettbewerb, das heißt kein Spieler kann
einen anderen behindern.

Wenn man zu mehreren spielen will, gibt es drei Möglichkeiten:

(a) eng gekoppelt: Die Spieler fragen reihum (bisher nur am selben Sichtgerät).
   Jeder entscheidet für sich, wann er seinen Tip abgibt.
   Er wirft dann seinen Tip verdeckt in eine Urne und
   notiert offen die Kosten der bisherigen Antworten laut Bildschirm.
   Wer abgibt, scheidet damit aus dem laufenden Spiel aus.
   (Probesetzen muß bisher noch jeder für sich verdeckt.)

(b) lose gekoppelt: In eine geschützte Datei werden die gemeinsamen Aufgaben
   für jeden Tag geschrieben; jeder fragt für sich.
   (Auch noch nicht programmiert.)

(c) ungekoppelt: Jeder spielt unabhängig von den anderen eine vereinbarte
   Anzahl von Spielen, und man vergleicht hinterher.
   (Das geht jetzt schon.)

Eingabe:  R (Inhaltsverzeichnis dieser Anleitung), sp (spielen);
                              ----------------------------------------------*)
PROC gib spielregel (INT CONST kapitelnummer):
FILE VAR alle regeln :: sequential file (input, "$WEGNER.STEINE.ELAN");
INT VAR kap, i;   TEXT VAR line of text;
IF kapitelnummer<0 OR kapitelnummer>8 THEN kap:= 0 ELSE kap:= kapitelnummer FI;
FOR i FROM 0 UPTO 22 * kap REP getline (alle regeln, line of text) PER;
FOR i FROM 1 UPTO 21
REP getline (alle regeln, line of text); putline (line of text) PER
END PROC gib spielregel; (*--------------------------------------------------*)
END PACKET spielregel; (*====================================================*)


PACKET brettverwaltung
DEFINES verstecke, frage, setze, zeig brett, ende eines spiels:
LET b= "   ", p= " . ",  BRETT = ROW 12 ROW 12 TEXT;
BRETT CONST leerbrett :: BRETT:
(ROW 12 TEXT : ( b,   b,   b,   b,   b,   b,   b,   b,   b,   b,   b,   b),
ROW 12 TEXT : ( b, b,"Sa ","Sb ","Sc ","Sd ","Se ","Sf ","Sg ","Sh ", b, b),
ROW 12 TEXT : ( b, "W1 ",   p,   p,   p,   p,   p,   p,   p,   p, "O1 ", b),
ROW 12 TEXT : ( b, "W2 ",   p,   p,   p,   p,   p,   p,   p,   p, "O2 ", b),
ROW 12 TEXT : ( b, "W3 ",   p,   p,   p,   p,   p,   p,   p,   p, "O3 ", b),
ROW 12 TEXT : ( b, "W4 ",   p,   p,   p,   p,   p,   p,   p,   p, "O4 ", b),
ROW 12 TEXT : ( b, "W5 ",   p,   p,   p,   p,   p,   p,   p,   p, "O5 ", b),
ROW 12 TEXT : ( b, "W6 ",   p,   p,   p,   p,   p,   p,   p,   p, "O6 ", b),
ROW 12 TEXT : ( b, "W7 ",   p,   p,   p,   p,   p,   p,   p,   p, "O7 ", b),
ROW 12 TEXT : ( b, "W8 ",   p,   p,   p,   p,   p,   p,   p,   p, "O8 ", b),
ROW 12 TEXT : ( b, b,"Na ","Nb ","Nc ","Nd ","Ne ","Nf ","Ng ","Nh ", b, b),
ROW 12 TEXT : ( b,   b,   b,   b,   b,   b,   b,   b,   b,   b,   b,   b) );
BRETT VAR setzbrett, ratebrett :: leerbrett;
ROW 12 TEXT VAR zeilen;
BOOL VAR offen, mittendrin :: FALSE, plausibel, druckbereit :: FALSE;
INT VAR x, y (* Koordinaten auf dem Brett *), dx, dy (* Schrittweite *),
   punkte :: 0, spielezahl :: 0, punktsumme :: 0, steinezahl, probegesetzt;
(* ratebrett und punkte initialisieren für Druckwunsch vor dem ersten Spiel *)

PROC verstecke (BOOL CONST offenes spiel, INT CONST anzahl steine):
INT VAR stein;  steinezahl:= anzahl steine;
offen:= offenes spiel; mittendrin:= TRUE; druckbereit:= FALSE;
setzbrett:= leerbrett; ratebrett:= leerbrett; punkte:= 0;
FOR stein FROM 1 UPTO steinezahl REP verstecke einen stein PER.

verstecke einen stein:
REP x:= random (3,10); y:= random (3,10) UNTIL setzbrett (y)(x) <> " 9 " PER;
setzbrett (y)(x):= " 9 "; IF offen THEN ratebrett (y)(x):= " 9 " FI.
END PROC verstecke; (*-------------------------------------------------------*)

PROC frage (INT CONST x1, y1, dx1, dy1):   (* Param: Koordinaten und Schritt *)
IF ratebrett (y1-dy1)(x1-dx1) <> "   " THEN LEAVE frage FI;
druckbereit:= FALSE; x:= x1; y:= y1; dx:= dx1; dy:= dy1;
ratebrett (y1-dy1)(x1-dx1) := antwort frisch berechnet (setzbrett);
IF (ratebrett (y1-dy1)(x1-dx1) SUB 1) = " "      (* Treffer oder Unfall *)
THEN punkte:= punkte + 1
ELSE punkte:= punkte + 2; ratebrett (y+dy) (x+dx) := leerbrett (y1)(x1) FI
END PROC frage; (*-----------------------------------------------------------*)

TEXT PROC antwort frisch berechnet (BRETT CONST lesebrett):
BOOL VAR vor eintritt :: TRUE;   INT VAR temp;
ROW 5 TEXT CONST winkel :: ROW 5 TEXT: ("'- ", "-' ", "bla", ",- ", "-, ");
REP treffer unfall knick weiter UNTIL leerbrett (y)(x) <> p PER;
leerbrett (y)(x).

treffer unfall knick weiter:
TEXT VAR cl, cr;                     (* Brett-Inhalt links und rechts voraus *)
IF " 1 " <= lesebrett (y+dy)(x+dx) AND lesebrett (y+dy)(x+dx) <= " 9 "
THEN LEAVE antwort frisch berechnet WITH " T " FI;      (* geradeaus besetzt *)
cl:= lesebrett (y+dy+dx)(x+dx-dy); cr:= lesebrett (y+dy-dx)(x+dx+dy);
IF " 1 " <= cl AND cl <= " 9 "                       (* links voraus besetzt *)
THEN IF vor eintritt OR (" 1 " <= cr AND cr <= " 9 ")
    THEN LEAVE antwort frisch berechnet WITH " U " (* rechts voraus besetzt *)
    ELSE rechts um FI
ELIF " 1 " <= cr AND cr <= " 9 "                (* nur rechts voraus besetzt *)
THEN IF vor eintritt
    THEN LEAVE antwort frisch berechnet WITH " U "
    ELSE links um FI
FI;  vor eintritt:= FALSE; x:= x+dx; y:= y+dy.

links um:
IF offen THEN ratebrett(y)(x):= winkel (2*dy -dx +3) FI;
temp:= dx; dx:= -dy; dy:= temp.

rechts um:
IF offen THEN ratebrett(y)(x):= winkel (2*dx +dy +3) FI;
temp:= dy; dy:= -dx; dx:= temp.
END PROC antwort frisch berechnet; (*----------------------------------------*)

PROC setze (INT CONST x, y, TEXT CONST char3):
druckbereit:= FALSE;
IF char3 <> "" AND char3 <>" " THEN ratebrett (y)(x) := " " + char3 + " "
ELIF ratebrett (y)(x) >= " 1 " AND ratebrett (y)(x) <= " 9 "
THEN ratebrett (y)(x):= " . " ELSE ratebrett (y)(x) := " 5 " FI
END PROC setze; (*-----------------------------------------------------------*)

PROC zeig brett (TEXT CONST kommentar):
INT VAR z;
IF NOT druckbereit THEN bereite zum druck FI;
IF kommentar = ""
THEN FOR z FROM 12 DOWNTO 1 REP putline (zeilen(z)) PER    (* nur Bildschirm *)
ELSE FILE VAR bretter :: sequential file (output, "STEINE.BRETTER");
    FOR z FROM 12 DOWNTO 1
    REP putline (zeilen (z)); putline (bretter, zeilen (z)) PER;
    putline (kommentar);      putline (bretter, kommentar);
    putline (" ");            putline (bretter, " "); close (bretter); putline
    ("Obiges wird dokumentiert: Datei STEINE.BRETTER (CHARS=YW,LINES=56)")
FI.

bereite zum druck:
IF mittendrin AND NOT offen THEN setze rufzeichen und 2 globale FI;
FOR z FROM 12 DOWNTO 1 REP
   zeilen (z):= ratebrett(z)(1) + ratebrett(z)(2) + ratebrett(z)(3)
        + ratebrett(z)(4) + ratebrett(z)(5) + ratebrett(z)(6)
        + ratebrett(z)(7) + ratebrett(z)(8) + ratebrett(z)(9)
        + ratebrett(z)(10) + ratebrett(z)(11) + ratebrett(z)(12) + nachlauf
PER; druckbereit:= TRUE.

nachlauf:
IF  z=11 THEN oberes vorwort + text (punkte) + oberes nachwort
ELIF z>1 OR offen THEN ""
ELIF mittendrin THEN drintext
ELIF spielezahl >= 1 AND steinezahl = 5 THEN text (punktsumme) + statistik
ELSE "" FI.

oberes vorwort:
IF mittendrin THEN ("Bisher ") ELSE ("Endstand ") FI.

oberes nachwort:
IF punkte = 1 THEN " Punkt" ELSE " Punkte" FI.

drintext:
IF plausibel THEN "Ihr Tip widerspricht keiner Antwort."
ELIF probegesetzt <> steinezahl
THEN text (steinezahl) +" Steine sind zu suchen; gesetzt " +text (probegesetzt)
ELSE "Bitte Rufzeichen (!) beachten." FI.

statistik:
IF spielezahl=1 THEN " Punkte im ersten Spiel"
ELSE " Punkte in " + text (spielezahl) + " Spielen; Mittel "
    + text (real(punktsumme)/real(spielezahl))
FI.
END PROC zeig brett; (*------------------------------------------------------*)

BOOL PROC ende eines spiels (BOOL CONST unbedingt zeigen, INT VAR ergebnis):
LET strafe=5;  INT VAR zuviel :: 0, zuwenig :: 0;  TEXT VAR tx;
druckbereit:= FALSE; ergebnis:= (* irgendwas *) 0;
IF offen THEN mittendrin:= FALSE; LEAVE ende eines spiels WITH TRUE FI;
setze rufzeichen und 2 globale; (* darunter BOOL VAR plausibel *)
IF   plausibel OR unbedingt zeigen
THEN FOR y FROM 10 DOWNTO 3 REP
    FOR x FROM 3 UPTO 10 REP  (* vergleiche gesetzte und geratene Steine *)
        tx:= ratebrett (y)(x) SUB 2;
        IF setzbrett (y)(x) = " 9 "
        THEN ratebrett (y)(x) := "*" + tx + " ";
             IF tx < "1" OR "9" < tx THEN zuwenig:= zuwenig + strafe FI
        ELIF "1" <= tx AND tx <= "9" THEN  zuviel:= zuviel  + strafe FI
    PER PER; mittendrin:= FALSE;
    IF zuviel > zuwenig           (* bestrafe auch den, der 64 Steine setzt *)
    THEN punkte:= punkte + zuviel
    ELSE punkte:= punkte + zuwenig FI;   ergebnis:= punkte;
    IF steinezahl = 5
    THEN spielezahl:= spielezahl +1; punktsumme:= punktsumme + punkte FI; TRUE
ELSE putline ("Kann gar nicht sein. -- Trotzdem zeigen: zz eingeben.");  FALSE
FI
END PROC ende eines spiels; (*-----------------------------------------------*)

PROC setze rufzeichen und 2 globale:     (* nämlich: probegesetzt, plausibel *)
INT VAR j, x1, y1;  TEXT VAR tp;
probegesetzt:= 0;
FOR y FROM 10 DOWNTO 3 REP
 FOR x FROM 3 UPTO 10 REP
   IF (ratebrett (y)(x) SUB 2) >= "1" AND (ratebrett (y)(x) SUB 2) <= "9"
   THEN probegesetzt:= probegesetzt +1 FI
PER PER;
plausibel:=  probegesetzt = steinezahl;
FOR j FROM 3 UPTO 10 REP x:= 2; y:= j; dx:= 1; dy:= 0; plausi;
                        x:= j; y:= 2; dx:= 0; dy:= 1; plausi;
                        x:=11; y:= j; dx:=-1; dy:= 0; plausi;
                        x:= j; y:=11; dx:= 0; dy:=-1; plausi
PER.                 (* wird nur gerufen, wenn "mittendrin" und "NOT offen". *)
              (* wie macht man x, y, dx, dy zu Parametern von "antwort .."? *)
plausi:
x1:= x-dx; y1:= y-dy; tp:= subtext (ratebrett (y1) (x1), 1, 2);
IF tp = "  " THEN (* nichts *)
ELIF tp = subtext (antwort frisch berechnet (ratebrett), 1, 2)
THEN ratebrett (y1) (x1) := tp + " "
ELSE ratebrett (y1) (x1) := tp + "!"; plausibel := FALSE FI.
END PROC setze rufzeichen und 2 globale; (*----------------------------------*)
END PACKET brettverwaltung; (*===============================================*)

PROC steine suchen:
BOOL VAR offen, mittendrin :: FALSE;
INT VAR len, posi, vorpunkte :: 0, vorspiele :: 0, steinezahl, punkte,
       punktsumme :: 0, spielezahl :: 0;
TEXT VAR eingabe, ein1 :: "X", ein2;
initialize random (cpu time MOD 512);   (* unter BS2000; im EUMEL nur notfalls:
initialize random (int(subtext(time of day(clock(1)),4,5)));                 *)
putline
  ("Steinesuchen -- ein Kombinationsspiel für einen (oder mehrere) Spieler.");
putline ("+  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +");
putline ("Neu: Steinezahl und erste Fragen in   e i n e r   Eingabe möglich.");
putline ("Neu: Eingabe von Position+Leerzeichen = wechseln: setzen/wegnehmen");
IF exists ("STEINE.ZAHLEN2") THEN vorige spielergebnisse FI;
REP IF mittendrin
   THEN IF ein1 <> "!" AND ein1 <> "r" AND ein1 <>"R" THEN zeig brett ("") FI;
        putline
       ("Eingabe:  ? (das heißt Auskunft) oder eine Folge von Wünschen ===>");
   ELSE putline
     ("Eingabe:  R (Spielregel); 05 oder 4 oder 5 (ein Spiel); E (Ende) ===>")
   FI;
   getline (eingabe); len:= length (eingabe);
   posi:= 1; verarbeite eine zeile
PER.

vorige spielergebnisse:
INT VAR pos1, pos2, pos3;
FILE VAR vorig :: sequential file (input, "STEINE.ZAHLEN2");
WHILE NOT eof (vorig) REP getline (vorig, eingabe) PER;
close (vorig);             (* im EUMEL nicht nötig *)
pos1:= pos (eingabe, "t"); pos2:= pos1 + pos (subtext (eingabe, pos1+1), "/");
pos3:= pos2 + pos (subtext (eingabe, pos2+1), "=");
vorpunkte:= int(subtext (eingabe, pos1+1, pos2-2));
vorspiele:= int(subtext (eingabe, pos2+1, pos3-2));
putline ("Voriges Mal:" + subtext (eingabe, 8) ).

verarbeite eine zeile:
WHILE posi <= len
REP ein1:= eingabe SUB posi; ein2:= eingabe SUB (posi + 1);
 IF ein1 = "r" OR ein1 = "R"  (* R oder R1, ..., R8 *)
 THEN gib spielregel (code ((eingabe+"0") SUB 2) - code("0"));
      LEAVE verarbeite eine zeile
 ELIF ein1 = " " THEN posi:= posi + 1
 ELIF ein1 = "!"
 THEN zeig brett (subtext (eingabe +" ", posi+1)); LEAVE verarbeite eine zeile
 ELSE ein wunsch FI          (*    +" "   hierüber fängt leeren Kommentar! *)
PER.

ein wunsch:
IF mittendrin THEN versuch ELSE neues spiel oder ende FI;
IF ein1 <> "s" OR ein1 <> "p"  (* <> Weiterspielen nach Zeigen der Regel *)
THEN put ("Eingabe hier nicht gültig:"); put (subtext(eingabe, posi)); line FI;
LEAVE verarbeite eine zeile.

versuch:
INT VAR i;  ein2:= eingabe SUB (posi + 1);
IF   ein1 = "?" THEN auskunft; LEAVE verarbeite eine zeile
ELIF ein1 = "z" OR ein1 = "Z"                                 (* Spiel-Ende *)
THEN IF offen
    THEN FOR i FROM 3 UPTO 10 REP frage (2, i, 1, 0) ;  frage (i, 2, 0, 1);
            frage (11, i, -1, 0); frage (i, 11, 0, -1)  PER
    FI;
    mittendrin:= NOT ende eines spiels (ein2="z" OR ein2="Z", punkte);
    IF steinezahl = 5 AND NOT offen AND NOT mittendrin
    THEN punktsumme:= punktsumme + punkte; spielezahl:= spielezahl + 1 FI;
    IF NOT mittendrin THEN zeig brett ("") FI; LEAVE verarbeite eine zeile
ELIF "a"<=ein2 AND ein2<="h"                                (* Spaltenfrage *)
THEN IF   ein1="n" OR ein1="N"        (* südwärts *)
    THEN frage (code (ein2) - code ("a") + 3, 11, 0, -1);
         posi:= posi + 2; LEAVE ein wunsch
    ELIF ein1="s" OR ein1="S"        (* nordwärts *)
    THEN frage (code (ein2) - code ("a") + 3, 2, 0, 1);
         posi:= posi + 2; LEAVE ein wunsch
    FI
ELIF "1"<=ein2 AND ein2<="8"                 (* Zeilenfrage oder Probesetzen *)
THEN IF   ein1="o" OR ein1="O"        (* westwärts *)
    THEN frage (11, code (ein2) - code ("1") + 3, -1, 0);
         posi:= posi + 2; LEAVE ein wunsch
    ELIF ein1="w" OR ein1="W"        (* ostwärts *)
    THEN frage (2, code (ein2) - code ("1") + 3, 1, 0);
         posi:= posi + 2; LEAVE ein wunsch
    ELIF "a"<=ein1 AND ein1<="h" AND NOT offen               (* Probesetzen *)
    THEN setze (code (ein1) - code ("a") + 3, code (ein2) - code ("1") + 3,
         eingabe SUB (posi + 2)); posi:= posi + 3; LEAVE ein wunsch
    FI
FI.

neues spiel oder ende:
INT VAR ort;
IF ein1 = "?" THEN gib spielregel (0); LEAVE verarbeite eine zeile
ELIF ein1 = "e" OR ein1 = "E"
THEN IF spielezahl > 0 THEN protokolliere FI; LEAVE steine suchen
ELSE offen:= ein1="0"; IF offen THEN ort:= posi + 1 ELSE ort:= posi FI;
   steinezahl := int (eingabe SUB ort);
   IF last conversion ok AND 1 <= steinezahl AND steinezahl <= 9
   THEN verstecke (offen, steinezahl); mittendrin:= TRUE;
        posi:= ort + 1; LEAVE ein wunsch
FI  FI.

auskunft:
putline ("Folgende Wünsche sind vorgesehen (Eingabe R zeigt die Spielregel):");
putline ("-- Frage:       Na .. Nh, O1 .. O8, Sa .. Sh, W1 .. W8");
IF offen
THEN putline ("-- zeige alle Antworten:   z;    erst danach geht E (Ende).")
ELSE putline ("-- Probesetzen: a1 .. h8 gefolgt von Ziffer 1 .. 9;");
 putline ("   wieder wegnehmen: a1 .. h8 gefolgt von einem anderen Zeichen");
 putline (
"-- Abgeben:     z wie 'zeigen' (wirkt nur, wenn der Tip stimmen   k a n n );"
); putline (
"                es gelten dann nur die 1 .. 9; erst danach geht E (Ende).")
FI;
putline ("Zwischen je zwei Wünsche bitte ein Leerzeichen tippen.").

protokolliere:
INT VAR ganz, rest;  REAL VAR mittel; TEXT VAR tx;
FILE VAR protokoll :: sequential file (output,"STEINE.ZAHLEN2");
vorpunkte:= vorpunkte + punktsumme; vorspiele:= vorspiele + spielezahl;
mittel:= real (vorpunkte) / real (vorspiele);
ganz:= int (mittel); rest:= vorpunkte - ganz * vorspiele;
tx:= "diesmal " + text(punktsumme) + " / " + text(spielezahl) + " = "
 + text (real (punktsumme) / real (spielezahl)) + "; insgesamt "
 + text (vorpunkte) + " / " + text (vorspiele) + " = " + text (mittel);
IF rest<>0 THEN tx:= tx + " = " + text(ganz) + " Rest " + text (rest) FI;
putline (protokoll, tx); putline (tx).
END PROC steine suchen; (*---------------------------------------------------*)

BOOL PROC exists (TEXT CONST name):                 (* im EUMEL vordefiniert *)
 disable stop;
 bs2000cmd ("/SETOPT OUT=RS");
 bs2000cmd ("/FSTAT " + name);                      (* Existiert die Datei? *)
 bs2000cmd ("/SETOPT OUT=A");
 IF is error
 THEN clear error; enable stop; FALSE
 ELSE enable stop; name <> "" FI
END PROC exists; (*----------------------------------------------------------*)

steine suchen.