fbpx Drücke „Enter”, um zum Inhalt zu springen.

Android: TicTacToe Funktionen programmieren – Part 02

Tobbe erklärt

Im ersten Teil der Android TicTacToe Reihe haben wir das Layout für das Spiel erstellt. Wir erinnern uns daran zurück, dass wir nur neun Buttons in die App eingebaut haben, sodass wir nur diese mit Java verändern müssen.

Im ersten Schritt müssen wir die neun Buttons initialisieren und den Button in Java mit dem in XML verknüpfen. Dieses wird wie folgt gemacht:

Initialisierung der Buttons:

Button f11, f12, f13, f21, f22, f23, f31, f32, f33;

Den Button in Java mit dem in XML verknüpfen:

f11 = findViewById(R.id.field_1_1);
f12 = findViewById(R.id.field_1_2);
f13 = findViewById(R.id.field_1_3);
f21 = findViewById(R.id.field_2_1);
f22 = findViewById(R.id.field_2_2);
f23 = findViewById(R.id.field_2_3);
f31 = findViewById(R.id.field_3_1);
f32 = findViewById(R.id.field_3_2);
f33 = findViewById(R.id.field_3_3);

Jetzt haben wir unsere Buttons initialisiert und mit XML verknüpft. Jedoch passiert aktuell noch gar nichts, wenn der Benutzer auf den Button klickt. Dieses werden wir im nächsten Schritt ändern. Wir müssen die Buttons mit Hilfe von dem OnClickListener dazu bringen, dass sie es merken wenn sie gedrückt werden.

setOnClickListener(this):

f11.setOnClickListener(this);
f12.setOnClickListener(this);
f13.setOnClickListener(this);
f21.setOnClickListener(this);
f22.setOnClickListener(this);
f23.setOnClickListener(this);
f31.setOnClickListener(this);
f32.setOnClickListener(this);
f33.setOnClickListener(this);

In die Klammern haben wir ein this geschrieben, sodass der OnClickListener in der gesamten Java Klasse nach der Methode onClick(View v) sucht. Wahrscheinlich hast du jetzt gerade noch einen Fehler unter dem this. Gehe mit dem Cursor auf das this und drücke Alt und Enter. Jetzt wähle aus, dass die Klasse Game.java die Klasse View.OnClickListener implementieren soll. Im nächsten Fenster wählst du aus, dass die Methode onClick(View v) implementiert werden soll. Danach sollte dein Code wie folgt aussehen:

Klassendefinition:

public class Game extends AppCompatActivity implements View.OnClickListener {

onClickMethode(View v):

@Override
public void onClick(View v) {

}

Die onClick(View v) Methode wird immer aufgerufen, wenn ein Button der onClickListener gesetzt wurde gedrückt wird, aufgerufen. Wir müssen im nächsten Schritt überlegen, was passieren soll wenn ein Button gedrückt wird. Außerdem müssen wir rausfinden, welcher Button überhaupt gedrückt wurde. Das können wir mithilfe der switch-case Anweisung realisieren. Wenn ein Button gedrückt wird, soll dieser entweder X oder O als Text bekommen. Außerdem soll eine Methode aufgerufen werden, die das Spielgeschehen speichert und prüft ob das Spiel beendet ist.

Um X oder O zu setzen erstellen wir zuerst eine Variable vom Typ String und geben ihr den Wert X.

String xo = "X";

In der OnClick(View v) Methode prüfen wir nun welcher Button gedrückt wurde und führen zwei Codezeilen aus. Wunder dich nicht, wir rufen nun eine Methode auf, die noch nicht existiert. Als Übergabewerte in der Methode geben wir die Position des Buttons mit. Also sozusagen die X und die Y Koordinate:

public void onClick(View v) {
switch (v.getId()){
case R.id.field_1_1:
f11.setText(xo);
handleInput(1, 1);
break;
case R.id.field_1_2:
f12.setText(xo);
handleInput(1, 2);
break;
case R.id.field_1_3:
f13.setText(xo);
handleInput(1, 3);
break;
case R.id.field_2_1:
f21.setText(xo);
handleInput(2, 1);
break;
case R.id.field_2_2:
f22.setText(xo);
handleInput(2, 2);
break;
case R.id.field_2_3:
f23.setText(xo);
handleInput(2, 3);
break;
case R.id.field_3_1:
f31.setText(xo);
handleInput(3, 1);
break;
case R.id.field_3_2:
f32.setText(xo);
handleInput(3, 2);
break;
case R.id.field_3_3:
f33.setText(xo);
handleInput(3, 3);
break;
}
}

Als nächstes werden wir die Methode handleInput implementieren. Wie du schon im Code davor gesehen hast, hat die Methode zwei Übergabeparameter. Diese sind vom Typ int und haben den Namen x und y für die jeweilige Koordinate:

private void handleInput(int x, int y){

}

Um das Spielgeschehen speichern und auswerten zu können werden wir ein zweidimensionales Array vom Typ int erstellen. In diesem werden für X die 1 gespeichert und für O die -1. Dadurch können wir bei der Auswertung erkennen, dass wenn eine Reihe 3 ergibt, X gewonnen hat und bei -3 der Spieler mit dem O gewonnen hat. Das Array wird wie folgt erstellt:

Über die OnCreate Methode:

int[][] gameStorage;

In die OnCreate Methode:

gameStorage = new int[3][3];

Nun haben wir ein Array vom Typen int erstellt. Das Array hat die Größe drei mal drei. Eine Besonderheit eines Array ist, dass es anfängt bei null zu zählen. Bei einer Größe von drei gibt es also die Arraypositionen 0, 1 und 2. Bei einem zweidimensionalen Array sieht die Nummerierung so aus:

(0, 0)(1, 0)(2, 0)
(0, 1)(1, 1)(2, 1)
(0, 2)(1, 2)(2, 2)

Zurück zu der erstellten handleInput Methode. Die Methode soll überprüfen, ob der ausgewählte Button noch frei ist. Ist er frei, so soll entweder ein X oder O gespeichert werden. Achtung: In diesem Teil wird der originale Button noch überschrieben, sodass es optisch nicht auffällt, jedoch sollte man das nicht machen. Dieses behandeln wir jedoch im nächsten Teil. Zurück zur Methode, es wird überprüft ob der Button noch frei ist und dann der Text gesetzt. Außerdem wird die Eingabe in dem Integer Array gespeichert. Wurde ein X gesetzt, so ändert sich der Wert der Variablen xo auf O und anders herum.

Der eben beschriebene Kontrollmechanismus in der handleInput Methode:

if (gameStorage[x-1][y-1] == 0){
if (xo.equals("X")){
gameStorage[x-1][y-1] = 1;
xo = "O";
} else {
gameStorage[x-1][y-1] = -1;
xo = "X";
}

Wie du siehst, ziehen wir von x und y immer eins ab. Das kommt dadurch, dass das Array anfängt bei null zu zählen, unsere Buttons jedoch bei eins. Außerdem siehst du, dass wenn ein X gesetzt wird, in dem Array gameStorage an der passenden Stelle eine eins gesetzt wird und bei einem O eine minus eins.

Am Ende der Methode handleInput müssen wir noch überprüfen, ob das Spiel beendet wurde oder nicht. Das Spiel ist beendet, wenn ein Spieler drei mal das gleiche Symbol in einer der acht möglichen Reihen hat. Drei vertikal, drei horizontal und zwei diagonal. Dazu erstellen wir die Methode checkGameEnd(). Diese ist vom Typ boolean und muss somit einen Rückgabewert haben der entweder richtig oder falsch ist.

Programmiert sieht das so aus:

private boolean checkGameEnd(){
return    (Math.abs(gameStorage[0][0] + gameStorage[0][1] + gameStorage[0][2]) == 3
|| Math.abs(gameStorage[1][0] + gameStorage[1][1] + gameStorage[1][2]) == 3
|| Math.abs(gameStorage[2][0] + gameStorage[2][1] + gameStorage[2][2]) == 3
|| Math.abs(gameStorage[0][0] + gameStorage[1][0] + gameStorage[2][0]) == 3
|| Math.abs(gameStorage[0][1] + gameStorage[1][1] + gameStorage[2][1]) == 3
|| Math.abs(gameStorage[0][2] + gameStorage[1][2] + gameStorage[2][2]) == 3
|| Math.abs(gameStorage[0][0] + gameStorage[1][1] + gameStorage[2][2]) == 3
|| Math.abs(gameStorage[0][2] + gameStorage[1][1] + gameStorage[2][0]) == 3);
}

Durch die Methode abs aus der Klasse Math bekommen wir den Betrag des Wertes. Der Betrag eines Wertes beachtet nicht das Vorzeichen, das heißt, dass eine minus drei zu einer drei wird. Hierbei musst du jedoch darauf achten, dass du den Betrag des Ergebnisses nimmst und nicht jeden einzelnen Wertes. Wenn drei gleiche in einer der acht möglichen Reihen im Array sind bekommt man entweder drei oder minus drei als Ergebnis. Durch den Betrag bekommen wir eine positive drei. Die beiden senkrechten Striche || stehen für ein oder. Durch die beiden Gleichzeichen == vergleichen wir den Betrag des Ergebnisses mit der drei. Wenn mindestens eine der acht Möglichkeiten eine drei ist, wissen wir, dass das Spiel beendet ist. Der Rückgabewert wird also true. Im anderen Fall läuft das Spiel weiter und der Rückgabewert ist false.

Die Methode checkGameEnd() liefert einen Rückgabewert vom Typ boolean. Deshalb können wir die Methode direkt in die if-Abfrage einbauen. Ist der Rückgabewert true, ist ebenfalls die if-Abfrage erfüllt. Das Spiel wird durch die noch zu erstellende Methode finishGame() beendet.

Das sieht dann so aus:

if (checkGameEnd()){
finishGame();
}

Die ganze Methode handleInput sieht so aus:

private void handleInput(int x, int y){
if (gameStorage[x-1][y-1] == 0){
if (xo.equals("X")){
gameStorage[x-1][y-1] = 1;
xo = "O";
} else {
gameStorage[x-1][y-1] = -1;
xo = "X";
}
}
if (checkGameEnd()){
finishGame();
}
}

Zum Abschluss muss noch die Methode finishGame() implementiert werden. Diese soll prüfen wer der Gewinner ist und einen Toast auf dem Handy anzeigen. Ein Toast ist eine Nachricht, die für eine Zeit unten auf dem Handy angezeigt wird. Dieses mal müssen wir nicht noch einmal auf das Array zugreifen, denn wir wissen, dass einer von beiden gewonnen hat. (Unentschieden folgt im nächsten Part!) Wir prüfen mit einer if-Abfrage welcher Spieler als nächstes dran gewesen wäre. Denn bevor das Spiel beendet wurde, wurde der Wert in der Variablen xo schon geändert. Das heißt, wenn xo den Wert X hat, so hat O gewonnen und anders herum. Das sieht wie folgt aus:

private void finishGame(){
if (xo.equals("X")){
Toast.makeText(getApplicationContext(), "O gewinnt!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "X gewinnt!", Toast.LENGTH_LONG).show();
}
}

Zum Abschluss müssen wir das Spiel noch neu starten. In unserem einfachen Fall machen wir das, in dem wir einfach die Activity neu aufrufen. Eine bessere Variante wäre es, wenn wir den Text der Buttons entfernen und das Array leeren. Wir rufen in unserem Beispiel aber wie gesagt die Activity neu auf. Das machen wir indem wir einen neuen Intent erstellen. Als Werte übergeben wir this, für die aktuelle Klasse und Game.class für die aufzurufende Klasse. In Java Code sieht das dann so aus:

Intent intent = new Intent(this, Game.class);
startActivity(intent);
this.finish();

Die ganze finishGame() Methode:

    private void finishGame(){
if (xo.equals("X")){
Toast.makeText(getApplicationContext(), "O gewinnt!", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "X gewinnt!", Toast.LENGTH_LONG).show();
}

Intent intent = new Intent(this, Game.class);
startActivity(intent);
this.finish();
}
}

Das war der zweite Teil der TicTacToe Reihe. Du kannst aktuell schon die App benutzen und das Spiel spielen, jedoch musst du aktuell noch die App neu starten, wenn es ein Unentschieden gibt. Das beheben wir aber im dritten Teil!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.