Jetzt Yahtzee spielen

17

Im Spiel Yahtzee würfeln die Spieler bis zu dreimal pro Spielzug abwechselnd mit 5 6-seitigen Würfeln, wobei sie möglicherweise zwischen den Würfeln Würfel sparen und dann eine Kategorie auswählen, die sie für ihren Wurf verwenden möchten. Dies geht so lange weiter, bis es keine Kategorien mehr gibt (was nach 13 Runden geschieht). Dann werden die Punktzahlen der Spieler gezählt und der Spieler mit der höchsten Punktzahl gewinnt.

Die Kategorien sind wie folgt:

  • Oberer Abschnitt
    • Asse : Summe der Würfel mit 1 Pip
    • Zweien : Summe der Würfel mit 2 Pips
    • Dreier : Summe der Würfel mit 3 Pips
    • Vierer : Summe der Würfel mit 4 Pips
    • Fives : Summe der Würfel mit 5 Pips
    • Sechser : Summe der Würfel mit 6 Pips
  • Unterer Abschnitt
    • Drei Gleiche: 3 Würfel mit gleichem Wert, Punktzahl ist die Summe aller Würfel
    • Vierling : 4 gleichwertige Würfel, Punktzahl ist die Summe aller Würfel
    • Full House : 3 Würfel mit einem Wert und 2 mit einem anderen, die Punktzahl beträgt 25
    • Kleine Straße : 4 aufeinanderfolgende Würfel, 30 Punkte
    • Large Straight : 5 aufeinanderfolgende Würfel, 40 Punkte
    • Yahtzee : alle 5 Würfel mit gleichem Wert, Punktzahl ist 50
    • Zufall : Jede Kombination von Würfeln, Punktzahl ist die Summe aller Würfel

Es gibt einige Regeln für die Kategoriewahl:

  • Wenn ein Spieler eine Kategorie auswählt, die nicht zu seinem Wurf passt, erhält er für diese Kategorie eine Punktzahl von 0.
  • Wenn ein Spieler im oberen Bereich eine Punktzahl von mindestens 63 erreicht, erhält er 35 Bonuspunkte.
  • Wenn ein Spieler einen Yahtzee gewürfelt hat, aber die Yahtzee-Kategorie bereits vergeben ist (von einem anderen Yahtzee - das Ausfüllen von 0 für einen Fehlschlag zählt nicht), erhält er einen Bonus von 100 Punkten. Dieser Bonus wird für jeden Yahtzee nach dem ersten vergeben.
    • Darüber hinaus muss der Spieler weiterhin auswählen, eine Kategorie auszufüllen. Sie müssen die Kategorie des oberen Abschnitts wählen, die ihrem Wurf entspricht. Wenn die entsprechende Kategorie des oberen Abschnitts bereits verwendet wurde, kann der Yahtzee für eine Kategorie des unteren Abschnitts verwendet werden (in diesem Fall wird bei Auswahl von Full House, Small Straight oder Large Straight die normale Anzahl von Punkten anstelle von 0 vergeben). Wenn alle Unterkategorien belegt sind, kann der Yahtzee auf eine ungenutzte Oberkategorie mit einer Punktzahl von 0 angewendet werden.

Die Herausforderung

Bei dieser Herausforderung werden die Konkurrenten 1000 Spiele von Yahtzee spielen. Am Ende jedes Spiels erhalten die Einreichungen, die den höchsten Punktestand erzielt haben, 1 Punkt. Nachdem alle Spiele beendet sind, gewinnt die Einsendung mit den meisten Punkten. Wenn es ein Unentschieden gibt, werden zusätzliche Spiele nur mit den gebundenen Einsendungen gespielt, bis das Unentschieden gebrochen ist.

Regler

Der vollständige Controller-Code befindet sich in diesem GitHub-Repository . Hier sind die öffentlichen Schnittstellen, mit denen die Spieler interagieren werden:

public interface ScorecardInterface {

    // returns an array of unused categories
    Category[] getFreeCategories();

    // returns the current total score
    int getScore();

    // returns the current Yahtzee bonus
    int getYahtzeeBonus();

    // returns the current Upper Section bonus
    int getUpperBonus();

    // returns the current Upper Section total
    int getUpperScore();

}
public interface ControllerInterface {

