Computer Chess Club Archives


Search

Terms

Messages

Subject: Re: Che++

Author: Eelco de Groot

Date: 16:38:45 09/02/00

Go up one level in this thread


On September 01, 2000 at 14:01:28, ujecrh wrote:

>On September 01, 2000 at 04:43:51, Alexander Kure wrote:
><snip>
>>>what is the function of wbnimzo2000b.cmp?
>>
>>This is the compiled version of the chess knowledge one can program in Che++.
>>Che++ is a special programing language for expressing chess terms. With Che++
>>you can define patterns and rules according to these patterns.
>>The engine refers to the that knowledge during its search.
>>Che++ has also been implemented in Nimzo 7.32 and will also be implemented in
>>Nimzo 8.
>>
>>Greetings
>>Alex
>
>Is there a reference or some documentation about Che++ somewhere ? I am curious
>about the format of such a language capable of expressing chess knowledge.
>
>Years ago J.C.Weil (co-author of Virtal Chess engine among others) wrote a paper
>about a kind of oracle capable of managing pawn structures. If I remember
>correctly the oracle supported a particular language to express such knowledge.
>Since this paper I have not heard about this approach anymore but I like the
>idea.
>
>Regards,
>
>Ujecrh

Unfortunately the Nimzo-Werkstatt is no longer on the web, it was at
http://www185.l4.xodox.com/index.htm. I hijacked some documentation for Che++
but it is in German:

News


Altmelon, 17.04.99

CHE++ Dokumentation:

Version 1.1

Ch.Donninger, NimzoWerkstatt



Vorwort, Geschichte:

Che wurde ursprünglich von Ch.Donninger in Nimzo3 implementiert. Die Eingabe
erfolgte in Nimzo3 über ein graphisches Interface (analog den ChessBase
and,or,not-Brettern). Die graphische Eingabe wurde anschließend automatisch in
Che übertragen und von dort mit Hilfe eines Compilers in einen virtuellen
Maschinencode übersetzt. Che konnte auch direkt eingeben werden. Die Sprache war
auf das graphische Interface zugeschnitten und es fehlte die Fexibilität einer
allgemeinen Programmiersprache. Diesen Mangel hat Andreas Mader durch einen
Entwurf für sehr mächtige Schachmuster Befehle behoben. Ferner hat Ch.Donninger
für die Realisierung einer Schachschule eine eigene Programmiersprache
entwickelt. Che++ ist die Vereinigung dieser Programmiersprache mit den neuen
Schachbefehlen. Die beiden "+" sind in Che++ tatsächlich angebracht. Eines steht
für die Maderschen Muster, das andere für die Donningerische Programmiersprache.
Che++ ist zu Che nicht aufwärts kompatibel. Es sollte aber eine reine
Routinearbeit sein, den ursprünglichen Che Code in Che++ zu übertragen. Wenn im
Folgenden von Che die Rede ist, wird immer Che++ gemeint.

Warum soll man Che lernen?

A) Um Nimzo zu verbessern.

B) Um eine Programmsprache zu lernen. Che ist wahrscheinlich eine ideale
Einsteigersprache. Es ist wesentlich leichter zu erlernen wie C, enthält aber
fast alle wichtigen Konstrukte. Wer einmal Che kann, für den ist C, Java oder
Pascal keine Hexerei mehr.

C) Um einfach herumzuspielen und zu schauen: Was macht ein Schachprogramm, wen
ich ihm diese und jene Regel eingebe (meistens ganz etwas Anderes als man sich
ausgedacht hat)?



Änderungen gegenüber Version 1.0:

a) Beseitigung von Fehlern im Interpreter und im mitgelieferten Che-Code.

b) Es ist nun möglich Resultate und Texte in ein eigenes Fenster in der
ChessBase Oberfläche auszugeben. Zu diesem Zweck wurde der neue Datentyp text
und die Ausgabefunktion TextOut(), SetVerbose() eingeführt. Diese Änderungen
sind in V.9) beschrieben.



c) Neue Funktionen zur Beeinflußung des Spielstiles. GetStyle(), SetStyle(),
GetScore(). Siehe das neue Kapitel IV.6).

d) Hinzufügung von Konstanten zur Unterstützung der neuen Funktionen.



ACHTUNG!! Die ChessBase-Engine mit dem Che++ V1.1. Interpreter benötigt die neue
ChessBase Oberfläche nimzo99.exe vom 14.04.99 (siehe Installation).



I) Installation:

nimzo99.exe vom 14.04.99 und Frres32.dll vom 24.02.99 in ChessBase/Nimzo99
Direktory kopieren (bestehende Files überschreiben).



nimzo 99.eng und nimzo99.dll in ChessBase/Engines Direktory kopieren. Enthält
neue Nimzo Engine mit Che Interpreter.



FreeShanny.ini in ChessBase/Nimzo99 Direktory kopieren. Nimzo99.dll nimmt den
Eintrag in CheCodeName und lädt das entsprechende compilierte Che++ File. Dieser
Eintrag muß geändert werden, falls das Che File woanders steht oder anders
benannt wird.

Anmerkung: Shanny ist der Shannon Weltmeister Pokal (ein Holzpferd). Shanny
steht in Hamburg in einem trostlosen modernen Bürokomplex. Shanny träumt von den
Weiden Altmelons und soll im Juni 99 befreit werden.



che.exe: Che++ Entwicklungs-Umgebung. Neues Direktory (z.B. c:\che) machen und
hineinkopieren.



shanny.cmp, shanny.che, euwe.che, midgame.che, endgame.che, bednorz.che,
stdlib.che: Ebenfalls in das Che-Direktory kopieren. Bisheriger Nimzo98 Code in
Che++. Die neue nimzo99.dll verwendet bereits diesen Code. Die Ausführung
beginnt in Shanny.che. Die Files können nur gemeinsam übersetzt werden.

Diese Files sind auch ein Che++ Beispielcode. Die Kommentare in diesen Files
sind ebenfalls Teil der Che-Sprachdokumentation. Der Code in den Beispielfiles
hat gegenüber diesem Dokument einen großen Vorteil. Er wurde vom Compiler
Korrektur gelesen. Wenn daher eine im Folgenden beschriebene Funktion vom
Compiler verschmäht wird, sollte man in den Files nach dieser (oder einer so
ähnlich geschriebenen) Funktion suchen. Es wurden aber nicht alle Funktionen
verwendet, weil die Nimzo98 Ratschläge sehr primitiv sind und daher nicht der
volle Umfang von Che++ ausgeschöpft werden mußte.



II) Che++ Developer Suite:

Ist ein leicht verändertes WordPad. Alle Editierbefehle funktionieren wie in
WordPad (siehe auch Online Hilfe). Che++ Code muß im Textformat (Standardendung
.che) abgespeichert werden. Man kann aber auch aus anderen Editoren Text
einlesen. Der Compiler speichert selbstständig ab, falls sich der Text seit der
letzten Compilierung geändert hat.

Zum compilieren Menü Build/Compile (Alt+C) auswählen. Es wird das aktuell
geladene Che-File (und alle inkludierten Files) compiliert.

Falls kein Che-File geladen wurde, hängt sich der Compiler meistens auf
(Benützerfehler).

Der compilierte Che-Code hat den gleichen Filenamen mit der Endung ".cmp" statt
".che". Der Compiler meldet in einem schmalen grauen Fenster am unteren Rand das
Ergebnis der Compilierung (Erfolg, bzw. Fehler).

Im Falle eines Fehlers, die Art des Fehlers, File, Zeile, Spalte wo der Fehler
aufgetreten ist, danach den Text der fehlerhaften Zeile , darunter einen Pfeil
auf die erste Fehlerposition.

Das Meldungsfenster kann mit Windows/Toggle-Info (Alt+T) zu und abgeschaltet
werden.

Die Position des ersten Fehlers ist meistens richtig. Die Erklärung was falsch
ist, entspricht eher der inneren Logik des Compilers als jener des Benützers.
Meistens erzeugt ein Fehler Folgefehler, weil der Compiler nicht genau weiß, wo
und mit welchem Konstrukt er fortfahren soll. Fehlerdiagnose und
"Error-Recovery" sind die schwierigsten Teile eines Compilers. Dies wird in
Che++ momentan nicht verbessert (Shanny befreien ist wichtiger).

Mit Build/Assemble (Alt+A) kann man Che++ Assembler Output erzeugen (Endung
".asm"). Man kann sich anschauen, welcher Code für die Che++ virtuelle Maschine
(CVM) erzeugt wird. (siehe dazu auch das Schlußkapitel dieser Dokumentation).

Die Nimzo99 Engine (genaugenommen CVM) lädt den compilierten Che-Code im ".cmp"
File und führt jedesmal an der Wurzel die Che-Befehle aus. Man kann verschiedene
Che-Files erzeugen. Im File FreeShanny.ini im Nimzo99 Direktory legt man mit
CheCodeName=<vollständiger FileName> fest, welches Che-File nimzo99 laden soll.
Man kann aber immer nur 1 File gleichzeitig laden. Wenn das File nicht
existiert, wird der Che-Teil an der Wurzel von Nimzo99 ausgelassen.



III) Allgemeine Sprachbeschreibung:

Che++ besteht aus einer allgemeinen Programmiersprache und speziellen
Schach-Datentypen und Befehlen. Die allgemeine Programmiersprache ist eine
Mischung aus small-C (C ohne Pointer) und Pascal (Ein C-Wolf im Pascal Pelz).
Man kann in dieser Sprache alles mögliche und nicht nur Schach-Ratschläge
programmieren (Theoretisch könnte man auch ein vollständiges Schachprogramm
damit schreiben).

Che++ lernt man am besten, indem man sich den Code in den mitgelieferten
Che-Files anschaut und danach versucht selber was zu schreiben.

Che unterscheidet zwischen Groß- und Kleinschreibung. "Hansi" ist nicht
gleich"hansi".

Im Folgenden ist erklärender Text in Times New Roman, konkreter Programmcode in
Courier geschrieben.



IV) Che-Syntax und Semantik

IV.1) Spezielle Schach-Datentypen:

boardside bezeichnet eine Seite eines Schachbrettes.

Wertebereich kside | qside | centre | kkside | qqside

kside = e,f,g,h-Linie.

qside = a,b,c,d-Linie.

centre = d,e-Linie.

kkside = f,g,h-Linie.

qqside = a,b,c-Linie.

Man kann ganz Allgemein einen speziellen Schach-Datentyp zu einer Liste
zusammenfassen (z.B. in einem Aufruf einer Schachprozedur). Dies geschieht
entweder durch die Aufzählung der einzelnen Elemente. Die Elemente müssen durch
mindestens ein Blank voneinander getrennt werden.

z.B. centre kside

Man kann aber auch globale Variablen von Typ Liste definieren.

z.B.

