Black Jack Karten Anzahl begrenzen

nils812

Neues Mitglied
Servus Leute, habe ne simple Frage.
Ich arbeite gerade an nem Black Jack spiel und möchte nun die 8 verschiedenen Karten auf je 4 begrenzen.
Das heißt, wenn ich zwei mal ne 8 gezogen habe, sollten nur noch 2 vorhanden sein.
Hatte vorher die Überlegung gehabt, es mit nem Array zu machen und dann jeweils die Zahl zu schließen, wenn die bestimme Zahl gezogen wird, das geht ja aber nicht, da man ja keine Elemente aus einem Array löschen kann.
Ich wäre sehr dankbar für einen Denkanstoß oder Lösungsansätze
 

mihe7

Top Contributor
Du solltest den Spaß halt entsprechend modellieren. Es gibt Farben
Java:
public enum Suite {
    HEARTS, DIAMONDS, CLUBS, SPADES
}
und Werte
Java:
public enum Rank {
    ACE, KING, QUEEN, JACK, 
    TEN, NINE, EIGHT, SEVEN, SIX, FIVE, FOUR, THREE, TWO
}
die gemeinsam eine Spielkarte bilden:
Java:
import java.util.Objects;

public class Card {
    private final Suite suite;
    private final Rank rank;
    
    public Card(final Suite suite, final Rank rank) {
        this.suite = Objects.requireNonNull(suite);
        this.rank = Objects.requireNonNull(rank);
    }
       
    @Override
    public boolean equals(Object obj) {
        if (obj == null || obj == this || !(obj instanceof Card)) {
            return obj == this;
        }
        
        Card other = (Card) obj;
        return suite == other.suite && rank == other.rank;
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(suite, rank);
    }
    
    public String toString() {
        return "Card [suite=" + suite + ", rank=" + rank + "]";
    }
}

Darauf aufbauend kann man ein Kartendeck implementieren, z. B.:
Java:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Deck {
    private final List<Card> cards;

    Deck(List<Card> cards) {
        this.cards = new ArrayList<>(cards);
    }
    
    public static Deck ofAllCards() {
        List<Card> cards = new ArrayList<>();
        for (Suite suite : Suite.values()) {
            for (Rank rank : Rank.values()) {
                cards.add(new Card(suite, rank));
            }
        }
        
        return new Deck(cards);
    }
        
    public void shuffle() {
        Collections.shuffle(cards);
    }
    
    public Card dealOneCard() {
        if (cards.isEmpty()) {
            throw new IllegalStateException("Deck is empty");
        }
        
        return cards.remove(cards.size() - 1);
    }
    
    public boolean isEmpty() {
        return cards.isEmpty();
    }
}

Ob Du intern Arrays, Listen oder sonst irgendwas verwendest, spielt dabei nicht die entscheidende Rolle, denn das wird gekapselt. Von außen sieht das Deck immer gleich aus:

Java:
public class Test {
    public static void main(String[] args) {
        Deck deck = Deck.ofAllCards();
        System.out.println("Not shuffled");
        show(deck);
        
        System.out.println();
        
        deck = Deck.ofAllCards();
        deck.shuffle();
        System.out.println("Shuffled");
        show(deck);
    }
    
    private static void show(Deck deck) {
        while (!deck.isEmpty()) {
            System.out.println(deck.dealOneCard());
        }
    }    
}

Mal so als Denkansatz.
 

nils812

Neues Mitglied
Puh, das ist aber irgendwie sehr komplex, dachte, dass geht mit weniger Zeilen Code.
Was genau müsste ich jetzt davon implementieren, damit es funktioniert?
@mihe7
 

KonradN

Super-Moderator
Mitarbeiter
Es ist wichtig, dass man alles gut strukturiert. Natürlich kann man das auch alles mit weniger Code abbilden. Nur eben hast Du da dann kaum noch eine Chance, es wirklich verständlich zu machen.

Schauen wir uns nur die Karten an: Das kann man auch einfach als Zahl abbilden:
Das Enum Rank kann man als Zahl abbilden ... angefangen von 0 .... da es 13 Karten sind, hätte man Werte von 0..12
Die Farben hat man dann als Werte von 0...3

Damit hat man dann für jede Karte einen Wert von 13*Farbwert+Rangwert

Und Du kannst dann aus dieser Zahl immer zurück zu den Werten: Farbwert = Kartenwert / 13 und Rangwert = Kartenwert % 13

