Dringend Hilfe: Java Code?

1 Antwort

Du hast ein 3×3-Feld. Das kann man als lineares Feld der Länge 9 abbilden. Ein Wert in Zeile z und Spalte s im 3×3-Feld liegt dann z. B. am Index

int index ( int z, s ) {
    return =z*3+s;
}

Die erlaubten Werte im Feld sind {0, 1, 2}. Dazu braucht man mindestens 2 Bit.

Das Feld wird nun in einem einzigen Integer gespeichert: Die untersten 2 Bit gehören zu Zelle 0, die nächsten 2 zu Zelle 1 usw. Das reicht in Java für 16 Zellen.

Du kannst eine Zelle i auslesen, indem Du das Feld um 2*i nach rechts schiebst und dann den Wert mit der Zellenbreite 0b11 maskierst (damit die anderen Zellenwerte verschwinden):

int get ( int index ) {
    return ( feld >> 2*index ) & 0b11;
}

Du kannst einen neuen Wert in eine leere Zelle schreiben, indem Du den Wert um 2*i nach links verschiebst und dann das Feld damit veroderst:

void set ( int index, int value ) {
    feld |= value << 2*index;
}

Mit Oder kann man aber nur Bits setzen, nicht löschen. Deshalb sollte man vor dem Überschreiben die Zelle erst mal auf 0 setzen (das wurde in Deinem Code komplett übersehen). Dazu verschiebst Du die Zellmaske 0b11 an die richtige Position (z.B. 0..011000), negierst diesen Wert (⇒ 1..1001111) und verundest ihn mit dem Feld:

void clear ( int index ) {
    feld &= ~(0b11 << 2*index);
}

Dein Code schreibt übrigens die Werte 0=frei, 2=spieler1 oder 3=spieler2 in das Feld. Das hat den Vorteil, dass man schneller prüfen kann, ob das Feld voll ist, weil dann alle 9 Zellen das High-Bit gesetzt haben (0x2AAAA=0b101010_101010_101010). Dafür muss man bei jedem Lesen und Schreiben eine Fallunterscheidung machen. Ich finde das unnötig kompliziert.

Mathematik2000 
Fragesteller
 12.12.2022, 20:35

Wie kann man die Methoden gibBesitzer und besetztePosition noch einfacher schreiben?

0
ralphdieter  12.12.2022, 21:04
@Mathematik2000

am Besten, indem man diese verkorksten 0/2/3-Werte wegwirft. Ich schreibe stattdessen mal (spieler+1) rein (also 1/2/3):

    public int gibBesitzer(int zeile, int spalte) {
        int index = zeile*3+spalte;
        return (spielInt>>>2*index & 0b11) - 1;
    }

    public void besetzePosition(int zeile, int spalte, int spieler) {
        int index = zeile*3+spalte;
        spielInt &= ~(0b11 << 2*index); // clear
        spielInt |= (spieler+1) << 2*index;
    }
0
ralphdieter  12.12.2022, 21:12
@ralphdieter

Oder mit den Hilfsmethoden aus meiner Antwort:

    public int gibBesitzer(int zeile, int spalte) {
        return get(index(zeile, spalte)) - 1;
    }

    public void besetzePosition(int zeile, int spalte, int spieler) {
        int index = index(zeile, spalte);
        clear(index);
        set(index, spieler+1);
    }

Ich finde das lesbarer.

0
Mathematik2000 
Fragesteller
 13.12.2022, 04:30
@ralphdieter

Was ist hier 0b11? Was haben Sie für Konstruktur geschrieben, gleiche wie ich?

0
Mathematik2000 
Fragesteller
 13.12.2022, 09:40
@ralphdieter

Kannst du bitte hier einzelnen Zeile ausführlich erklären, was genau passiert ist? Ich verstehe nicht was 0b11 ist und woher kommt es? Warum -1?

0
ralphdieter  13.12.2022, 09:58
@Mathematik2000

0b11 = 11 binär = 3 dezimal.