list brdsidelist:=kside qside;

Man kann nun im Weiteren den Listennamen anstatt "kside qside" verwenden. Der
Compiler legt eine Liste mit 2 Einträgen und einem abschließenden Element mit
dem Wert CHE_LISTEND (-1) an.

Es geht aber auch so:

list brdsidelist[10]:=kside qside;

Liste mit 10 Elementen, die ersten 2 Elemente mit kside qside belegt, der Rest
wird mit CHE_LISTEND (-1) für die (momentan) nicht verwendeten Elemente
markiert.

list brdsidelist[10];

Liste mit 10 Elementen, momentan alles mit -1 belegt.

list brdsidelist;

Leere Liste mit der Standardlänge 64 Einträge (plus abschließender Listenende
Marker). Alle Einträge werden vom Compiler auf CHE_LISTEND (-1) gesetzt. Liste
kann für weitere Berechnungen verwendet werden.

Falls nicht anders vermerkt, gilt diese Listenbildung für alle anderen
Schach-Datentypen. In der Regel ist es nicht sinnvoll in einer Liste
verschiedene Typen zu mischen. Der Compiler gibt in diesem Fall eine Warnung
aus.

col bezeichnet eine bestimmte Linie auf dem Schachbrett.

Wertebereich aline | bline | cline | dline | eline | fline | gline | hline |
.line

.line ist eine Linie mit Wildcard ('.' zu Beginn). Bezeichnet alle Linien.

color bezeichnet die Farbe einer Partei

Wertebereich w | b | ?

? ist der Wildcard für die Farbe (auch bei Figuren). '?' steht für Weiß oder
Schwarz.

compoperator bezeichnet einen Vergleichsoperator

Wertebereich = | <> | < | <= | > | >=

= Ist gleich (equal)

<> Ungleich (not equal)

< Kleiner als (less than)

<= Kleiner gleich (less equal)

> Größer als (greater than)

>= Größer gleich (greater equal)

Anmerkung1: Von compoperator können KEINE Listen gebildet werden.

Anmerkung2: Die Che-Basissprache verwendet dieselben Operatoren mit derselben
Bedeutung.

sqr bezeichnet ein bestimmtes Feld auf dem Schachbrett.

Wertebereich a1 | b1 ... | h8

sqr kan an beliebiger Stelle statt eines konkreten Wertes das Wildcard Symbol
"." enthalten.

"a." bedeutet alle Felder auf der A-Linie.

".4'' bedeutet alle Felder auf der 4-ten Reihe.

".." bedeutet alle Felder, ganzes Brett.

move bezeichnet einen Zug auf dem Schachbrett. Besteht aus 2 zusammenschriebenen
sqrs.

Wertebereich e2e4 | b1c3 ....

Mit Wildcards e.e4 alle Züge von der e-Linie nach e4.

..e4 beliebiges Ausgangsfeld nach e4.

e2e. Von e2 auf ein anderes Feld auf der e-Linie.

e2.7 Von e2 irgendwohin auf die 7-te Reihe

e2.. Von e2 irgendwo anders hin.

Einschränkung Man kann syntaktisch sowohl im vom-Feld als auch im nach-Feld
Wildcards verwenden. Dem Che-Interpreter ist das aber zu kompliziert (es gibt
auch wenig sinnvolle Kombinationen). Züge, die sowohl im von- als auch im
nach-Feld Wildcards enthalten werden vom Interpreter ignoriert (erzeugt aber
keinen Fehler).

piece bezeichnet eine Schachfigur

piece besteht aus 2 Buchstaben, von denen der erste die Farbe (w = white,b =
black, ? = white and black) und der zweite die Art der Figur (K = King, Q =
Queen, R = Rook, B = Bishop, N = Knight, P = Pawn, O = Officer (Q oder R oder N
oder B) angibt.

Wildcards ?N ist weißer oder schwarzer Springer

?O weißer,schwarzer Springer, Läufer, Turm oder Dame.

w? alle weißen Figuren

b? alle schwarzen Figuren

?? alle Figuren.

row bezeichnet eine Reihe auf dem Schachbrett.

Wertebereich row1 | row2 ... | row8

sqrcolor bezeichnet eine Felderfarbe.

Wertebereich lsqr | dsqr | ?sqr (light (weiße) Felder, dark (schwarze) Felder,
alle Felder)



IV.2) Spezielle Schach-Funktionen zur Mustererkennung:

IV.2.a) Einleitung:

Soweit nicht anders vermerkt gibt es jede Funktion in 3-facher "Ausfertigung".

<Funktionsname>: Standardfunktion. Die Bedingungen werden "geandet". Die
Bedingung muß für jedes Element der Liste zutreffen.

or<Funktionsname>: Die Bedingungen werden "geord". Die Bedingung muß für
mindestens 1 Element der Liste zutreffen (aber nicht notwendiger Weise für
alle).

Get<Funktionsname> Die Liste im Funktionsaufruf wird mit allen zutreffenden
Datenelementen gefüllt. Die Liste muß eine vorher definierte List-Variable sein.

Die ersten beiden Typen (and, or) geben immer CHE_TRUE (1) zurück, falls die
Bedingung erfüllt ist, ansonsten CHE_FALSE (0).

Die Get-Funktion gibt immer die Anzahl der gefundenen Elemente zurück.

Speziallfall: Falls die Parameter Liste leer ist (kein Element) gibt die "and-"
Funktion immer CHE_TRUE zurück, die "or-" Funktion immer CHE_FALSE.

Anmerkung1: Die meisten Funktionen überprüfen (bis zu einem gewissen Ausmaß) ob
die Parameter im zulässigen Wertebereich sind. Falls dies nicht der Fall ist,
wird immer CHE_FALSE zurück gegeben.

Anmerkung2: Eine "Not"-Ausgabe der Funktionen ist nicht notwendig. Es kann zu
diesem Zweck der Standard-Che-Operator "!" verwendet werden. Auch "or" ist
streng genommen nicht notwendig, weil Che einen "or" Operator besitzt. Es ist
aber angenehmer

orPcOnSqrs(wP,e2 e3) als PcOnSqrs(wP,e2) or PcOnSqrs(wP,e3) zu schreiben.
Außerdem erzeugt die or-Funktion effizenteren Che-Code.



IV.2.b) Position des Königs:

Syntax: [or|Get]KingPos(color, boardside-list)

Beschreibung: Die Bedingung ist erfüllt, wenn sich der König der Farbe color auf
allen Elementen der boardside-Liste aufhält (and-Version). Auf mindestens einem
Element der boardside-Liste (or-Version) bzw. Get-Funktion gibt die Board-Side
Elemente zurück.

Anmerkung: Bei den folgenden Funktionen erfolgt die Beschreibung nur für die
"and-" Version. "or-" und "Get-" verhalten sich immer sinngemäß gleich.

Beispiele (die beste Quelle dafür sind die beiliegenden Che-Files):

KingPos(?,kkside); TRUE, falls beide Könige auf f-h-Linie stehen.

orKingPos(w,centre kkside); TRUE, falls weißer König im Zentrum oder auf
Königsflügel steht.

list ksidelist[3];

GetKingPos(b,ksidelist); Liste ksidelist enthält nach dem Aufruf die Seiten auf
denen schwarzer König steht (Seiten überschneiden sich. Es können daher mehrere
sein). Die Funktion gibt die Anzahl der in ksidelist abgelegten Seiten zurück.

GetKingPos(b,kkside); Ist illegal (und sinnlos). Der Get-Funktion muß immer eine
vorher definierte List-Variable übergeben werden. Die Liste muß lange genug
sein, damit alle gefundenen Elemente vom Interpreter abgespeichert werden
können. Der Interpreter überprüft das NICHT. Eine zu kurze Liste ist ein
(grauslicher) Bug. In der Regel werden einfach anderere Listen "still und leise"
überschrieben und das Resultat der Berechnung ist Mist.



Achtung:

orKingPos(?,kkside); Bedeutet NICHT, daß entweder der weiße oder der schwarze
König am Königsflügel stehen. Die orFunktion ord nur die Elemente der
abschließenden Liste und nicht die durch die anderen Parameter bestimmten
Bedingungen.

Weißer oder schwarzer König am Königsflügel muß man korrekt so schreiben:

KingPos(w,kkside) or KingPos(b,kkside)

IV.2.c) Figuren auf Feldern:

Syntax: [or|Get]PcOnSqrs(piece, sqr-list)

Beschreibung: Die Bedingung ist erfüllt, wenn auf allen in sqr-list angeführten
Feldern die Figur piece steht.

Beispiele:

PcOnSqrs(wP,e4 d5) weißer Bauer auf e4 und d5.

PcOnSqrs(?P,e.) mindestens ein Bauer (egal welcher) auf e-Linie.

PcOnSqrs(?O,..) ... mindestens ein Offizier auf dem ganzen Brett. Wir sind im
Bauernendspiel, falls Bedingung nicht erfüllt ist.



!PcOnSqrs(??,.7) Keine Figur auf 7-ter Reihe.



IV.2.c) Zähle Figuren:

Syntax: CmpNrPcs(piece,compoperator,zahl,sqr-list)

Beschreibung: Mit dieser Bedingung wird beschrieben, wie viele Figuren auf einer
bestimmten Anzahl von Feldern stehen. Großer Bruder von PcOnSqrs().

Beispiele:

CmpNrPcs(wP,>=,2,c.) Mindestens zwei weiße Bauern auf der c-Linie.

CmpNrPcs(?Q,>=,1,..) and CmpNrPcs(?O,>,4,..) Das ist die Nimzo Definition für
Mittelspiel. Mindestens eine Dame am Brett und mehr als 4 Offiziere.

CmpNrPcs(??,<=,4,..) Vier oder weniger Figuren am Brett (Schaue in
Endspieldatenbank nach).

PcOnSqrs(piece,sqr-list) ist identisch mit CmpNrPcs(piece,>,0,sqr-list).

Anmerkung: Es gibt für diese Funktion keine or- oder Get-Version. PcOnSqrs() ist
etwas effizienter als die gleichwertige CmpNrPcs() Formulierung.



IV.2.d) Doppelbauer:

Syntax: [or|Get] DblPwns(color, sqr-list)

Beschreibung: Mit dieser Bedingung wird der Standort von Doppelbauer abgefragt.

Beispiele:

DblPwns(w,c3) Der Bauer auf c3 ist ein Doppelbauer (benötigt nicht die Position
des zweiten Doppelbauers).

DblPwns(b,c.) Irgendwo auf der c-Line befindet sich zwei weiße Doppelbauern.

DblPwns(?,..) Irgendwo am Brett ist ein weißer oder schwarzer Doppelbauer.

orDblPwns(w,c. d.) Weißer Doppelbauer auf c- oder d-Line.