    // returns the player's scorecard (cloned copy, so don't try any funny business)
    ScorecardInterface getScoreCard(Player p);

    // returns the current scores for all players, in no particular order
    // this allows players to compare themselves with the competition,
    //  without allowing them to know exactly who has what score (besides their own score),
    //  which (hopefully) eliminates any avenues for collusion or sabotage
    int[] getScores();

}
public enum Category {
    ACES,
    TWOS,
    THREES,
    FOURS,
    FIVES,
    SIXES,
    THREE_OF_A_KIND,
    FOUR_OF_A_KIND,
    FULL_HOUSE,
    SMALL_STRAIGHT,
    LARGE_STRAIGHT,
    YAHTZEE,
    CHANCE;

    // determines if the category is part of the upper section
    public boolean isUpper() {
        // implementation
    }

    // determines if the category is part of the lower section
    public boolean isLower() {
        // implementation
    }

    // determines if a given set of dice fits for the category
    public boolean matches(int[] dice) {
        // implementation
    }

    // calculates the score of a set of dice for the category
    public int getScore(int[] dice) {
        // implementation
    }

    // returns all categories that fit the given dice
    public static Category[] getMatchingCategories(int[] dice) {
        // implementation
    }
}
public class TurnChoice {

    // save the dice with the specified indexes (0-4 inclusive)
    public TurnChoice(int[] diceIndexes) {
        // implementation
    }

    // use the current dice for specified category
    public TurnChoice(Category categoryChosen) {
        // implementation
    }

}

public abstract class Player {

    protected ControllerInterface game;

    public Player(ControllerInterface game) {
        this.game = game;
    }

    public String getName() {
        return this.getClass().getSimpleName();
    }

    // to be implemented by players
    // dice is the current roll (an array of 5 integers in 1-6 inclusive)
    // stage is the current roll stage in the turn (0-2 inclusive)
    public abstract TurnChoice turn(int[] dice, int stage);

}

Darüber hinaus gibt es einige Dienstprogrammmethoden in Util.java. Sie dienen hauptsächlich der Vereinfachung des Controller-Codes, können jedoch von Spielern verwendet werden, wenn sie dies wünschen.

Regeln

  • Spieler dürfen in keiner Weise interagieren, außer mit der Scorecard.getScoresMethode, um die aktuellen Punktzahlen aller Spieler zu sehen. Dies beinhaltet das Absprechen mit anderen Spielern oder das Sabotieren anderer Spieler durch Manipulieren von Teilen des Systems, die nicht Teil der öffentlichen Schnittstelle sind.
  • Wenn ein Spieler einen illegalen Zug macht, darf er nicht am Turnier teilnehmen. Alle Probleme, die zu illegalen Zügen führen, müssen vor Beginn des Turniers behoben werden.
  • Wenn nach der Durchführung des Turniers weitere Einreichungen erfolgen, wird ein neues Turnier mit den neuen Einreichungen durchgeführt, und die Gewinner-Einreichung wird entsprechend aktualisiert. Ich übernehme jedoch keine Garantie dafür, dass das neue Turnier pünktlich durchgeführt wird.
  • Einsendungen dürfen keine Fehler im Controller-Code ausnutzen, die dazu führen, dass dieser von den tatsächlichen Spielregeln abweicht. Weisen Sie mich auf Fehler hin (in einem Kommentar und / oder in einem GitHub-Problem), und ich werde sie beheben.
  • Die Verwendung von Java Reflection Tools ist verboten.
  • Jede Sprache, die auf der JVM ausgeführt wird oder in Java oder JVM-Bytecode kompiliert werden kann (z. B. Scala oder Jython), kann verwendet werden, sofern Sie zusätzlichen Code für die Schnittstelle mit Java angeben.

Letzte Kommentare

Wenn es eine Dienstprogrammmethode gibt, die ich dem Controller hinzufügen soll, fragen Sie einfach in den Kommentaren nach und / oder machen Sie ein Problem mit GitHub. Ich füge sie hinzu, vorausgesetzt, sie lässt keine Regelverletzungen zu und macht keine Informationen verfügbar welche Spieler sind nicht eingeweiht. Wenn Sie es selbst schreiben und eine Pull-Anfrage auf GitHub erstellen möchten, noch besser!

