In Ausgabe 12/87 und 1/88 stellten wir Kampfinterpreter für die gängigsten Computersysteme vor. Hier wollen wir in die Technik der Kampfprogrammierung einsteigen.
Der Computer lädt zwei Programme in seinen Arbeitsspeicher und beginnt deren Instruktionen auszuführen. Die Programme, die bis dahin friedlich auf dem Massenspeicher ruhten, erwachen zum Leben. Sie testen Speicherstellen und verschieben sich auf andere Positionen. Analysiert man ihren Programmcode, wird man feststellen, daß sie versuchen, das andere Programm im Speicher ausfindig zu machen. Sie verändern dessen Programmcode, machen es unwirksam, vernichten es. Das ist ihre Aufgabe, dazu wurden sie programmiert.
Keine Angst, es geht hier nicht um die Fortsetzung von »Tron«, sondern um Kampfprogramme Die Bezeichnung mag dabei etwas irreführen, denn Kampfprogramme sind völlig harmlos. Jedenfalls verhalten sie sich uns Menschen gegenüber freundlich. Am ehesten könnte man sie mit den siamesischen Kampffischen vergleichen, denn einem anderen Kampfprogramm machen sie den Garaus. Im allgemeinen handelt es sich bei den Kampfprogrammen um ein Strategiespiel. In Ausgabe 12/87 und 1/88 stellten wir sogenannte Kampfinterpreter für den ST, Amiga, PC und C 64 vor. Wir wollen hier etwas genauer erklären, wie Kampfprogramme entwickelt werden, und welche Strategien dabei eine Rolle spielen.
Zu Beginn eines jeden Gefechts lädt der Kampfinterpreter die Programme an zufällige Stellen in seinen Speicher. Die Programme wissen nicht, wo sie sich oder der Gegner befinden. Anschließend werden die Befehle der Programme abwechselnd abgearbeitet. Sobald der Interpreter auf einen nicht ausführbaren Befehl stößt, hat das Programm verloren. Von den zehn Befehlen, die der Interpreter versteht, ist nur einer nicht ausführbar, der »DAT«-Befehl. Dieser wird von einem Programm gleichzeitig zum Speichern von Daten verwendet. Da er vom Interpreter nicht ausführbar ist, liefert er prima Munition, mit dem das gegnerische Programm außer Gefecht gesetzt werden kann. Gelingt es einem Kampfprogramm, diesen Befehl in dem gegnerischen Code zu plazieren, hat es schon fast gewonnen. Dazu muß es jedoch den DAT-Befehl verschießen können.
Unter anderem ist dazu der »MOV«-Befehl zuständig. Dem MOV-Befehl folgen zwei Zahlen, die angeben. was transportiert wird, und wohin es geschrieben werden soll.
MOV #0 1
schreibt die Zahl Null in den Speicherplatz. der einen Platz hinter dem MOV-Befehl liegt.
An dieser Stelle wollen wir kurz die Adressierungsarten des Kampfinterpreters ansprechen. Wie schon angedeutet, wissen die Programme nicht, an welcher Stelle sie im Speicher stehen. Sie können nur Speicherstellen vor oder nach ihrer eigenen Position bestimmen. Würde die zweite Zahl im obigen Beispiel eine 2 sein, würde die 0 zwei Speicherplätze nach dem MOV-Befehl geschrieben werden. Zusätzlich gibt es die Adressierungsart »&«. Steht dieses Zeichen vor einer Zahl, schaut der Interpreter erst entsprechend viele Zeichen vor oder nach dem Befehl nach, welche Zahl dort steht. Diese neue Zahl benutzt er dann, um die tatsächliche Adresse vor oder hinter dem Befehl zu verwenden.
MOV #0 &1
DAT 833
In diesem Fall holt sich der MOV-Befehl die Zahl, die einen Speicherplatz hinter dem MOV-Befehl steht, hier der DAT-Befehl. Dieser enthält die Zahl 833. Der MOV-Befehl schreibt die Zahl Null also 833 Speicherplätze hinter seine eigene Adresse. Anstelle des DAT-Befehls kann dort jeder beliebige Befehl stehen. Der MOV-Befehl würde dann immer den zweiten angegebenen Wert verwenden. Grundsätzlich beziehen sich Befehle zum Verändern von Speicherstellen auf den zweiten Wert eines Kommandos. Dieser kann von ADD-, SUB-, DJZ-und MOV-Befehlen verändert werden. Die erste Zahl eines Befehls bleibt immer unangetastet.
Zurück zu unserem ersten kleinen Kampfprogramm. Dieses soll quer durch den Speicher DAT-Befehle schießen, in der Hoffnung, auf diese Weise den Gegner zu treffen.
MOV 3 &3
ADD #3 2
JMP -2
DAT 833
In diesem Fall wurde das Zeichen »#« vor der ersten Zahl des MOV-Befehls weggelassen. MOV transportiert nun keine Zahl, sondern einen ganzen Befehl. Und zwar den Befehl, der drei Adressen weiter hinten im Programm steht, der DAT-Befehl. Dieser Todesschuß soll nun auf die Speichersteile 833 Schritte hinter dem MOV-Befehl losgelassen werden (Sie erinnern sich an das »&«-Zeichen?). Hier haben wir auch gleich einen kleinen Programmiertrick verraten, denn der DAT-Befehl ist gleichzeitig Munition und Zielangabe.
Auf diese Weise spart man sich einen Programmschritt, das Kampfprogramm wird kleiner, und von anderen Programmen schwerer zu treffen. Und schließlich ist es dem getroffenen Programm egal, welche Nummer der Schuß enthielt.
Damit der DAT-Befehl nicht immer auf die gleiche Adresse geschossen. und damit der Speicher möglichst flächendeckend bombardiert wird, erhöht der folgende ADD-Befehl die Zieladresse um drei. Dadurch bleiben natürlich Lücken im Sperrfeuer, diese sind jedoch so klein gewählt, daß ein gegnerisches Programm kaum durchschlüpfen kann. Kleiner sollte man den Wert allerdings nicht wählen, da sonst zuviel Zeit verstreicht, bis unser Programm den Speicher durchsiebt hat. Der nachfolgende JMP-Befehl springt zwei Adressen zurück, auf den MOV-Befehl. womit das Spielchen von vorne beginnt.
Ein Programm gibt es allerdings, das ganz einfach durch die Maschen unseres DAT-Netzes schlüpfen kann: das »Knirps«-Programm. Es besteht aus nur einem Befehl:
MOV 0 1
wodurch es extrem schnell und effektiv wird. Knirps kopiert sich selber einfach eine Speicherstelle weiter. Da das Programm anschließend an der nächsten Adresse abgearbeitet wird, an der jetzt ebenfalls ein Knirps steht, frißt sich das Programm relativ schnell durch den Speicher. Das Gemeine an Knirps ist aber die Tatsache, daß dieses den Gegner einfach überrollt. Anstelle des gegnerischen Programmcodes stehen dann lauter Knirps-Anweisungen. wodurch aus dem Gegner ebenfalls ein Knirps wird, der nur noch dem Feind-Knirps hinterher rollen kann. Als Kampfprogrammierer sollte man sich jedoch hüten, nur den Knirps aufs Schlachtfeld zu schicken. Schnell sind nur noch Knirpse vorhanden, die friedlich hinterdrein laufen, sich aber nicht gegenseitig bekämpfen können. Der Kampf würde dann unentschieden ausgehen.
Zu diesem kleinen Parasiten gibt es natürlich auch ein Vertilgungsmittel. Der Fliegenfänger für den Knirps heißt »Knirps-Falle«, und ist fast ebenso kurz wie der Knirps selber. Das Programm nutzt dabei die wohl einzige Schwäche des Knirps aus. Er schleicht sich immer von hinten an ein Feindprogramm an. um dies dann anzuknabbern. Wird die erste Speicherstelle vor dem Kampfprogramm also beständig mit einem DAT-Befehl bombardiert, müßte es möglich sein, den Knirps aufzuhalten. Genau das macht die Knirps-Falle:
MOV 2 -1
JMP -1
DAT 0
Die Knirps-Falle könnte man jetzt mit unserem DAT-Sperrfeuer-Programm kombinieren. Ein entsprechendes Programm sähe dann so aus:
MOV 4 -1
MOV 3 &3
ADD #3 2
JMP -3
DAT 833
Das Programm hat aber den Nachteil, daß die Knirps-Falle nur jeden vierten Taktzyklus ausgeführt wird. In den anderen drei Taktzyklen könnte ein Knirps also unbemerkt über unser Kampfprogramm hinwegrollen. Dieses Problem kann man mit dem SPL-Befehl elegant umgehen. Dieser Befehl spaltet ein Programm auf. Unser Schießprogramm sähe mit dem SPL-Befehl modifiziert ungefähr so aus:
SPL 3
MOV 5 -2 ;Erster Teil
JMP -1
MOV 3 &3 ;Zweiter Teil
ADD #3 2
JMP -2
DAT 833
Ab jetzt werden immer abwechselnd ein Befehl beim ersten Programmteil und ein Befehl beim zweiten Programmteil ausgeführt. Die Knirps-Falle ist immer genügend oft aktiv, und kann einen Knirps meistens aufhalten. Ein Schlupfloch von einem Taktzyklus bleibt für den Knirps jedoch bestehen: dies kann nicht beseitigt werden.
Mit dem SPL-Befehl lassen sich ganze Heerscharen von Knirpsen in Marsch setzen.
SPL 2
JMP -1
MOV 0 1
Der erste Befehl bindet den Knirps in das Programm mit ein. Dieser rückt beim nächsten Taktzyklus einen Programmschritt vor. Anschließend springt der JMP-Befehl wieder zum SPL. der wiederum einen Knirps aktiviert. Der bereits in Marsch gesetzte Knirps ist unterdessen noch einen Schritt vorgerückt, so daß zwischen dem ersten und zweiten Knirps eine Lücke von einem Programmschritt klafft. Das Spiel setzt sich theoretisch so lange fort, bis der ganze Bildschirm mit Knirpsen gefüllt ist, und das Programm durch die eigenen Knirpse überrollt wird.
Praktisch kann ein Programm bei unseren Kampf-Interpretern nicht mehr als lOOTeüprogramme aktivieren. Irgendwann wird die Knirps-Kolonne abreißen. Erst wenn ein Teilprogramm vom Gegner getroffen wird, setzt die Fabrik einen neuen Knirps in Marsch.
Wenn Sie Kampfprogramme entwickeln. sollten Sie immer etwas vorsichtig mit dem SPL-Befehl umgehen. Einerseits werden mit SPL viele Programme aktiviert. Der Gegner muß um zu gewinnen, alle ausschalten. Gerade bei der Fabrik ist es sehr schwierig, die vielen kleinen Knirpse außer Gefecht zu setzen. Andererseits werden die einzelnen Programme immer weniger oft ausgeführt, je mehr Programme aktiv sind. Bei der Fabrik läßt sich dieses Verhalten sehr gut beobachten. Zu Beginn eines Kampfes werden die Knirpse rasend schnell produziert. Sind aber zirka 50 Knirpse auf dem Bildschirm, geht die Produktion nur noch sehr stockend voran. Sie sollten also mit dem SPL-Befehl experimentieren und gegebenenfalls ab einer bestimmten Zahl ausgeführter SPL-Befehle einen Produktionsstop einbauen.
Zum Schluß dieser kleinen Exkursion wollen wir Ihnen noch ein besonders gemeines Programm vorstellen. das wir aufgrund seines Verhaltens »Karnickl« getauft haben. Dieses Programm produziert am laufenden Band Fabriken und setzt diese irgendwo im Speicher ab. Sie können sich vorstellen, wie die Kampfarena nach kurzer Zeit aussieht.
SPL 12 ;aktiviert Fabrik
MOV <9 <10 ;kopiert Fabrik
CMP #11 8
JMP 2 JMP -8
JMP -3 DAT 14
SUB #5 6 DAT 833
SPL &5 SPL 2 ; Fabrik
MOV #14 3 JMP -1
ADD #653 3 MOV 0 1
Zu Beginn des Programms wird erst einmal eine Fabrik in Betrieb genommen, um Gegner, die sich knapp hinter Karnickl aufhalten, gleich zu schnappen. Der folgende Programmteil kopiert die Fabrik am Ende des Programms in den Speicher. 832 Plätze hinter der Adresse unseres Programms. Das Kleiner-Zeichen (<) hat eine ähnliche Funktion wie das »&«-Zeichen. Zusätzlich wird die Zahl in der Speicherstelle um eins vermindert. Das Größer-Zeichen (>) erhöht eine Zahl um 1. Nachdem die drei Befehle der Fabrik kopiert wurden, nimmt sie der SPL-Befehl in Betrieb. Anschließend wird die Zieladresse für die neue Fabrik um 653 erhöht und das Programm beginnt von vorne.
In diesem Programm fehlen sämtliche Schutzmechanismen, die wir bisher besprochen haben. Im folgenden Programm sind diese alle eingebaut. Zusätzlich besitzt die kopierte Fabrik eine Knirpsfalle, und das Programm verschießt quer durch den Speicher DAT-Bomben.
SPL 3 MOV 3 &3 SPL 16
MOV 6 -2 ADD #133 2 MOV <9 <10
JMP -1 JMP -2 CMP #11 8
SPL 5 DAT 967 JMP 2
JMP -3 JMP -8 JMP -1
SUB #5 6 DAT 18 DAT 0
SPL &5 DAT 833 SPL 2
MOV #18 3 SPL 4 JMP -1
ADD #653 3 MOV 2 -2 MOV 0 1
Übrigens: In Amerika erscheint das »Core War Newsletter», und es gibt die internationale Gesellschaft der Kampfprogramme.
Auch bei Happy-Computer können Sie Ihr Kampfprogramm an einem Wettbewerb teilnehmen lassen. Deshalb verlängern wir den in Ausgabe 12/87 ausgeschriebenen Wettkampf bis zum 29. Februar 1988.
(hf)