Warum -1? Das Feld soll die Werte 0 (=leer) oder 1 oder 2 (spieler) enthalten. In deiner Implementierung wird das durch umständliche Fallunterscheidungen als 0 (=leer) oder 2 oder 3 (spieler+1) geschrieben, damit istVoll() einfacher wird. Ich halte das für unnötig kompliziert.

Mein Vorschlag verwendet als Kompromiss die Werte 1 (=leer) oder 2 oder 3 (spieler+1). Damit entfallen die Fallunterscheidungen. Ich schreibe stattdessen immer den Wert spieler+1 und gebe den Wert−1 als Besitzer zurück.

Man könnte das Feld nun im Konstruktor mit 0x15555 = 0b010101_010101_010101 initialisieren, oder jeden Besitzer≤0 (statt ==0) als freies Feld akzeptieren.

Kannst du bitte hier einzelnen Zeile ausführlich erklären

Die Methoden get(), set() und clear() habe ich schon in meiner Antwort erklärt. noch ausführlicher schaffe ich es nicht.

0
ralphdieter  13.12.2022, 19:22
@Mathematik2000

Damit werden alle 9 Plätze mit 01 (=leer) initialisiert. In der Binärdarstellung ist es leichter zu erkennen.

0
ralphdieter  13.12.2022, 19:25
@Mathematik2000
Warum 2*index

Weil für jede Position 2 Bits gebraucht werden. Für Platz Nr. 5 muss man um 10 Bit verschieben.

0
ralphdieter  14.12.2022, 20:27
@Mathematik2000
Bmbedeutet 01 LEER?

Eigentlich sollen die Werte 0=leer oder 1/2=Spieler geschrieben werden. Dein Programm zählt bei den Spielern Eins dazu, damit sich leichter prüfen lässt, ob alle Plätze belegt sind. Die Spielerwerte 2 und 3 (binär 10 und 11) haben nämlich das obere Bit gesetzt.

Ich habe das vereinfacht, indem ich immer Eins addiere (und beim Auslesen subtrahiere). Jetzt wird also die 0 als 1 gespeichert. Die Prüfung auf das obere Bit klappt hier genauso.

0
ralphdieter  14.12.2022, 20:37
@Mathematik2000
Und warum &0b11?

Beim Verschieben der Bits von spielInt fallen ja nur die Positionen vor der gewünschten weg. die Positionen dahinter sind noch da und müssen gelöscht werden, damit nur der Inhalt an der gewünschten Position übrig bleibt:

8 . 7 . 6 .. 5 . 4 . 3 .. 2 . 1 . 0 <--- index
01 01 11 10 01 10 11 10 01 | Shift (>> 2*3)
00 00 00 01 01 11 10 01 10 | Mask (& 0b11)
00 00 00 00 00 00 00 00 10

0
Mathematik2000 
Fragesteller
 14.12.2022, 19:59

Warum schreiben wir hier &3? Was bedeutet das?

0
ralphdieter  14.12.2022, 20:53
@Mathematik2000

3 == 0b11. Der Programmierer war hier wohl nur zu faul zum Tippen.

Ich bin ja schon froh, dass er in istVoll() wenigstens eine Hex-Zahl (0x2AAAA) und nicht dezimal 174762 geschrieben hat.

Wenn es um die Bitpositionen in einer Zahl geht, sind Konstante im Binärformat (0b...) lesbarer, obwohl sie länger sind. Bei 0b10_10_10_10_10_10_10_10_10 sieht man nämlich sofort, welche Bits gemeint sind, bei 0x2AAAA kann man es mit etwas Übung erkennen, aber bei 174762 brauchen die meisten einen Taschenrechner.

0
ralphdieter  15.12.2022, 13:53
@Mathematik2000

Die 3 ist die Bitmaske, in der nur die Bits der Zelle 0 gesetzt sind. nochmal ein Beispiel, wie man an den Wert in Zelle 5 kommt:

8 . 7 . 6 .. 5 . 4 . 3 .. 2 . 1 . 0 <--- index
01 01 11 10 01 10 11 10 01 | Shift (>> 2*5)
00 00 00 00 00 01 01 11 10 | Inhalt der Zellen 5-8
00 00 00 00 00 00 00 00 11 | Maske 0b11
00 00 00 00 00 00 00 00 10 | Wert = Inhalt & Maske

0
Mathematik2000 
Fragesteller
 16.12.2022, 10:57
@ralphdieter

benutzen wir immer die Maske 3, es ist fest? Woher sollen wir wissen, dass wir die Maske 3 schreiben sollen und nicht die Maske 22, 4 oder 1?

0
Mathematik2000 
Fragesteller
 15.12.2022, 18:37

Welche Rolle spielt hier die Operator ~ ? KANNST DU DEN Code nochmal erklären?

else if(spieler == 0) {
            setzung = ~(3<<(zeile*3*2)+(spalte*2));
            spielInt = spielInt&setzung;
0
ralphdieter  15.12.2022, 18:58
@Mathematik2000

Damit wird eine Zelle gelöscht (=auf 0 gesetzt). Dazu wieder ein Beispiel für Zelle 5:

8 . 7 . 6 .. 5 . 4 . 3 .. 2 . 1 . 0 <--- index

00 00 00 00 00 00 00 00 11 | Maske 0b11 (= 3)
00 00 00 11 00 00 00 00 00 | Maske << 2*5
11 11 11 00 11 11 11 11 11 | negiert (~) -- im Code „setzung“
01 01 11 10 01 10 11 10 01 | altes Spielfeld
01 01 11 00 01 10 11 10 01 | Spielfeld & neg

Das Ergebnis ist ein Spielfeld, bei dem nur die Bits von Zelle 5 gelöscht wurden.

0
Mathematik2000 
Fragesteller
 15.12.2022, 19:03
@ralphdieter

Also wenn spieler gleich 0 ist, dann schieben wir erstmal nach rechts und dann löschen wir die Zelle. Sagt der Code das?

1
Mathematik2000 
Fragesteller
 16.12.2022, 08:53
@ralphdieter ich habe den Code geändert ist jetzt auch richtig?
 /**
     * Gibt den Besitzer der angegebenen Position auf dem Spielfeld.
     * 
     * @param zeile  vertikale Position (0-2)
     * @param spalte horizontale Position (0-2)
     * @return 0 (unbesetzt), 1 (Spieler 1), 2 (Spieler 2)
     */
    public int gibBesitzer(int zeile, int spalte) {
        int index= zeile*3+spalte; 


        int schiebung = 2*(zeile*3+spalte);
        int geschobenerInt = spielInt>>schiebung; 


        if((geschobenerInt&3) == 0b01) {            //0b01= 1 Dezimal
            return 1;
        } else if((geschobenerInt&3) == 0b10) {     //0b10 = 2 Dezimal
            return 2;
        } else {
            return 0;
        }
    }


    /**
     * Besetzt die angegebene Position auf dem Spielfeld fuer einen Spieler.
     * 
     * @param zeile   vertikale Position (0-2)
     * @param spalte  horizontale Position (0-2)
     * @param spieler 0 (leer), 1 (Spieler 1), 2 (Spieler 2)
     */


    public void besetzePosition(int zeile, int spalte, int spieler) {
        int setzung;
        if(spieler == 1 || spieler == 2) {
            setzung = spieler <<2*(zeile*3+spalte); 
            spielInt = spielInt|setzung;
        }
        else if(spieler == 0) {
            setzung = ~(3<<2*(zeile*3+spalte));
            spielInt = spielInt&setzung;
        }
    }


    /**
     * Überprüft ob das Spielfeld komplett voll ist
     * return   boolean true falls Spielfeld voll ist
     */


    public boolean istVoll() {
        for(int zeile = 0; zeile<3; zeile++) {
            for(int spalte = 0; spalte<3; spalte++) {
                if(gibBesitzer(zeile, spalte) == 0) {
                    return false;
                }
            }
        }
        return true;
    }
}
0
Mathematik2000 
Fragesteller
 16.12.2022, 10:12