Mego
quelle
ACES? Du meinst ONES? Dies sind Würfel, keine Karten.
mbomb007
Ich kann mich nicht erinnern, gesehen zu haben, dass es so heißt, als ich es gespielt habe, aber okay.
mbomb007
Gibt es eine Methode, um die Punktzahl für eine bestimmte Kategorie mit einem Satz Würfel zu erhalten?
mbomb007
@ mbomb007 Nein, aber ich kann bestimmt eins machen :)
Mego

Antworten:

4

DummyPlayer

package mego.yahtzee;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class DummyPlayer extends Player {

    public DummyPlayer(ControllerInterface game) {
        super(game);
    }

    @Override
    public TurnChoice turn(int[] dice, int stage) {
        Category[] choices = game.getScoreCard(this).getFreeCategories();
        Category choice = choices[new Random().nextInt(choices.length)];
        if(IntStream.of(dice).allMatch(die -> die == dice[0])) {
            if(Stream.of(choices).filter(c -> c == Category.YAHTZEE).count() > 0) {
                choice = Category.YAHTZEE;
            } else if(Stream.of(choices).filter(c -> c == Util.intToUpperCategory(dice[0])).count() > 0) {
                choice = Util.intToUpperCategory(dice[0]);
            } else {
                choices = Stream.of(game.getScoreCard(this).getFreeCategories()).filter(c -> c.isLower()).toArray(Category[]::new);
                if(choices.length > 0) {
                    choice = choices[new Random().nextInt(choices.length)];
                } else {
                    choices = game.getScoreCard(this).getFreeCategories();
                    choice = choices[new Random().nextInt(choices.length)];
                }
            }
        }
        return new TurnChoice(choice);
    }

}

Dieser Player soll als grundlegende Übersicht für die Verwendung der im Yahtzee-Controller vorhandenen Tools dienen. Es wählt, wann immer möglich, Yahtzee und trifft ansonsten zufällige Entscheidungen, wobei die strengen Joker-Regeln eingehalten werden.

Mego
quelle
1

Asse und Achter

Nun, das hat viel länger gedauert, als ich es mir gewünscht hätte, da ich in letzter Zeit sehr beschäftigt war.

package mego.yahtzee;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static mego.yahtzee.Category.*;

public class AcesAndEights extends Player {
    private Category[] freeCategories, matchingCategories, usableCategories;

    public AcesAndEights(ControllerInterface game) {
        super(game);
    }

    @Override
    public TurnChoice turn(int[] dice, int stage) {
        List<Integer> holdIndices = new java.util.ArrayList<>();

        freeCategories = game.getScoreCard(this).getFreeCategories();

        matchingCategories = Category.getMatchingCategories(dice);
        Arrays.sort(matchingCategories);

        usableCategories = Arrays.stream(freeCategories)
                                 .filter(this::isMatchingCategory)
                                 .toArray(Category[]::new);
        Arrays.sort(usableCategories);

        if (isMatchingCategory(YAHTZEE))
            return doYahtzeeProcess(dice);

        if (isUsableCategory(FULL_HOUSE))
            return new TurnChoice(FULL_HOUSE);

        if (stage == 0 || stage == 1) {
            if (isMatchingCategory(THREE_OF_A_KIND)) {
                int num = 0;
                for (int i : dice) {
                    if (Util.count(Util.boxIntArray(dice), i) >= 3) {
                        num = i;
                        break;
                    }
                }
                for (int k = 0; k < 5; k++) {
                    if (dice[k] == num)
                        holdIndices.add(k);
                }
                return new TurnChoice(toIntArray(holdIndices.toArray(new Integer[0])));
            }

            if (isFreeCategory(LARGE_STRAIGHT) || isFreeCategory(SMALL_STRAIGHT)) {
                if (isUsableCategory(LARGE_STRAIGHT))
                    return new TurnChoice(LARGE_STRAIGHT);

                if (isMatchingCategory(SMALL_STRAIGHT)) {
                    if (!isFreeCategory(LARGE_STRAIGHT))
                        return new TurnChoice(SMALL_STRAIGHT);

                    int[] arr = Arrays.stream(Arrays.copyOf(dice, 5))
                                      .distinct()
                                      .sorted()
                                      .toArray();
                    List<Integer> l = Arrays.asList(Util.boxIntArray(dice));
                    if (Arrays.binarySearch(arr, 1) >= 0 && Arrays.binarySearch(arr, 2) >= 0) {
                        holdIndices.add(l.indexOf(1));
                        holdIndices.add(l.indexOf(2));
                        holdIndices.add(l.indexOf(3));
                        holdIndices.add(l.indexOf(4));
                    }
                    else if (Arrays.binarySearch(arr, 2) >= 0 && Arrays.binarySearch(arr, 3) >= 0) {
                        holdIndices.add(l.indexOf(2));
                        holdIndices.add(l.indexOf(3));
                        holdIndices.add(l.indexOf(4));
                        holdIndices.add(l.indexOf(5));
                    }
                    else {
                        holdIndices.add(l.indexOf(3));
                        holdIndices.add(l.indexOf(4));
                        holdIndices.add(l.indexOf(5));
                        holdIndices.add(l.indexOf(6));
                    }
                    return new TurnChoice(toIntArray(holdIndices.toArray(new Integer[0])));
                }
            }

            if (isFreeCategory(FULL_HOUSE)) {
                int o = 0, t = o;
                for (int k = 1; k <= 6; k++) {
                    if (Util.count(Util.boxIntArray(dice), k) == 2) {
                        if (o < 1)
                            o = k;
                        else
                            t = k;
                    }
                }

                if (o > 0 && t > 0) {
                    for (int k = 0; k < 5; k++) {
                        if (dice[k] == o || dice[k] == t)
                            holdIndices.add(k);
                    }
                    return new TurnChoice(toIntArray(holdIndices.toArray(new Integer[0])));
                }
            }
        }
        else {
            Arrays.sort(freeCategories, Comparator.comparingInt((Category c) -> c.getScore(dice))
                                                  .thenComparingInt(this::getPriority)
                                                  .reversed());
            return new TurnChoice(freeCategories[0]);
        }

        return new TurnChoice(new int[0]);
    }