GetDblPwns(w,sqrlist) Sqrlist enthält nach dem Aufruf die Positionen aller
weißen Doppelbauern.

Anmerkung: Doppelbauer bedeutet mindestens 2 Bauern derselben Farbe auf einer
Linie. Kann aber auch ein Tripel oder Quadro-Bauer sein.



IV.2.e) Bauern auf Linien:

Syntax: [or|Get]POnLine(color,compoperator,number,col-list)

Beschreibung: Mit dieser Bedingung wird die Anzahl der Bauern auf den Linien
überprüft. Derselbe Effekt kann auch mit CmpNrPcs() und teilweise mit DblPwns()
erreicht werden, aber diese Funktion ist effizienter.

Bespiele:

POnLine(?,=,0,dline eline) Offene d- und e-Line (Kein Bauer drauf).

POnLine(w,>=,2,eline) ist äquivalent zu DblPwns(w,e.)

GetPOnLine(w,>=,2,collist) collist enthält alle Linien mit weißen Doppelbauern.
Return-Value ist Anzahl dieser Linien.



IV.2.f) Freibauer:

Syntax: [or|Get]Passed(color, sqrlist)

Beschreibung: Mit dieser Bedingung wird überprüft, ob auf den Feldern in sqrlist
Freibauern stehen.

Beispiele:

Passed(w,.7) Weiß hat Freibauer auf 7-ter Reihe.

GetPassed(w,sqrlist) Liste aller weißen Freibauern (Siehe Funktion
RooksBehindPassed() in EndGame.che)

Passed(?,..) Gibt es am Brett einen weißen oder schwarzen Freibauer?



IV.2.g) Felderkontrolle durch Figur:

Syntax: [or|Get]Control(piece, sqr-list)

Beschreibung: Überprüft, ob Figurentyp die Felder in sqr-list kontrolliert (In
einem Zug schlagend! darauf ziehen kann). Bauer auf e2 kontrolliert also d3,f3,
nicht jedoch e3 oder e4. Die Funktion ignoriert welche Seite am Zug ist. Ferner
werden Fesselungen NICHT berücksichtigt. D.h. der Zug kann wegen Schach illegal
sein. Die Figur kontrolliert das Feld auf dem sie gerade steht NICHT!

Beispiele:

Control(w?,h7) Weiß kann im nächsten (pseudolegalen-)Zug nach h7 fahren.

Control(wR,a1 h1) Die Felder a1 und h1 werden von den weißen Türmen
kontrolliert. Es muß aber nicht derselbe Turm sein.

GetControl(bR,sqrlist) Alle Felder auf die die schwarzen Türme fahren können.



IV.2.h) Abtauschkontrolle über Feld:

Syntax: [or|Get]ExchngCtrl(color,sqrlist)

Beschreibung: Mit dieser Bedingung wird abgefragt, ob eine Farbe die
"Abtauschkontrolle" über ein Feld hat. Abtauschkontrolle bedeutet, daß die Seite
hinfahren und der Gegner die Figur nicht mit Materialgewinn schlagen kann. Wenn
von Seite schon eine Figur auf dem Feld steht, dann kann sie der Gegner nicht
mit Materialgewinn schlagen. Falls eine Figur des Gegners am Feld steht, dann
kann sie die Seite schlagen und verliert zumindest kein Material.

Anmerkung: Die Abtauschkontrolle wird statisch bestimmt. Es werden keinerlei
Fesselungen berücksichtigt. Der Abtausch-Algorithmus ist neu in Nimzo (Programm
verwendet normaler Weise zu diesem Zweck Ruhesuche). Das Ergebnis dieser
Funktion ist daher mit Vorsicht zu geniessen. Es werden auch keinerlei
positionellen Kriterien verwendet. Ein Abtausch Läufer gegen Springer wird daher
als Verlust bedrachtet, weil der Materialwert des Läufers etwas höher ist.

Beispiel:

ExchngCtrl(w,h7) Weiß kann nach h7 fahren (bzw. steht schon dort), ohne daß dies
von Schwarz verhindert werden kann (bzw. Figur kann nicht vertrieben werden).

ExchngCtrl(?,d4 e4 d5 e5) Weiß und Schwarz, d.h. keine Seite, haben die
Abtauschkontrolle über das Zentrum.



IV.3) Spezielle Funktionen zur Manipulation von Listen und Feldern.

IV.3.a) Länge einer Liste:

Syntax: ListLen(list)

Beschreibung: Gibt die Anzahl der verwendeten Elemente einer Liste zurück.

Beispiele:

ListLen(e2 e3) Gibt Wert 2 zurück.

list tmplist;

ListLen(tmplist) Gibt Wert 0 zurück, da der Compiler mit dem Befehl list
tmplist; eine List der Länge 64 anlegt, alle Elemente aber mit dem Wert
CHE_LISTEND (-1) belegt. Die Anzahl der verwendeten Elemente ist daher 0
(ListLen verwendet das erste -1 als Marker für das Ende der Liste).

Siehe auch stdlib.che für möglliche Verwendungszwecke dieser Funktion.



stdlib.che enthält weiters eine Reihe von Funktionen zur Listenmanipulation.



IV.3.b) Felder-Manipulation:

Man kann und DARF nicht davon ausgehen, daß die Felder auf dem CHE-Brett der
Reihe nach angeordnet sind. D.h. a2 kommt - numerisch - NICHT direkt nach h2.
Tatsächlich soll man immer die symbolischen Konstanten verwenden und keine
direkten Zahlen. Um die Felder durchlaufen zu können bzw. um bestimmte Felder
auswählen zu können, müßen immer die folgenden Funktionen verwendet werden.



IV.3.b.1) Nächstes Feld:

Syntax: NxtSqr(sqr)

Beschreibung: Liefert die interne Nummer des nächsten Feldes von sqr zurück.
Falls es kein nächstes Feld gibt, wird CHE_INVSQR (-1) zurück gegeben.

Beispiele:

sqr:=NxtSqr(h1) sqr enthält nun den Wert für das Feld a2.

sqr:=NxtSqr(a.) sqr enthält den Wert für b.

sqr:=NxtSqr(.7) sqr enthält den Wert für .8

sqr:=NxtSqr(..) sqr enthält Wert CHE_INVSQR (-1). (Gibt kein Nachfolgefeld)

Ebenso liefern NxtSqr(h8), NxtSqr(h.) und NxtSqr(.8) CHE_INVSQR zurück.

Der folgende Programmteil durchläuft alle Felder von a1 nach h8 und schreibt den
internen Wert des Feldes in das File aaache.h.

sqr:=a1;

while (sqr:=NxtSqr(sqr))<> CHE_INVSQR do

write sqr; # write schreibt den Wert auf aaache.h

end;

IV.3.b.2) Vorhergehendes Feld:

Syntax: PrvSqr(sqr)

Beschreibung: Liefert die interne Nummer des vorhergehenden Feldes zurück. Fall
es kein vorhergehendes Feld gibt, wird CHE_INVSQR zurück gegeben. Verhalten bis
auf Richtung wie NxtSqr();

Beispiel:

Felder von h8 "rückwärts" nach a1 ausgeben.

sqr:=h8;

while ((sqr:=PrvSqr(sqr)) <> CHE_INVSQR do

write sqr;

end;

IV.3.b.3) Alle Felder auf Reihe nach links.

Syntax: GetLSqrs(sqr,sqrlist)

Beschreibung: sqrlist enthält nach Aufruf alle Felder die links (von Weiß aus
gesehen) von sqr stehen (inkl. das Feld selbst). Es wird die Anzahl der Felder
in sqrlist zurück gegeben.

Beispiel:

GetLSqrs(c4,sqrlist) sqrlist enthält nach Aufruf die Felder c4 b4 a4. Die
Funktion gibt den Wert 3 zurück.

Anmerkung: Das Feld darf keinen Wildcard enthalten. In diesem Fall wird eine
leere Liste zurück gegeben. Diese gilt für alle Get..Sqrs() funktionen.



IV.3.b.4) Alle Felder auf Reihe nach rechts.

Syntax: GetRSqrs(sqr,sqrlist)

Beschreibung: sqrlist enthält nach Aufruf alle Felder die rechts (von Weiß aus
gesehen) von sqr stehen (inkl. das Feld selbst). Es wird die Anzahl der Felder
in sqrlist zurück gegeben.

Beispiel:

GetRSqrs(h1,sqrlist) sqrlist enthält nach Aufruf das Feld h1 und gibt den Wert 1
zurück.



IV.3.b.4) Alle Felder auf Linie nach unten.

Syntax: GetDwnSqrs(sqr, sqrlist)

Beschreibung: sqrlist enthält nach Aufruf alle Felder die auf der Linie
unterhalb (von Weiß aus gesehen) von sqr stehen (inkl. das Feld selbst). Es wird
die Anzahl der Felder in sqrlist zurück gegeben.

Beispiel:

GetDwnSqrs(e4,sqrlist) sqrlist enthält nach Aufruf die Felder e4 e3 e2 e1 und
gibt den Wert 4 zurück. Angenommen sqr ist das Feld eines weißen Freibauern
(siehe RookBehindPassed() in EndGame.che). Dann läßt sich die Regel ."Türme
hinter Freibauern" wie folgt programmieren.

i:=GetDwnSqrs(sqr,tmplist);

SetSqrs(?R,i*factor,tmplist); Diese Funktion wird bei den Advices noch erklärt.

Variable i ist dabei die Anzahl der Felder in tmplist. D.h. die Reihe des
Freibauerns. Durch den Ausdruck i*factor in SetSqrs() wird ein Turm hinter einem
weit vorgerückten Freibauern höher bewertet als hinter einem noch nicht
vorgerückten.

In RookBehindPassed() sind die beiden Zeilen kompakter geschrieben. Und zwar als

SetSqrs(?R,GetDwnSqrs(sqr,tmplist)*factor,tmplist);

Normale Menschen schreiben es in der 2-Zeilen Form, real-programmers in der
Kurzform.



IV.3.b.5) Alle Felder auf Linie nach oben.

Syntax: GetUpSqrs(sqr, sqrlist)

Beschreibung: sqrlist enthält nach Aufruf alle Felder die auf der Linie oberhalb
(von Weiß aus gesehen) von sqr stehen (inkl. das Feld selbst). Es wird die
Anzahl der Felder in sqrlist zurück gegeben.

Beispiel:

GetUpSqrs(e4,sqrlist) sqrlist enthält nach Aufruf die Felder e4 e5 e6 e7 e8 und
gibt den Wert 5 zurück

"Turm hinter Freibauern" läßt sich analog für einen schwarzen Freibauer wie
folgt programmieren.

i:=GetUpSqrs(sqr,tmplist);

SetSqrs(?R,i*factor,tmplist); Diese Funktion wird bei den Advices noch erklärt.

Oder wieder kompakter als