@ralphdieter Gilt alle Beispiele zu diesem Code auch, z. B. HIER:
00 00 00 00 00 00 00 00 11 | Maske 0b11 (= 3)
00 00 00 11 00 00 00 00 00 | Maske << 2*5
11 11 11 00 11 11 11 11 11 | negiert (~) -- im Code „setzung“
01 01 11 10 01 10 11 10 01 | altes Spielfeld
01 01 11 00 01 10 11 10 01 | Spielfeld & neg
0
Mathematik2000 
Fragesteller
 16.12.2022, 10:14
@Mathematik2000

Oder hier:

8 . 7 . 6 .. 5 . 4 . 3 .. 2 . 1 . 0 <--- index
01 01 11 10 01 10 11 10 01 | Shift (>> 2*5)
00 00 00 00 00 01 01 11 10 | Inhalt der Zellen 5-8
00 00 00 00 00 00 00 00 11 | Maske 0b11
00 00 00 00 00 00 00 00 10 | Wert = Inhalt & Maske
0
Mathematik2000 
Fragesteller
 16.12.2022, 12:55
@ralphdieter

nein, der Test zeigt Fehler, ich muss unbedingt (spieler*1) und ==3 für spieler 2 schreiben, damit der Code funktioniert

0
ralphdieter  16.12.2022, 22:15
@Mathematik2000

Die Beispiele zeigen, wie man Bits an einer bestimmten Position setzt bzw. löscht. Das gilt ganz allgemein.

0
ralphdieter  16.12.2022, 22:45
@Mathematik2000
nein, der Test zeigt Fehler

Welcher Test? Welcher Fehler? Ich glaube nicht, dass ich etwas übersehen habe.

Das einzige Problem (auf das ich schon in meiner Antwort hinwies) tritt auf, wenn Du einen Spieler≠0 auf eine Position setzt, die nicht 0 (leer) ist: Dabei werden nämlich nur Bits auf 1 gesetzt (mit Oder), aber nie auf 0 (dafür braucht man Und).

Der Code aus Deiner Frage funktioniert nicht, wenn man eine Position von Spieler 2 (alter Wert=3=0b11) mit Spieler 1 (neuer Wert=2=0b10) überschreiben will. Dann bleibt nämlich Spieler 2 drin.

Im neuen Code funktioniert das auch nicht, und zusätzlich scheitert der Fall, Spieler 1 mit Spieler 2 zu überschreiben. In beiden Fällen steht danach eine 3=0b11 drin, und diesen Spieler gibt es gar nicht.

Zur Abhilfe sollte man die Position immer erst (mit &) löschen und dann (mit |) neu beschreiben:

    public void besetzePosition(int zeile, int spalte, int spieler) {
        int setzung;

        // Alten Wert löschen:
        setzung = ~(3<<2*(zeile*3+spalte));
        spielInt = spielInt&setzung;

        // Neuen Wert schreiben:
        setzung = spieler <<2*(zeile*3+spalte); 
        spielInt = spielInt|setzung;
    }

Wie Du siehst, braucht man dann keine Fallunterscheidung mehr.

0
Mathematik2000 
Fragesteller
 16.12.2022, 11:11

Warum mit den Wert mit der Zellenbreite 0b11 maskierst? woher kommt 0b11? Wie kommen Sie auf 3 und nicht 2 oder 4?

0
ralphdieter  16.12.2022, 22:21
@Mathematik2000
Wie kommen Sie auf 3 und nicht 2 oder 4?

Um 3 verschiedene Werter zu speichern, braucht man mindestens zwei Bit. Deshalb hat die Maske für einen Zellenwert hier zwei Bits gesetzt.

Für 4 Spieler + leer bräuchtest Du schon 3 Bit. Dann wäre die Maske drei Bit lang: 0b111 (=7).

0