    private TurnChoice doYahtzeeProcess(int[] dice) {
        if (isUsableCategory(YAHTZEE))
            return new TurnChoice(YAHTZEE);

        Category c = Util.intToUpperCategory(dice[0]);
        if (isUsableCategory(c))
            return new TurnChoice(c);

        Category[] arr = Arrays.stream(freeCategories)
                               .filter(x -> x.isLower())
                               .sorted(Comparator.comparing(this::getPriority)
                                                 .reversed())
                               .toArray(Category[]::new);
        if (arr.length > 0)
            return new TurnChoice(arr[0]);

        Arrays.sort(freeCategories, Comparator.comparingInt(this::getPriority));
        return new TurnChoice(freeCategories[0]);
    }

    private boolean isFreeCategory(Category c) {
        return Arrays.binarySearch(freeCategories, c) >= 0;
    }

    private boolean isMatchingCategory(Category c) {
        return Arrays.binarySearch(matchingCategories, c) >= 0;
    }

    private boolean isUsableCategory(Category c) {
        return Arrays.binarySearch(usableCategories, c) >= 0;
    }

    private int getPriority(Category c) {
        switch (c) {
            case YAHTZEE: return -3;        // 50 points
            case LARGE_STRAIGHT: return -1; // 40 points
            case SMALL_STRAIGHT: return -2; // 30 points
            case FULL_HOUSE: return 10;     // 25 points
            case FOUR_OF_A_KIND: return 9;  // sum
            case THREE_OF_A_KIND: return 8; // sum
            case SIXES: return 7;
            case FIVES: return 6;
            case FOURS: return 5;
            case THREES: return 4;
            case TWOS: return 3;
            case ACES: return 2;
            case CHANCE: return 1;          // sum
        }
        throw new RuntimeException();
    }

    private int[] toIntArray(Integer[] arr) {
        int[] a = new int[arr.length];
        for (int k = 0; k < a.length; k++)
            a[k] = arr[k];
        return a;
    }
}

Der Bot sucht in den Würfeln nach Mustern, die bestimmten Kategorien entsprechen könnten, und enthält die erforderlichen. Es wählt sofort eine Übereinstimmungskategorie mit hoher Priorität, wenn eine gefunden wird. Andernfalls wählt es eine Kategorie mit der höchsten Punktzahl. Erzielt durchschnittlich fast 200 Punkte pro Spiel.

TNT
quelle