SetSqrs(?R,GetUpSqrs(sqr,tmplist)*factor,tmplist);

IV.4) Bibliotheks-Funktionen:

Che++ ist mächtig und flexibel, aber auf manche Formulierungen muß man erst
draufkommen. Außerdem benötigt man zum Aufruf mancher Funktionen relativ viele
Parameter. Um Schreib- und Denkarbeit zu erleichtern, bzw. um etwaige künftige
Änderungen auf einen Platz zu beschränken, wird eine Standard-Bibliothek von
Funktionsaufrüfen und Standardwerten in stdlib.che bereit gestellt.

Der erste Befehl in jedem Che-Programm sollte daher

include stdlib.che

sein. Man sollte immer die Funktionen bzw. Werte dieser Bibliothek benutzen.
Falls man selber öfters spezielle Funktionen verwendet die in stdlib.che noch
nicht vorkommen, sollte man diese in eine eigene Bibliothek (z.B. HansiLib.che)
schreiben und diese als nächstes inkludieren. Ferner sollte man diese Bibliothek
der übrigen Che-Menschheit zur Verfügung stellen.



Die Bonus-Werte für gute, schlechte Züge, Felder in stdlib.che sind erste
Schätzungen. Mit diesen Werten könnte bzw. sollte man experimentieren. Ansonsten
soll man vom Herumpfuschen in stdlib.che eher Abstand nehmen.

Die Verwaltung von stdlib.che ist Aufgabe von Ch.Donninger. Fehler in stdlib.che
bzw. in allen anderen Che++ Teilen sollten an Chrilly Donninger, Email:
chrilly@wvnet.at, weitergegeben werden. Die aktuelle Che-Version eine
Beta-Version. Der Che-Compiler plus Interpreter sind ca. 3.000 Zeilen C-Code.
Die Version enthält daher mit absoluter Sicherheit einige Fehler.

Falls sich die Funktionen in HansiLib.che als allgemein nützlich herausstellen,
werden sie beim nächsten Update in stdlib.che eingearbeitet.

Der Sinn und Zweck der Bibliotheks-Funktionen ist in stdlib.che relativ gut
dokumentiert und wird daher hier nicht wiederholt.



IV.5) Advice-Funktionen:

IV.5.a) Gute und schlechte Züge:

Syntax: SetMvs(color,bonus,move-list)

Beschreibung: Die in color festgelegte Seite bekommt für jeden Zug in move-list
den in "bonus" festgelegten zusätzlichen Wert. Ein Bauer hat in Nimzo einen
reinen Materialwert von 105. Ein Bonus von 20 ist ca. 1/5 - 1/6 Bauernheiten.
Der Bonus wird auch für Züge in der Variante vergeben und nicht nur an der
Wurzel. Allerdings wird der Bonus im Laufe der Variante verkleinert (alle 2
Plies mit 2 dividiert).

Anmerkung: Nimzo kann intern bei den Advice-Zügen nur nach Farbe und nicht nach
Figurenart unterscheiden. Aus diesem Grund kann man auch nur die Farbe in SetMvs
angeben.

Beispiel:

SetMvs(w,20,e2e4 c2c4) weiße Züge e2e4 und c2c4 bekommen Bonus von 20
(Good-Move).

SetMvs(b,-20,c5c4) schwarzer Zug c5c4 bekommt Malus von -20 (Bad-Move)

SetMvs(w,-20,d1..) Weiß bekommt für jeden Zug von d1 weg einen Malus von -20
(auf d1 steht weiße Dame, wir wollen Damenausflug zu Beginn des Spieles
vermeiden).



SetMvs(b,20,..e5) Schwarz soll von irgendwo nach e5 ziehen.



IV.5.b) Gute und schlechte Bauern-Züge:

Syntax: SetPMvs(color,bonus,sqr-list)

Beschreibung: Bauer von Farbe bekommt für einen Zug auf ein Feld in Sqr-List den
Bonus. Falls ein Feld der 4-ten Reihe eingegeben wird, wird jener Zug erzeugt,
auf dem der Bauer gerade steht. Angenommen der Bauer steht auf e2, dann bekommt
e2e4 den Bonus, steht der Bauer auf e3, dann e3e4. Es werden nur
nicht-schlagende Bauernzüge erzeugt. Schlagende Bauernzüge müssen mit SetMvs()
angegeben werden.

Im Gegensatz zu SetMvs() wird ein Zug nur eingetragen, wenn auf dem Ausgangsfeld
ein Bauer steht.

Anmerkung: Nachdem Nimzo intern für den Zug-Bonus Figuren nicht unterscheiden
kann, bekommen auch andere Figuren den Bonus, falls sie den entsprechenden Zug
ausführen. Durch die Zusatzbedingung - Bauer muß auf dem Ausgangsfeld in der
Wurzel stehen - ist ein derartiger Effekt aber schwer möglich.

Beispiele:

SetPMvs(w,20,e4) Fahre mit weißen Bauer entweder von e2 oder von e3 nach e4.

SetPMvs(b,-20,c4 e5) Die schwarzen Bauernzüge c5c4 und e7e5 bzw. e6e5 sind
schlecht.



IV.5.c) Gute und schlechte Felder:

Syntax: SetSqrs(piece,bonus,sqr-list)

Beschreibung: Die Figur "piece" bekommt für die Felder in sqr-list einen
(zusätzlichen) Bonus. Für den Wert von bonus gilt das in IV.5.b) gesagte. Die
Figur (und damit die Seite der die Figur angehört) bekommt den Bonus, falls die
Figur am Ende einer vom Programm berechneten Variante auf einem der Felder
steht.

Beispiele:

SetSqrs(wB,20,e2) weißer Läufer steht auf e2 gut.

SetSqrs(bR,30,.2) Bonus für schwarzen Turm auf 2-ter Reihe.

SetSqrs(?B,20,..) Bonus für weiße und schwarze Läufer auf gesamten Brett. D.h.
der Wert der Läufer wird erhöht. (gibt dafür mit ChngPcsVal() eigene
Spezialfunktion).



IV.5.d) Tausche/Behalte Läufer:

Syntax: KeepBs(color,bonus,sqrcolor-list)

Beschreibung: Ändere den Figurenwert des Läufers der Spielfarbe ´color´ auf den
in sqrcolor-list angegebenen Feldern um den Wert "bonus".

Beispiele:

KeepBs(w,20,lsqr) Erhöhe den Wert des weißen Läufers auf den weißen Feldern um
den Wert 20. Weiß wird eher versuchen, diesen Läufer zu behalten.

KeepBs(b,-20,lsqr) Verringere den Wert des schwarzen Läufers auf den weißen
Feldern um den Wert 20. Schwarz wird eher versuchen, den Läufer abzutauschen.

KeepBs(?,20,.sqr) Alle Läufer sind auf allen Feldern um 20 mehr wert (Selber
Effekt wie Beispiel SetSqrs(?B,20,..) in IV.5.c)



IV.5.e) Tausche/Behalte Figuren:

Syntax: KeepPcs(bonus, piece-list)

Beschreibung: Der Wert der Figuren in Piece-List wird um bonus verändert.

Beispiele:

KeepPcs(-10,wO) Alle weißen Offiziere sind um 10 weniger Wert. Weiß wird eher
versuchen die Figuren abzutauschen.

KeepPcs(20,?B) Eine weitere Version von alle Läufer sind auf allen Feldern um 20
mehr wert (dies ist die effektivste Variante).



IV.5.f) Tausche/Behalte-Figuren:

Syntax: ChngPcsVal(bonus,piece-list)

Beschreibung: Die Funktion hat exakt dieselbe Auswirkung wie KeepPcs aus IV.5.e
(bei der ursprünglichen Definition von Che++ durch A.Mader hatte KeepPcs noch
keinen bonus Parameter). Vielleicht fällt irgendwem ein sinnvolle
unterschiedliche Verwendung ein. Bis dahin sollte KeepPcs() verwendet werden.



IV.5.g) Gute/Schlechte Diagonalen:

Syntax: SetDiags(piece,bonus,move-list)

Beschreibung: Es wird auf jeder von move-list beschriebenen Diagonalen der
Felderwert der Figur um den Bonus verändert.

Anmerkung: Genau genommen können es auch Reihen und Linien sein. Von- und
nach-Feld des Zuges muß nur in Turm oder Läuferrichtung sein.



Achtung: Wildcards in der move-list werden von dieser Funktion NICHT
unterstützt. Ein Zug mit Wildcards wird ignoriert.

Beispiele:

SetDiags(wB,20,a2d5) die Felderwerte des weißen Läufers werden auf den Feldern
a2,b3,c4,d5 um 20 erhöht (soll auf einem dieser Felder zu stehen kommen).

SetDiags(bR,30,a2h2) eine weitere Version von Bonus für schwarzen Turm auf 2-ter
Reihe.

SetDiags(bB,-20,g7b2) Keine Auswirkung, weil g7 und b2 nicht auf einer Diagonale
liegt.

SetDiags(wR,20,e1e3) Bonus von 20 für weißen Turm auf e1,e2 oder e3.

IV.5.h) Minoritätsangriff:

Syntax: MinAttack(color)

Beschreibung: Die durch color definierte Farbe soll einen Minoritätsangriff am
Damenflügel starten.

Beispiel:

MinAttack(w) Weiß soll am Damenflügel einen Minoritätsangriff machen.



IV.5.i) Forciere Bauern

Syntax: ForcePwns(color,col-list)

Beschreibung: Die Bauern der durch color definierten Farbe sollen auf den durch
col-list definierten Reihen foricert (nach vorne gezogen) werden.

Beispiel:

ForcePwns(w,fline gline hline) Weiß soll am Königsflügel einen Bauernsturm
machen.



IV.5.j) Attackiere

Syntax: Attack(color,boardside-list)

Beschreibung: Die durch color definierte Farbe soll einen Angriff auf den durch
die boardside-list definierten Linien machen. Es werden die Bauern forciert,
sowie Turm und Dame auf die Linie plaziert.

Beispiel:

Attack(w,kkside) Mache weißen Bauernsturm auf f,g,h-Linie und plaziere weiße
Türme und Dame auf diese Linien.



IV.5.k) Kontrolliere Felder:

Syntax: CtrlSqrs(piece, bonus,sqrlist)

Beschreibung: Die durch piece definierte Figur soll die durch sqr-list
definierten Felder kontrollieren, d.h. in einem Zug auf dieses Feld ziehen
können. Bei Bauern gelten nur Schlagzüge auf dieses Feld als Kontrolle.

Anmerkung1: In Nimzo9x wird CtrlSqrs über die Mobilität realisiert. Der Bonus
erhöht die Mobilitätsgewichtung der Felder für die angegebenen Figuren. Der
Endeffekt ist daher auch von der Größe des Mobilitätsfaktors abhängig. Ferner
bekommen auch "XRay"-Züge diesen Bonus. Allerdings wird diese Form der Kontrolle
mit den geringeren XRay-Mobilitätsfaktor gewichtet. Allgemein gilt: Endeffekt
ist (bonus*Mobilitätsfaktor)/256.

