Programmieranleitung: Spieler
und Spielstrategien
Jeder Fußballspieler wird durch eine Java-Klasse definiert.
Mehrere Spieler einer Mannschaft können Instanzen derselben Klasse
sein. Es ist aber auch möglich und sinnvoll für verschiedene
Spieler verschiedene Klassen vorzusehen (z.B. Abwehrspieler,
Stürmer, etc.).
Das Framework zum Programmieren kann von der Informatik 1 Homepage
heruntergeladen werden. Zum Testen des Frameworks kann der
Dummy-Spieler TestPlayer.java verwendet werden.
Die Basisklasse zum Ausbauen
Das folgende kleine Java Programm genügt, um einen Spieler zu
implementieren, der nur herumsteht und sonst nichts tut:
import
org.RoboBall.*;
public
class BasicPlayer extends Player
{
public BasicPlayer() { super(); }
public SoccerAction makeMove()
{
return SoccerAction.nothing();
}
} |
Die Bestandteile des Programms sind schnell aufgezält. Mit "import
org.RoboBall.*;" werden alle zur Verfügung stehenden
Aktionen, die die Spieler durchführen können zugänglich
gemacht. Der Konstruktor "public
BasicPlayer()" ruft lediglich den Konstruktor der Oberklasse Player
auf. Es ist möglich - aber nicht nötig - den Konstruktor zu
erweitern. Die eigentliche Aktivität des Spielers wird in der
Methode "public
SoccerAction makeMove()" definiert. Diese Methode wird vom
Spielprogramm regelmäßig aufgerufen. Jeder Spieler kommt so
reihum einmal dran, bevor die Runde mit dem nächsten Spielzug
wieder von vorne beginnt. Bei jedem Aufruf kann die Methode dann eine
Aktion (vom Typ SoccerAction)
zurückgeben. Die zur Verfügung stehenden
Aktionen werden weiter unten in diesem Tutorial aufgelistet und
beinhalten im wesentlichen Bewegungen des Spielers. Bevor man sich
für eine Bewegung entscheidet, ist es möglich, sich auf dem
Feld umzusehen, festzustellen, wo man (der Spieler) selbst steht, wo
die
anderen Spieler und der Ball sich befinden. Auch dafür gibt es
fertig implementierte Methoden, die von "public
SoccerAction makeMove()" aufgerufen werden können.
Ein erster eigener Spieler
Um eigene Spieler zu schreiben, erzeugt man sich am besten eine Kopie
der Datei "TestPlayer.java",
nennt sie um in "MyFirstPlayer.java"
oder "MyOffensePlayer.java",
benennt den Namen der Klasse darin auch entsprechend um und editiert
den Rumpf der Methode "makeMove()".
Ein erster Spieler, der mehr als nur nichts tut, wäre zum Beispiel
der folgende:
import
org.RoboBall.*;
public
class MyFirstPlayer extends Player
{
public MyFirstPlayer() { super(); }
public SoccerAction makeMove()
{
return SoccerAction.move(UP);
}
} |
Dieser Spieler macht bei jedem Spielzug einen Schritt nach
vorne. Bewegungen können sein "move(UP)",
"move(DOWN)",
"move("RIGHT)"
und "move(LEFT)".
Hierbei bedeutet "UP"
immer "in Richtung des gegnerischen Tores, "DOWN"
immer in Richtung des eigenen Tores, "RIGHT"
immer in Richtung der rechten Seite aus der Sicht des eigenen Tores,
und "LEFT"
immer in Richtung der linken Seite aus der Sicht des eigenen Tores. Die
Bewungsrichtungen sind also immer unabhängig von der Aufteilung
der
Platzhälften, von der Position der Spieler und von der - sowieso
nicht vorhandenen - Blickrichtung.
Ein paar "Beispieler"
Im folgenden sind einige Spieler aufgeführt, die ein paar einfache
Strategien verfiolgen (der Einfachheit halber ist nur die Methode
"makeMove()" angegeben:
// Spieler, der
sich immer zum
// gegnerischen Tor hin bewegt.
public SoccerAction makeMove()
{
return SoccerAction.move(UP);
} |
// Ein Spieler,
der wartet, bis der Ball
// bei ihm erscheint, und dann nach vorne dribbelt.
public SoccerAction makeMove()
{
if (BallAround(getMyPos()) {
return SoccerAction.dribble(UP);
}
return SoccerAction.nothing();
} |
// Spieler, der
als Torwart fungiert,
// und immer versucht, den Ball auf
// der Linie abzuweheren.
public SoccerAction makeMove()
{
if (getBallXPos() < getMyXPos())
return SoccerAction.move(LEFT);
if (getBallXPos() > getMyXPos())
return SoccerAction.move(RIGHT);
return SoccerAction.move(DOWN);
} |
// Ein Spieler,
der, wenn er den Ball hat,
// versucht, diesen zum weiter vorne stehenden
// Mannschaftskameraden abzuspielen.
public SoccerAction makeMove()
{
if (lookTo(UP) == BALL)
return SoccerAction.pass(UP);
} |
So lassen sich beliebige weitere Spielstrategien programmieren.
Über die Funktionen sendMessageTo
und sendMessageToAll kann man
Informationen zwischen den Spielern austauschen. Die Nachricht ist
dabei immer ein Object
(z.B. ein String).
Mit receiveMessages
bekommt man ein Array aller Nachrichten, die an einen während der
letzten Runde geschickt wurden. Wenn man sie nicht verwendet verfallen
sie zur nächsten Runde.
Im folgenden werden die einzelnen
durchführbaren Aktionen aufgelistet. Eine vollständige
Dokumentation im JavaDoc-Format steht auch auf dem Server zum Download
zur Verfügung.
Mögliche Spielaktionen
Es gibt 5 mögliche Aktionen: move,
run, dribble, pass und nothing.
Die ersten haben als Parameter eine Richtung. Richtungen können
sein UP,
DOWN, LEFT und RIGHT.
move(<Richtung>)
Bewegt den Spieler einen Schritt in die
angegebene Richtung. Falls auf dem Zielfeld ein Spieler egal welcher Mannschaft
steht, wird dieser in diese Richtung geschoben. Falls kein Platz zum Schieben vorhanden
ist (anderer Spieler, Spiefeldrand, Ball), wird ein Foul erzeugt. Bei einem Foul
tauscht der gefoulte Spieler seine Position mit dem Foulenden. Falls auf dem Zielfeld
der Ball ist, wird dieser auch verschoben. Bälle koennen wie Spieler geschoben
werden.
run(<Richtung>)
Bewegt den Spieler um zwei Schritte in
die angegebene Richtung. Falls dabei nur ein Schritt gemacht werden
kann, weil das um zwei Schritte entfernte Feld besetzt ist, dann
verhält sich run(<RICHTUNG>)
wie move(<Richtung>).
dribble(<Richtung>)
Bewegt den angrenzendem Ball auf das
Feld, das einen Schritt vom Spieler die angegebene Richtung liegt. D.h.
dribble(UP)
positioniert den Ball vor den Spieler, egal wo er vorher war. Der
Spieler bewegt sich nicht. Der Ball kann nicht auf ein Feld gedribbelt
werden, auf dem schon ein Spieler steht.
pass(<Richtung>)
Bewegt den Ball in die angegebene
Richtung so lange bis er auf einen Spieler stößt. Kurz davor
bleibt er stehen. Wenn in der angegebenen Richtung kein Spieler mehr
kommt, dann wird der Ball nur um eine Position verschoben.
nothing()
Tut nichts. Bleibt einfach stehen.
Zustandsabfragen
Für das Finden einer sinnvollen Spielaktion können
verschiedene Informationen über den Spielzustand eingeholt werden.
Einige Mehtoden erwarten als Parameter eine Richtungsabgabe (die
gleichen Konstanten wie bei den Spielaktionen) oder eine Ortsangabe.
Ortsangaben können in Form von zwei Koordinaten (int
x, int y) angegeben werden. Es ist auch möglich, statt
dessen eine Instanz der java.awt.Point
Klasse zu verwenden. Die Zustandsabfragemöglichkeiten sind:
Wo ist der Ball?
Dazu können die Methoden ballAround(<ORT>), getBallPos(), getBallXPos(),
und getBallYPos()
verwendet werden. ballAround()
liefert als Ergebnis eine Richtungskonstante (UP,
DOWN, LEFT, RIGHT oder NOTHING).
getBallPos()
liefert als Ortsangabe des Balls ein java.awt.Point-Objekt,
die anderen beiden Methdden liefern jeweils die X oder Y Koordinate des
Balls als int-Wert. Mit selfBallAround()
erhält man die Information ob der Ball auf einem dem Spieler
angrenzenden Feld liegt.
Kann ich ein Tor schießen?
Um festzustellen, ob mit einem Spielzug ein Tor gemacht werden
könnte bietet sich der Aufruf folgender Methoden an: canScoreGoal()
liefert eine SoccerAction,
die unmittelbar zu einem Tor füren würde oder null, falls dies nicht möglich ist. canScoreGoal(<ORT>)
liefert die gleiche Information unter der Annahme, daß ein
Spieler der eigenen Mannschaft an dem angegebenen Ort stehen würde.
Wo sind die (anderen) Spieler?
getMyPos()
liefert die eigene Position als java.awt.point-Objekt. Alternativ kann
man auch getMyXPos()
und getMyYPos()
verwenden, um die Position als getrennte X und Y Koordinaten zu
erhalten. findRobots()liefert
alle Positionen der Roboter aus dem angegebenen Team
(entweder MY_TEAM
oder OTHER_TEAM)
als Array von java.awt.Point-Objekten.
D.h. zum Beispiel findRobots(OTHER_TEAM)[0].X
ist die X-Koordinate des ersten (naja nullten) Spielers der
gegnerischen Mannschaft. Manchmall kann es interessant sein, zu
erfahren, was für ein Spieler auf einem Bestimmten Feld steht.
Dazu
dient die Methode getRobotClassName(java.awt.Point
pos). So läßt sich die Entscheidung für einen
Spielzug in Abhängigkeit der Klasse der anderen Spieler machen. getRobotClassName()funktioniert
bei eigenen Spielern.
Mit den Methoden nextOtherRobotAround(<RICHTUNG>)
und nextOtherRobotAround(<ORT>)
erhält man die Richtungskonstante, in welcher der Spieler steht,
der als erstes auf das angegebene Feld oder anf aus in die angegebene
Richtung angrenzende Feld treten darf . Das gleiche gibt es auch
für Spieler der eigenen Mannschaft, nämlich als nextOwnRobotAround(...).
oder mannschaftsunabhängig als nextRobotAround(<ORT>).
Sich umschauen
int
lookAt(<ORT>) und int
lookTo(<RICHTUNG>) liefern Informationen über den
Inhalt der durch den Ort angegeben Felder oder der durch die Richtung
angegebenen an mich angrenzenden Felder. Die Rückgabewerte vom Typ
int sind die (selbsterklärenden) Konstanten: BALL,
MY_TEAM, OTHER_TEAM, FREE, oder WALL.
Man kann sogar "um die Ecke" und "durch andere Spieler hindurch"
blicken
mit int
lookToRel(<ORT>,<RICHTUNG>) oder mit
int lookToRel(<ORT>, <RICHTUNG>, int distance).
Diese Methoden erlauben es von einem angegeben Ort aus in die angegeben
Richtung um ein Feld oder um die angegebene Distanz Felder zu blicken.
Kann ich einen Pass spielen?
Dazu liefern die Methoden isPassPossible(<ORT>,<RICHTUNG>)
und isSelfPassPossible(<RICHTUNG>)
informationen. Die Antworten sind vom Typ boolean und sagen, ob ich
oder
der Roboter am angegebenen Ort in die angegebene Richtung passen kann.
Sonstige Infos
Die Methode int getPriority(<ORT>) gibt die Priorität
des Roboters auf dem angegebenen Feld. Je kleiner die Priorität,
desto früher kommt der
Roboter an die Reihe.
Weitere weniger wichtige, aber trotzdem aufrufbare Methoden sind die
folgenden:
int
getFieldWidth(), int getFieldHeight(), java.awt.Dimension getFieldSize()
liefern Größenangaben des Spielfeldes. Bei unserem Turnier
wird dies aber immer 13x15 sein (wobei y=0 und y=14 die Torbereiche
sind). int
getRelativeDirection(java.awt.Point from, java.awt.Point to)
liefert eine Richtungskonstante, nämlich die Richtung in der der
Punk to
aus Sicht des Punktes from
liegt. Die Methode moveOrRun(<RICHTUNG>)
liefert eine SoccerAction
als Vorschlag, wie der Ball bestmöglich zu erreichen ist.