Wenn Du nun ein Deck haben willst, dann kannst Du einfach ein Array mit 4*13 Feldern erstellen. Dieses initialisierst Du dann mit dem index.
Dann eine Variable, die angibt, wie viele Karten noch da sind.

Eine Karte ziehen bedeutet dann:
  • AnzahlKarten um eins reduzieren.
  • eine Zufallszahl von [0...AnzahlKarten] zu ermitteln.
  • Die Karte wird dann getauscht mit der Karte an Platz AnzahlKarten
  • Die gezogene Karte ist am Platz AnzahlKarten

Damit hast Du jetzt eine vermeintlich einfachere Lösung. Nur eben wirst Du diese:
  • deutlich schwerer verstehen
  • kaum etwas wieder verwenden können
  • der Code ist fehleranfällig. Da die Karte ein int Wert ist: Was ist, wenn Du da plötzlich etwas anderes rein bekommst? -1? 1.267? Das sind ja keine gültigen Kartencodes. Es fehlt halt jede Typsicherheit.
  • ....

Daher auch von meiner Seite: Wenn Du etwas programmieren willst: erstelle vernünftige Klassen!
 

temi

Top Contributor
Was genau müsste ich jetzt davon implementieren, damit es funktioniert?
Alles, was da steht.

Das sieht nur so komplex aus, wenn man sich alles auf einmal ansieht. Nimm dir Klasse für Klasse einzeln vor und analysiere den Code. Jede für sich genommen ist recht einfach. Tatsächlich könnte z. B. die Klasse Card fast vollständig von einer IDE automatisch generiert werden, weil es Standardmethoden sind.

Die Mühe lohnt sich auf jeden Fall für dich, denn sie wird dir vermutlich auch einige Aha-Erlebnisse bescheren.

Falls du konkrete Fragen dazu hast, dann stelle sie hier gerne.
 
Zuletzt bearbeitet:

mihe7

Top Contributor
Puh, das ist aber irgendwie sehr komplex, dachte, dass geht mit weniger Zeilen Code.
Ein Kartendeck, das aus Spielkarten besteht, ist doch nicht komplex, sondern beschreibt auf m. E. relativ natürliche Weise den tatsächlichen Sachverhalt. Hier verweise ich mal auf die Antwort von @temi.

Die Anzahl der Zeilen ist nicht wirklich ausschlaggebend. Beispielsweise besteht Card zu 99 % aus Boilerplate-Code, der von der IDE erzeugt werden kann, wie @temi richtig angemerkt hat. Wenn Du Java ab Version 16 verwendest, kannst den Spaß zu einem Einzeiler reduzieren:
Java:
public record Card(Suite suite, Rank rank) {}

Dann reden wir von nicht einmal 10 Zeilen Code für Suite, Rank und Card, um Spielkarten sauber darzustellen.

Das Deck implementiert ein klein wenig Logik (mischen und Karte ausgeben, wobei mir gerade auffällt, dass es vielleicht besser Karte ziehen (drawOneCard) heißen sollte). Das ist die Logik, die Du sowieso in irgendeiner Form haben musst.

Wenn Du das mit der von @KonradN skizzierten Indexlösung vergleichst, ist das hier sogar sehr einfach: statt einem Array von Indizes hast Du einfach eine Liste von Karten und brauchst ansonsten nichts zu verwalten. Nichs umrechnen, keine Größen verwalten etc. Wird alles über die Typen erschlagen. Sogar das Mischen übernimmt eine Methode aus der Standardbibliothek -> eine Zeile Code, man muss sich um nichts kümmern.

Die OO-Lösung ist zudem flexibel, was Erweiterungen betrifft. Deck stellt ein einziges Kartendeck dar. Im Casino wird Blackjack z. B. mit sechs oder acht Decks gespielt. Das lässt sich mit der Klasse Deck relativ einfach in verschiedenen Varianten umsetzen, ohne dass Du am bestehenden Code großartig was verändern müsstest. Zum Beispiel kannst Du einfach einen Kartenschlitten implementieren, der eine beliebige Anzahl an Decks zusammenfasst und für jede Karte zufällig ein nicht-leeres Deck auswählt, aus dem die nächste Karte gezogen wird. Oder aber Du erstellst eine Factory, die ein großes Deck erzeugt. Das ist hier alles mit ein paar Zeilen Code machbar.
 

Ähnliche Java Themen

Neue Themen


Oben