bzw. (bonus*XRayFaktor)/256.

Anmerkung2: Eine Dame ist intern für die Berechnung der Mobilität Turm plus
Läufer (aber Mobilitätsfaktor der Dame ist nur 1/4 des sonstigen
Mobilitätsfaktors). D.h. Eine Veränderung für Turm und Läufer wirkt sich auch
auf die Dame aus (und umgekehrt).

Anmerkung3: Nimzo9x kennt bisher keine Bauern-Mobilität. Im Moment hat daher die
Eingabe einer Bauernkontrolle keine Auswirkungen. Dies soll jedoch im nächsten
internen Update (April 99) geändert werden.

Beispiele:

CtrlSqrs(?O,30,d4) Beide Seiten sollen versuchen mit den Figuren d4 zu
kontrollieren (Druck machen).

CtrlSqrs(wQ,20,d.) Dame soll d-Linie kontrollien. ACHTUNG: Dies wird aber intern
umgesetzt als

CtrlSqrs(wR,20,d.) und CtrlSqrs(wB,20,d.). D.h. auch Turm und Läufer werden
versuchen Felder auf der d-Linie zu kontrollieren.



IV.6) Beeinflußung des Spielstiles:

Mit Hilfe dieser Funktionen kann der Spielstil analog dem
Engine-Parameter-Dialog der ChessBase-Oberfläche eingestellt werden. Es besteht
allerdings ein wesentlicher Unterschied: Die Werte des Parameter-Dialogs sind
permanent und unabhängig von der aktuellen Stellungen. Die mit Che++
eingestellten Werte sind nur für die aktuelle Stellung und die anschließende
Suche gültig. Wird in der folgenden Stellung keine Che-Spielstil-Funktion
aufgerufen, verwendet das Programm wieder die ursprünglichen im Parameter-Dialog
eingestellten Werte. Dadurch kann der Spielstil des Programmes selektiv, in
Abhängigkeit von bestimmten Bedingungen (z.B. Übergang ins Endspiel), verändert
werden.



IV.6.a) Abfrage des eingestellten Spielstiles:

Syntax: GetStyle(StyleId);

Beschreibung: Die Funktion GetStyle(StyleId) liefert für den durch StyleId
bestimmten Engine-Parameter die aktuelle Einstellung zurück.

Achtung: Es wird der im Engine-Parameter-Dialog eingestellte Wert zurück
gegeben. Vorhergehende Aufrufe der Prozedur SetStyle() haben auf diesen Wert
KEINEN Einfluß.



StyleId kann die folgenden - in stdlib.che definierten - Werte annehmen:



ST_MIDGAMEMOB Mittelspiel-Mobilität.



ST_ENDGAMEMOB Endspiel-Mobilität.



ST_KINGSAFTY Gewichtung der Königssicherheit.



ST_PAWNEVAL Gewichtung der Bauern-Struktur-Bewertung.



ST_PAWNMAT Materialwert für Bauer.



ST_KNIGHTMAT Materialwert für Springer.



ST_BISHOPMAT Materialwert für Läufer.



ST_ROOKMAT Materialwert für Turm.



ST_QUEENMAT Materialwert für Dame.

Für die folgenden 5 Parameter bedeutet Wert 1 (CHE_TRUE), das Feature ist
eingeschaltet, Wert 0 (CHE_FALSE), das Feature ist ausgeschaltet.



ST_PIECETHREAT Erkenne Figuren-Bedrohung und erweitere Suche entsprechend.



ST_FEWMVSTHREAT Erweitere Suche falls nur 1 legaler Zug möglich.



ST_FUTIL1CUTOFF Setze Futility-Cutoff (selektive Suchtechnik) 1 Ply vor dem
Horizont ein.



ST_FUTIL2CUTOFF Setze Futility-Cutoff 2 Plies vor dem Horizont ein.



ST_LEARNFLG Hash-Tabellen-Lernen. Verwende Ergebnisse von bereits gespielten
Partien.



ST_OPPONENT Gegner von Nimzo99. Optimiert den Spielstil für einen bestimmten
Gegner. Die möglichen Werte sind (siehe stdlib.che).



OPP_FRITZ (Gegner Fritz, Wert 0), OPP_JUNIOR (Gegner Junior, Wert 1), OPP_REBEL
(Gegner Rebel, Wert 2), OPP_OTHER (alle anderen, Wert 3).



IV.6.b) Einstellung Spielstiles:

Syntax: SetStyle(StyleId,Value);

Beschreibung: Die Prozedur SetStyle(StyleId,Value) verändert für die aktuelle
Stellung den durch StyleId bestimmten Engine-Parameter. Die Wert des
Engine-Parameter-Dialogs werden durch diese Funktion nicht geändert.



StyleId kann alle in IV.6.a) beschriebenen Werte annehmen. Für Value gilt
ebenfalls das in IV.6.a) gesagte. Zusätzlich wird der Wert:



ST_DEFAULTVALUES Verwende die Standard-Parameter der Engine. Value wird nicht
verwendet.

unterstützt.

Der Parameter ST_LEARNFLG kann aus programmtechnischen Gründen mit SetStyle
NICHT verändert werden.



IV.6.c) Abfrage der Bewertung:

Syntax: GetScore(PrevMoves);

Beschreibung: Die Funktion GetScore(PrevMoves) liefert die Bewertung der
PrevMoves zurück liegenden Stellung (1 Bauer entspricht dabei den Wert 100).
Falls dafür keine Bewertung vorliegt (weil z.B. ein neues Spiel begonnen wurde)
wird der Wert 0 zurück geliefert. Die Bewertung ist aus der Sicht der Seite die
am Zug ist. D.h. positiver Wert: Programm ist im Vorteil (die ChessBase-Engine
bewertet im Gegensatz dazu immer aus der Sicht von Weiß).

Beispiele:



GetScore(1); Liefert die Bewertung des letzten vom Programm gemachten Zuges
zurück (Achtung: Nicht jene des Gegners, PrevMoves zählt Züge und nicht Plies).

GetScore(2); Liefert die Bewertung des vorletzten vom Programm gemachten Zuges
zurück.

Anmerkung: Man kann die Bewertung des Gegners nicht abfragen, weil im
Turniermodus das Programm immer nur für seine eigene Farbe rechnet.





V) Allgemeine Che++ Sprachkonstrukte:

Dieser Teil behandelt den schachunabhängigen Teil der Programmiersprache Che.
Che wurde von einem C-Gläubigen (Ch.Donninger) und einen C-Haßer (A.Mader)
geschaffen (es gibt unter richtigen Programmierern nur diese 2 Reaktionen auf
C). Das Resultat: Che ist der Struktur nach klein-C, die Schlüsselwörter der
Sprache stammen aber überwiegend von Pascal.

In Che muß jeder Bezeichner (Variable, Liste, Prozedur) definiert werden, bevor
man sie verwendet. Dies ist für BASIC Programmierer ungewohnt und lästig. Dieses
Konzept hat sich aber in allen modernen Programmiersprachen durchgesetzt. Der
Compiler entdeckt dadurch viel besser Programmierfehler.

Beispiel:

var hansi,susi;

begin

hansi:=1;

susi:=hasi+1;

Der Che Compiler würde in Zeile 4 melden: Fehler, "hasi" unbekannt. Ohne die
vorige Deklaration der Variablen in der ersten Zeile müßte er annehmen, daß
"hasi" eine neue Variable ist (und kein falsch geschriebenes "hansi") und ihr
den Wert 0 zuweisen. Der Wert von susi wäre entgegen der Absicht des
Programmierers 1 und nicht 2. In längeren Programmen sind derartige Fehler kaum
zu entdecken (der Autor spricht aus Erfahrung). Oder anders ausgedrückt. Man
macht beim Programmieren sowieso genug Fehler. Man sollte dem Compiler aber die
Chance geben, wenigstens die blödesten davon zu finden.

Wie bereits erwähnt ist Che "case-sensitiv". "Hansi", "hansi", "hAnsi" sind also
3 verschiedene Variablen. Dies gilt auch für alle oben beschriebenen
Schachausdrücke. Das Feld "A1" statt "a1" ist ein Syntaxfehler.
Case-Sensitivität hat sich heute ebenfalls weitgehend durchgesetzt. Tatsächlich
stammt die Nicht-Unterscheidung aus der Computer-Steinzeit, wo es noch nicht
möglich war, den vollen Zeichensatz darzustellen.

Am besten lernt man die allgemeine Che-Sprache, indem man sich den Code in den
"che-Files" durchliest. Für C-oder Pascal Programmierer dürfte dies ausreichend
sein um Che zu verstehen.



V.1) Kommentare:

Ein Kommentar beginnt in Che mit einem "#" und geht bis zum Ende der Zeile. Eine
Zeile darf in Che-maximal 256 Zeichen lang sein.



V.2) Inklude-Files:

Es ist vorteilhaft, den Code eines Che-Programmes in kleinere, logische
Einheiten aufzuteilen bzw. von anderen geschriebene Teile (wie die stdlib)
wieder zu verwenden. Am Ende muß aber der gesamte Code gemeinsam übersetzt
werden. Dies geschieht indem man die Teile in einem Hauptmodul (im Beispielcode
shanny.che) inkludiert.

Syntax: include <FileName>

Die Compilation wird an dieser Stelle im aktuellen File unterbrochen, es wird am
Beginn des inkludierten Files fortgesetzt. Wenn das inkludierte File
abgearbeitet ist, wird nach der inklude-Zeile weiter compiliert. Man kann in
einem inkludierten File weitere andere Files inkludieren. Man muß dabei nur
aufpassen, daß man keine Endlosschleife produziert, weil sich die Files jeweils
gegenseitig inkludieren.

Beispiel:

include stdlib.che # Sollte immer der erste Befehl in einem Che Programm sein.

Achtung: Nach dem include sollte auf derselbe Zeile kein weiterer Befehl stehen,
weil die Compilation in der nächsten Zeile fortsetzt (Kommentare sind natürlich
möglich).

Anmerkung: Che unterstützt momentan nicht die getrennte Compilierung der
einzelnen Teile. Man muß immer alles gemeinsam übersetzen. Dies hat u.A. den
Nachteil, daß man bei der Bearbeitung eines SubModuls immer zuerst in den
Hauptmodul (z.B. shanny.che) zurück gehen muß, bevor man den SubModul übersetzt.
Falls der Compiler im SubModul einen Fehler findet, muß man wieder in den
Submodul zurück gehen, den Fehler beheben, wieder zurück in den Hauptmodul...

Zukünftige Che-Versionen werden einen Linker enthalten und diesen Mangel beheben
(Shanny befreien ist aber momentan wichtiger).



V.3) Datentypen:

Che kennt 2 Datentypen. Normale Variablen (32-Bit Zahlen) und Listen. Eine
Variable kann global oder lokal für eine Routine definiert werden.
Listen-Variablen können nur global definiert werden. Variablen und Listen können
auch als Parameter für Funktionen und Prozeduren verwendet werden. Eine Liste
ist eine Array von 32-Bit zahlen, wobei das Listenende mit CHE_LISTEND (-1)
markiert wird.

Eine Deklaration ist lokal, wenn sie innerhalb eine Prozedur, bzw. Funktion
steht. Sie ist global, wenn sie außerhalb steht. Ein globale Variable ist ab dem
Zeitpunkt der Deklaration bis an den Ende des Codes definiert. Einge lokale
Variable existiert nur bis zum Ende der Prozedur.

Man kann die Werte einer Liste bei der Deklaration festlegen. Globale Variablen
haben den Anfangswert 0, der Wert lokaler Variablen ist nach der Deklaration
unbestimmt (Zufall).

Beispiele:

var i,j,k;

Definiert die Variable i, j und k.

list CentreSqrs:=d4 e4 d5 e5;

# Definiert die 4 Elemente lange Liste CentreSqrs mit den Zentrums-Feldern.



list pwnlist[16];

Definiert eine 16-Elemente lange Liste. Die Liste enthält bei der Deklaration
keine Werte. Sie kann aber durch Operationen mit Werten belegt werden (z.B.
durch den Aufruf einer Get-Funktion).

list tmplist; Kurzform von list tmplist[64];

list WrongList[2]:=wK wN wP;

Fehlerhafte Listdeklaration. Liste ist nur 2 Elemente lang, enthält aber 3
Anfangswerte. Umgekehrter Fall ist aber okay.

list OkayList[10]:=wK wN wP; Die restlichen 7 Elemente werden vom Compiler mit
-1 belegt.



V.4) Zahlen:

Che rechnet mit 32-Bit langen ganzen Zahlen ("signed integer"). Floating point
Zahlen werden nicht unterstützt. Man kann die Zahlen in dezimaler Schreibweise
(10, -10 ...) oder in hexadezimaler Form eingeben. Eine Hexadezimale Zahl
beginnt mit "0x". Die dezimale Zahl 31 lautet Hexadezimal 0x1F bzw. 0x1f
(genauso wie in C). Eine hexadezimale Zahl wird aber ebenfalls als signed
integer behandelt.

Ferner kann man Zahlen auch als symbolische Konstanten definieren.

Beispiele:

const GOODMVBONUS:=20;

Der Compiler ersetzt im folgenden Code "GOODMVBONUS" durch die Zahl 20. Die
Verwendung von GOODMVBONUS erhöht die Lesbarkeit des Programmes. Will man den
Bonus verändern, so braucht man die Änderung nur an einer Stelle vornehmen. Bei
häufig verwendeten Zahlen mit einer bestimmten Bedeutung sollte man daher immer
symbolische Konstanten verweden.

const PWNSPERCOLOR:=8;

const COLORS:=2;

list BothColorPwns[PWNSPERCOLOR*COLORS]; Compiler rechnet das in Liste der Länge
16 um.



V.5) Operatoren:

Che unterstützt alle üblichen arithmetischen, logischen Operatoren. Darüber
hinaus werden auch die meisten C-Bitoperatoren unterstützt. Alle Operatoren
können uneingeschränkt in Ausdrücken verwendet werden. Das zahlenmäßige Ergebnis
von logischen Operatoren ist "1" für wahr, "0" für falsch. Im folgenden werden
die Operatoren gemäß ihres Vorranges (Precedence) aufgezählt. Zuerst erwähnte
Operatoren haben Nachrang gegenüber später erwähnten. Che verwendet dieselben
Vorrangregeln wie C.



V.5.a) Zuweisung:

:= Zuweisungs-Operator.

Beispiele:

x:=1; Der Variablen auf der linken Seite wird der Wert des Ausdruckes auf der
rechten Seite zugewiesen. x hat nun den Wert 1.

y:=x+2;

Nachdem der Zuweisungs-Operator den absoluten Nachrang hat, werden immer zuerst
alle Berechnungen auf der rechten Seite ausgeführt und erst das Endergebnis der
linken Variablen zugewiesen.

V.5.b) Logische Operatoren:

and or Logisches "und" bzw. "oder". Die Auswertung erfolgt dabei nach der
Short-Circuit Methode. Wenn das Resultat eines logischen Ausdruckes feststeht,
wird die weitere Berechung übersprungen.

Beispiel:

if i <> 0 and k/i < 10 then Erzeugt niemals eine Division durch 0. Wenn i den
Wert 0 hat, steht das Ergebnis des Ausdruckes fest und die Berechnung rechts von
and wird nicht ausgeführt.



V.5.e) Bitwise Operatoren:

& | ^ Bitwises "und", "oder" und "xor".

Beispiel:

x:=0x1; Verwendet bei Bitopterationen meist Hex-Zahlen. Bitmuster von 100 ist im
Kopf schwierig zu sehen. Das Bitmuster von 0x64 ist hingegen für das geübte Auge
sofort "sichtbar".

x:=x^0x1; x ist jetzt 0.

x:=x^0x1; und jetzt wieder 1. Beliebter Trick um eine Variable zu "toggelen".

V.5.d) Vergleichsoperatoren:

= <> > >= < <= Ist gleich, Ungleich, Größer, Größer gleich, Kleiner, Kleiner
gleich. (Siehe auch IV.1 compoperator)

Beispiel:

i:=5;

j:= i < 10; j enthält nun den Wert 1, weil das Ergebnis des Vergleichs wahr ist.

k:= i <> 1; k enthält den Wert 0, weil das Ergebnis des Vergleichs falsch ist.

Anmerkung: Wie man aus den obigen Beispielen sieht, kann man
Vergleichsoperatoren nicht nur in if ...then else Konstrukten einsetzen, sondern
auch beliebig damit rechnen (Sind überall dort erlaubt, wo eine "Expression"
stehen kann. Also fast überall).



V.5.e) Arithmetisches Plus, Minus:

+ - Plus, "Minus"

Beispiel:

hansi:=1;

susi:=1;

family:=hansi + susi + 1;

singlemother:=family - hansi;

V.5.f) Arithmetische Multiplikation, Divison und Modulo:

* / % Multiplikation, Divison, Modulo.

Die Berechnung werden dabei in Ganzzahl Arithmetik durchgeführt.

Beispiel:

x:=5/2; x hat Wert 2, Rest wird bei Ganzzahlarithmetik weggeschnitten.

x:=9%7; x hat Wert 2. Modulo ist Rest von Divison.

y:=4*x+1; x hat Wert 9. Hier sieht man den Effekt der Vorzeichenregel.
Multiplikation hat Vorrang gegenüber der Addition. Es wird zuerst 4 mit 2
multipliziert und danach wird 1 dazugezählt. Hätte die Addition den Vorrang,
würde zuerst addiert und das Ergebnis wäre 12.

y:=4*(x+1); Jetzt ist das Ergebnis 12. Die Klammer ist die Rettung unter den
Operatoren. Sie hat immer Vorrang. Im Zweifelsfall über die Auswirkungen der
Vorrangregeln sollte man daher immer Klammern verwenden.

V.5.g) Unäre Operatoren:

Alle vorhergehenden Operatoren waren binär. Sie verarbeiteten 2 Operanden zu
einem neuen Ergebnis (z.B. 3 * 5 ). Die folgenden Operatoren "bearbeiten" nur
einen Operand. Die unären Operatoren haben alle denselben Vorrang. Kommen
mehrere unäre Operatoren in einem Ausdruck vor, so gilt die Regel: wer zuerst
kommt mahlt zuerst.

! - ~ ++ -- Logisches Not, unäres Minus, Bitwises Complement, Increment
Operator, Decrement Operator.

Beispiele:

if !PcOnSqrs(wP,e4) then Führe nachfolgende Operation aus, wenn kein weißer
Bauer auf e4 steht.

x:=-5; Weise x den Wert -5 zu.

const REDLCD:=0x10; Angenommen wir steuern LCDs verschiedener Farbe an (jedes
LCD entspricht einem Bit in der Variable LcdsOn). Wir wollen jetzt das rote LCD
löschen, aber alle anderen LCDs unverändert lassen.

Mit dem Komplementoperator (gesetztes Bit wird 0, nicht gesetztes 1) und dem
bitweisen and geht das ganz einfach.

LcdsOn:=LcdsOn & (~REDLCD); Klammer ist nicht notwendig, erhöht aber die
Lesbarkeit.

i:=1;

while i<= 10 do Durchlaufe die Schleife von 1 bis 10.

i++; Erhöhe den Wert von i um 1. Effizentere Form von i:=i+1;

end;

i:=10; Dasselbe noch einmal in der anderen Richtung von 10 nach 1.

while i>=1 do

i--; Effizentere Form von i:=i-1;

end;

V.5.h) Klammern:

Runde Klammern dienen zur Veränderung der Vorrangregeln. Außerdem werden sie zur
Begrenzung der Parameterliste von Routinen verwendet (wird noch besprochen). Mit
eckigen Klammern können die einzelnen Elemente einer Liste angesprochen werden.

Beispiele:

sqr:=a1;

while (sqr:=NxtSqr(sqr))<> CHE_INVSQR do

end;

Die Schleife durchläuft alle Felder eines Schachbrettes. Die Klammer in while ..
ist notwendig, weil sonst zuerst der logische Operator <> ausgewertet und danach
erst das Ergebnis zugewiesen würde. Ohne Klammer ergibt das eine Endlosschleife.
sqr würde ewig zwischen den Werten für b1 und c1 hin und her pendeln.

mylist[0]:=CHE_LISTEND; Setzt das 0-te Element (Listen fangen beim Element 0 an)
auf den Wert CHE_LISTEND. D.h. Diese Anwendung löscht die Liste.

i:=0;

while sourcelist[i] <> CHE_LISTEND do

targetlist[i]:=sourcelist[i];

i++;

end;

targetlist[i]:=CHE_LISTEND; Notwendig, weil sonst targetlist keinen Ende-Marker
hätte.

Kopiert die Elemente der Liste targetlist nach sourcelist.





V.6) Prozeduren und Funktionen:

Die Gliederung eines Che-Programmes erfolgt über Prozeduren und Funktionen.
Prozeduren und Funktionen unterscheiden sich in einem einzigen Punkt. Eine
Funktion liefert einen Wert (32-Bit Zahl) als Ergebnis zurück. Eine Prozedur
nicht. Ein Funktion darf man daher überall dort verwenden (aufrufen), wo man
auch eine gewöhnliche Variable verwenden kann (D.h. in Expressions). Ein
Prozeduraufruf steht dagegen immer für sich alleine (ist ein Statement).
Ansonsten besteht aber kein Unterschied.

Syntax: procedure|function <Name>'(' [Parameterlist] ')'

[Lokale Variablendeklaration]

begin

<Statements>

end;

Beispiele:

function IsPwnEndGame()

begin

return (!PcOnSqrs(?O,..));

end

Die Funktion(aus stdlib.che) hat keine Paramter oder lokale Variablen, ruft
PcOnSqrs auf und gibt das logische NOT dieser Funktion zurück (0 falls Offiziere
am Brett stehen, 1 falls es keine Offiziere gibt).

procedure GoodSqrs(piece,list sqrlist)

begin

SetSqrs(piece,CHE_SQRBONUS,sqrlist);

end;

Die Prozedur GoodSqrs hat 2 Paraemeter. Die normale Variable piece und die Liste
sqrlist. Diese beiden Variablen werden zusammen mit der symbolischen Konstanten
CHE_SQRBONUS an die Advice-Funktion SetSqrs weiter gegeben.



Wichtig: Falls eine Routine normale Variablen und Listen als Parameter hat,
müssen immer zuerst die normalen Variablen und danach erst die Listen stehen.

procedure WrongGoodSqrs(list sqrlist,piece)

ist daher ein Syntax Fehler.

Normale Variablen in der Parameterliste werden immer mit "Call by Value"
übergeben. D.h. der Wert der Variablen wird außerhalb der Prozedur nicht
verändert. Bei Listen wird die Adresse übergeben. D.h. der Inhalt von Listen
wird durch Operationen innerhalb einer Routine auch außerhalb verändert.

Beispiele:

function AddOne(i)

begin

i++; Dieses Increment hat außerhalb der Procedure keinen Einfluß.

return i;

end;

i:=0;

AddOne(i);

j:=i; i wurde durch AddOne nicht verändert. j wird also der Wert 0 zugewiesen.

Bei Listen ist das aber anders. Die folgende Prozedur aus stdlib.che kopiert die
Elemente von FromList nach ToList. Der Inhalt von ToList wird auch außerhalb der
Prozedur verändert. i,j sind lokale Variablen, die für die Operation innerhalb
der Procedure verwendet werden. Die Operationen für diese lokalen Variablen
wirken sich außerhalb der Procedure in keiner Weise aus (selbst wenn irgendwo
anders eine Variable auch i oder j heißt). Listen kann man in Che V1.0 nicht
lokal definieren (Vielleicht aber in V2.0).

procedure CopyList(list ToList,list FromList)

var i,j;

begin

j:=0;

for i in FromList do

ToList[j]:=i;

j++;

end;

ToList[j]:=CHE_LISTEND;

end

Was tut man, wenn eine Variable durch eine Funktion verändert werden soll?

Lösung a)

i:=0;

i:=AddOne(i); Man verwendet den return-Wert einer Funktion und weist ihn der
Variable zu. i hat nun den Wert 1.

Lösung b) (Mit Vorsicht zu verwenden).

Definiert Variable global und greift in Routine direkt auf die globale Variable
zu. Man darf die Variable in diesem Fall aber NICHT als Parameter definieren, da
ein Parameter die globale Definition aufhebt.

Lösung c)

Man übergibt nicht den Wert der Variablen (Call-by-Value) sondern die Adresse
der Variablen (Call by Reference). Diese Lösung ist in Che (im Gegensatz zu C)
NICHT erlaubt, weil damit die berühmt berüchtigten Pointer eingeführt werden.
Falls aber von Che Anwendern der dringende Bedarf nach Lösung c) begründet
werden kann, könnte diese Möglichkeit in Version 2.0 eingeführt werden.

Wenn eine Routine aufgerufen wird muß sie zuvor zuvor deklariert worden sein.
Was geschieht aber wenn Funktion A Funktion B aufruft und diese wiederum
Funktion A? Man kann dazu Funktionen und Prozeduren vorwärts definieren und zwar
mit:

extern function Alpha(depth);

extern function Evaluate();

function Beta(depth)

begin

if depth<=1 then

return -Evaluate();

else

return Alpha(depth-1);

end;

end;

function Alpha(depth)

begin

if depth<=1 then

return Evaluate();

else

return Beta(depth-1);

end;

end;

Irgendwo (kann aber auch in einem anderen File sein) muß man natürlich auch noch
die Funktion Evaluate programmieren. Falls dies nicht geschieht, meldet der
Compiler am Ende der Übersetzung "Link Error, Routine Evaluate not defined".

Anmerkung: Der obige Code ist das Grundgerüst für den Alpha-Beta Algorithmus.

Funktionen und Prozeduren können sich auch selber aufrufen. Ein klassisches
(aber praktisch unsinniges) Beispiel ist die Berechnung der Fakulät von n.

function fac(n)

begin

if n <= 1 then

return n;

else

return n*fac(n-1);

end;

end;

V.6.a) Die Main-Prozedur:

In Che sind alle Prozeduren und Funktionen gleichberechtigt. Jede kann jede
andere aufrufen. Nur eine Prozedur ist gleicher. Die Prozedur main(). Jedes
Che-Programm muß eine derartige Funktion haben. Der Compiler meldet bei
Abwesenheit einen "Link-Error". Die Che-Runtime-Umgebung ruft beim Starten eines
Programmes von sich aus main() auf. Obwohl syntaktisch erlaubt, ist es im
Allgemeinen keine blöde (sondern eine saublöde) Idee von anderen Funktionen aus
main() aufzurufen (main() sollte sich auch nicht selber aufrufen).



V.7) Kontrolsstrukturen, Verzweigungen:

Che bietet 4 Möglichkeiten um vom normalen linearen Ablauf eines Programmes
abzuweichen.



V.7.a) IF-THEN-ELSE

Syntax: if <Expression> then <Statements> [ elseif <Expression> then
<Statements> ] [else <Statements> ] end;

Der elseif und der else Zweig ist dabei optional. Die eckigen Klammern kommen
nicht im Code vor. Sie drücken nur aus, daß elseif bzw. else optional sind.

Beispiel:

k:=GamePhase(); Bestimme die Spielphase

if k= MIDGAME then Falls wir im Mittelspiel sind

MidGameRules(); Mittelspiel-Regeln anwenden.

elseif k = ENDGAME or k = LATEENDGAME then

EndGameRules(); Im Endspiel oder späten Endspiel die Endspielregeln.

else

PawnEndGameRules(); Ansonsten die Bauern-Endspielregeln.

end;

V.7.b) Switch/Case Verzweigung.

Im obigen Beispiel kann die Spielphase 4 Werte annehmen. Anstatt die
Fallunterscheidung über eine Reihe von if ... elseif ..else vorzunehmen, ist es
in solchen Fällen oft übersichtlicher die switch-case Verzweigung zu verwenden.

Syntax: switch <Expression> { case <Constant-Expression>: <Statements> [break; ]
} end;

Die geschweiften Klammern bedeuten, daß man beliebig viele case-Statements
verwenden kann (inklusive keines). Auch das break; am Ende eines case statements
ist optional. Falls es verwendet wird, verzweigt das Programm an die erste Zeile
nach dem switch-end; Statement. Fehlt das break; werden die Statements des
nächsten case-Statements auch ausgeführt.

Anmerkung: Es ist alles wie in C.

Der obige Code läßt sich mit switch-case wie folgt schreiben (siehe auch
shanny.che):

switch GamePhase()

case MIDGAME:

MidGameRules();

break;

case ENDGAME: ENDGAME geht sofort zu den Befehlen von LATEENDGAME weiter (kein
break;)

case LATEENDGAME:

EndGameRules();

case PAWNENDGAME:

PawnEndGameRules();

end;

Ob man das Problem mit if-then oder switch-case formuliert ist weitgehend
Geschmackssache. Der Che-Compiler erzeugt meist auch identischen Code.

V.7.c) Goto Statement:

Heiß umfedet, wild umstritten ist dieses Statement. Manche möchten es für immer
in die Informatik Hölle verbannen, manche hegen eine heimliche perverse Liebe
dafür. Che ist eine tolerante Sprache. Wer es verwenden will, soll es verwenden.
Wer nicht, kann das Problem auch ohne goto hinbringen.

Syntax: goto <label>;

Beispiel:

if FatalError() then

goto exception; Irgendetwas schlimmes ist passiert, springe zur
Ausnahmebehebung.

end;

return DoNormalProcessing();

exception: Das ist ein Label.

Cleanup();

return 0;

Man kann goto in jeder beliebigen Situation einsetzen. Der übliche
Verwendungszweck ist aber wie im obigen Beispiel. Viele gotos erzeugen den
berühmt berüchtigten Spagetti-Code. Man kann mit goto nur innerhalb einer
Routine springen. Che verbietet (wie C) Sprünge nach außen.

Anmerkung: Modernste Programmiersprachen haben das goto verbannt. Nachdem sich
das Böse aber nicht ausrotten läßt, schleicht sich das goto über das sogenannte
Exception-Handling wieder durch die Hintertür herein.



V.7.d) Return-Statement:

Prozeduren oder Funktionen können an beliebiger Stelle im Programmablauf mit
einem return statement verlassen werden.

Syntax: return ; Im Falle einer Prozedur.

return <Expression>; Im Falle einer Funktion.

Prozeduren geben keinen Wert zurück. Darum darf nach dem return kein Ausdruck
mehr stehen. Eine Funktion liefert hingegen den Wert des nach return stehenden
Ausdrucks zurück.

Das obige Beispiel läßt sich ohne goto wie folgt programmieren:

if FatalError() then

Cleanup();

return 0;

end;

return DoNormalProcessing(); Funktionsaufruf ist auch eine Expression.

Falls der Code Teil einer Prozedur ist, müßte man das Fragment so schreiben:

if FatalError() then

Cleanup();

return;

end;

DoNormalProcessing();

return;

V.8.) Programm-Schleifen:

Häufig will man in einen Programm dieselbe Operation für verschiedene Werte
einer Variable wiederholt ausführen. Dazu dienen Schleifen (die in einigen
Beispielen bereits vorgekommen sind). Che kennt 3 Schleifenkonstrukte.



V.8.a) While-Schleife:

Syntax: while <Expression> do <Statements> end;

Solange des Ergebnis des Ausdrucks wahr ist (jeder Wert ungleich 0) wird die
Schleife durchlaufen.

Beispiel:

function NonRecursiveFac(n)

var fac;

begin

fac:=n;

while n>= 1 do

fac:= fac*(fac-1);

n--;

end;

return fac;

end;

Diese ist die nicht-rekursive Variante der Fakultätsberechnung aus dem Kapitel
V.6). Diese Variante ist weniger elegant, aber wesentlich effektiver als die
rekursive fac() Funktion.



V.8.b) Repeat-Until-Schleife:

Syntax: repeat <Statements> until <expression>;

Die Repeat-Until-Schleife wird immer zumindest einmal durchlaufen. Sie wird
beendet, wenn das Ergebnis der Expression nach dem until das erste Mal wahr
(ungleich 0) wird.

Beispiel:

procedure CopyList(list ToList,list FromList)

var i,j;

begin

i:=0;

repeat

k:=FromList[i];

ToList[i]:=k;

i++;

until k=CHE_LISTEND;

end;

Jede Liste enthält zumindest den abschließenden List-Marker. Es wird daher
mindestens ein Element von FromList[] nach ToList[] übertragen. Die Schleife
wird solange fortgesetzt, bis der Endmarker das kopierte Element ist.



V.8.c) For -In-Schleife:

Die bisher besprochenen Konstrukte kommen in dieser oder sehr ähnlicher Form in
vielen anderen Sprachen ebenfalls vor. Die For-In-Schleife ist ein spezielles
Che-Konstrukt zur einfacheren Handhabung von Listen.

Syntax: for <Variable> in <Liste> do <Statements> end;

Die Variable durchläuft in der For-In-Schleife alle Elemente der Liste. Die
Schleife wird beim Erreichen des Listen-Endes abgebrochen.

Beispiele (Kopie des Codes aus EndGame.che):

procedure RooksBehindPassed(factor)

var sqr;

begin

GetPassed(w,pwns); # list pwns is filled with positions of passed pawns.

for sqr in pwns do # loop through each sqr.

SetSqrs(?R,GetDwnSqrs(sqr,tmplist)*factor,tmplist);

end;

Analoger Code für schwarze Freibauern wurde hier weggelassen.

end;

Dies ist der Code-Teil für die weißen Freibauern der Prozedur
RooksBehindPassed() aus EndGame.che. Die Funktion GetPassed(w,pwns) füllt
zunächst die Liste pwns mit den Positionen der weißen Freibauern. In der
anschließenden For-In-Schleife durchläuft die Variable sqr alle gültigen Werte
(alle Freibauern Positionen) dieser Liste. Die Funktion GetDwnSqrs(sqr,tmplist)
legt in der tmplist die Position aller Felder hinter (unterhalb) des Freibauern
ab. SetSqrs() erhöht anschließend den Felder-Bonus für die Türme auf diesen
Feldern. Der Bonus berechnet sich dabei aus dem Return-Wert von GetDwnSqrs()
(Anzahl der Felder hinter Freibauern) mal dem Parameter factor. Im Falle von
mehreren Freibauern wird sich der Turm damit hinter dem am weitest vorgerückten
stellen.

Die Funktion CopyList läßt sich mit der For-In-Schleife so schreiben:

procedure CopyList(list ToList,list FromList)

var i,j;

begin

j:=0;

for i in FromList do

ToList[j]:=i;

j++;

end;

ToList[j]:=CHE_LISTEND;

end

Im Unterschied zu der Repeat-Until-Schleife müssen wir hier den End-Marker extra
anhängen, weil die Schleife beim Erreichen des End-Marker nicht mehr durchlaufen
wird.

Wegen der Bedeutung von Listen in Che wurde der Code für die For-In-Schleifen
speziell optimiert. Diese Version wird daher etwas effektiver ausgeführt wie die
Repeat-Until-Schleife. Die For-In-Schleife dürfte auch die elegantere und
übersichtlichere Formulierung sein.



V.9) Input-Output:

Che ist diesbezüglich eine eher armselige Sprache. Allerdings wurden gegenüber
Version 1.0 in Kooperation mit Mathias Feist/ChessBase wesentliche
Verbesserungen eingeführt.



V.9.a) Der Text-Datentyp:

Syntax: text <Variablenname> "beliebiger Text";

Text kann nur global, d.h. außerhalb eine Funktion oder Prozedur, definiert
werden. Der zwischen den " stehende Text kann durch Aufruf der Funktion
TextOut() ausgegeben werden.

Anmerkung: Die gesamte Zeile der text-Definition darf 256 Zeichen nicht
überschreiten.



V.9.b) Die Textausgabe:

Syntax: TextOut(<Text-Variable>);

Es wird der durch die Text-Variable definierte Text (alles was zwischen den "
steht) in das Ausgabe-Fenster der ChessBase Oberfläche geschrieben. Man kann
dadurch z.B. die Definition von bestimmten Mustern überprüfen (siehe die
Prozedur Colle() in midgame.che).

Beispiel:



text wColle "white Colle Position";

procedure Colle()

begin

if IswColle() then

TextOut(wColle); Falls Colle-Formation vorliegt, gib den Text white Colle
Position aus.



wColleAdvices();

end;

end;

V.9.c) Ausgabe von numerischen Ausdrücken:

Syntax: write <Expression>;

Write schreibt den Wert der Expression im dezimal- und im hexadezimal-Format in
das Ausgabe-Fenster der ChessBase Oberfläche.

Der write-Befehl ist ebenfalls zum Debuggen nützlich. Man kann damit z.B. den
Rückgabewert von Funktionen überprüfen.

Beispiel:



write GetScore(1); Gibt die Bewertung des letzten vom Programm berechneten Zuges
im Ausgabefenster aus.



V.9.d) Ausgabe von Ratschlägen:

Syntax: SetVerbose(Wert);

Beschreibung: Falls Wert ungleich 0 ist (CHE_TRUE), wird das Programm in den
Verbose (geschwätzig) Modus gesetzt. Im Verbose-Modus werden vorgeschlagene Züge
sowie ihre Bewertung im ChessBase-Ausgabe-Fenster angezeigt. Ferner wird eine
Veränderung des Spielstiles gemeldet. Die übrigen Ratschläge - wie z.B. die
Veränderung von Felderbewertungen - werden aber auch im Verbose-Mode NICHT
angezeigt. Mit SetVerbose(0); kann diese Ausgabe unterdrückt werden.

Anmerkung: Im Ausgabe-Fenster erscheint in jeder Stellung "Starte-Analyse" und
"Beende-Analyse". Diese Ausgabe erfolgt von der ChessBase-Oberfläche und kann
mit SetVerbose(0) nicht abgestellt werden.



Damit haben wir das Ende der Che-Sprachbeschreibung erreicht. Das abschließende
Kapitel enthält Hintergrund Information, die für die praktische
Che-Programmierung nicht unbedingt notwendig ist.



VI) Die Java- und die Che-Virtuelle-Maschine (CVM):

Bei der Compilierung von Che wird - wie in Java - virtueller Maschinencode
erzeugt. Dieser wird dann von der in Nimzo99 "sitzenden" CVM ausgeführt. Das vom
Internet-Explorer (u.A.) verwendete Java Pendant heißt JVM. Die CVM und die JVM
haben einen ähnlichen Befehlssatz (Art der verwendeten virtuellen Maschinen
Instruktionen). Der Befehlssatz heißt virtuell, weil er nicht direkt in Hardware
abläuft, sondern von einem Programm, der virtuellen Maschine, in Software
ausgeführt wird. Man könnte aber ohne weiteres eine Java bzw. Che Hardware
bauen, die diesen Code direkt ausführt. Die Struktur von CVM ist z.B. wesentlich
einfacher als der Intel-Maschinencode.

Anmerkung: Virtuelle Maschinen wurden bereits in der Computer-Steinzeit
erfunden. Sie sind aber erst mit der Verfügbarkeit von (zu) schneller Hardware
praktisch sehr wichtig geworden. Ein Pentium 400 läuft auch mit einem 1/5tel der
effektiven Geschwindigkeit für viele Aufgaben noch immer schnell genug.

Der wesentliche Unterschied zwischen der JVM und CVM ist die angestrebte
Optimierung. Das oberste Ziel bei der JVM-Entwicklung war ein möglichst kleines
Programm (Optimierung von Speicherplatz bzw. Übertragungsbandbreite). CVM wurde
bezüglich Ausführungsgeschwindigkeit und möglichst einheitlichen und einfachen
Befehlssatz optimiert.

In der JVM wird daher jeder Befehl mit 1-Byte dargestellt. Die Befehlsoperanden
werden ebenfalls in einem möglichst kleinen Format gespeichert. Z.B. Gibt es in
der JVM "nahe" und "ferne" Sprünge. Ein naher Sprung kann maximal 127 Bytes nach
vorne oder zurück im Programmcode springen, ein ferner Sprung rund 32.000 Bytes.
Bei einem Sprung über 32.000 Bytes wird die JVM zum Tempelhüpfer. Sie muß über
mehrere Zwischenetappen springen. Ein kurzer Sprung ist 2-Bytes lang, ein langer
3-Bytes. Java kennt aus demselben Grund auch kurze (2Byte) und lange (4 Byte)
Integer Zahlen.

Die CVM verwendet sowohl für Befehle als auch Befehlsdaten einheitlich die auf
INTEL-Prozessoren schnellste Datenlänge von 4-Bytes. Dadurch benötigt die CVM
z.B. auch nur eine Sprungart, den "flat" Sprung, der theoretisch bis zu
2Gigabyte weit springen kann. Der Preis für diese Vereinfachung und
Geschwindigkeits-Optimierung: Ein Sprungbefehl ist in der CVM 8 Bytes lang. Dies
gilt sinngemäß auch für allen anderen Befehle.

Durch die größere Einheitlichkeit kann der Che-Compiler theoretisch auch
optimaleren CVM Code erzeugen als die JVM. Der aktuelle Che-Compiler nützt diese
Möglichkeiten aber nur im bescheidenen Ausmaß in Form eines sogenannten Loophole
(Schlüsselloch) Optimierers aus. Einen guten Optimierer zu schreiben ist fast so
kompliziert und aufwendig wie gute Fehlermeldungen.

Wie in Java wäre es auch bei der Che-Compilierung möglich direkten
Maschinen-Code (bzw. Intel-Assembler- Code) zu erzeugen. Dies wäre sogar relativ
einfach realisierbar. Allerdings würde dadurch ein Che-Benützer auch den
Visual-C++ Compiler benötigen um die Nimz99.dll mit dem jeweiligen
Che-Programmteil zusammenbauen zu können.

Die JVM wurde in den 80er Jahren entwickelt und ist eine für 8- bzw. 16-Bit
Prozessoren optimierte virtuelle Maschine. Die CVM ist die JVM Weiterentwicklung
für die 32-Bit Architektur der 90er Jahre.

In der Che-Developer-Suite kann man mit dem Befehl Build-Assemble (Alt+A)
symbolischen CVM -Code erzeugen. Die detaillierte Beschreibung aller CVM-Befehle
würde aber den Rahmen dieses Dokuments sprengen.






This page took 0.01 seconds to execute

Last modified: Thu, 15 Apr 21 08:11:13 -0700

Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.