Mainkan game Yahtzee

17

Dalam permainan Yahtzee, pemain bergiliran memutar 5 dadu 6 sisi hingga tiga kali per putaran, mungkin menyelamatkan dadu di antara gulungan, dan kemudian memilih kategori yang ingin mereka gunakan untuk gulungan mereka. Ini berlanjut sampai tidak ada lagi kategori (yang terjadi setelah 13 putaran). Kemudian, skor pemain dihitung, dan pemain dengan skor tertinggi menang.

Kategori adalah sebagai berikut ("jumlah dadu" berarti menambahkan jumlah pips pada dadu yang ditentukan):

  • Bagian Atas
    • Aces : jumlah dadu yang menunjukkan 1 pip
    • Dua : jumlah dadu menunjukkan 2 pips
    • Tiga : jumlah dadu menunjukkan 3 pips
    • Empat : jumlah dadu yang menunjukkan 4 pips
    • Balita : jumlah dadu yang menunjukkan 5 pips
    • Sixes : jumlah dadu menunjukkan 6 pips
  • Bagian bawah
    • Three of a Kind : 3 dadu dengan nilai yang sama, skor adalah jumlah dari semua dadu
    • Four of a Kind : 4 dadu dengan nilai yang sama, skor adalah jumlah semua dadu
    • Full House : 3 dadu dengan satu nilai dan 2 dengan lainnya, skor adalah 25
    • Small Straight : 4 dadu berurutan, skor 30
    • Large Straight : 5 dadu berurutan, skornya adalah 40
    • Yahtzee : semua 5 dadu dengan nilai yang sama, skor adalah 50
    • Peluang : setiap kombinasi dadu, skor adalah jumlah dari semua dadu

Ada beberapa aturan tentang pilihan kategori:

  • Jika seorang pemain memilih kategori yang tidak cocok dengan gulungan mereka, mereka menerima skor 0 untuk kategori itu.
  • Jika seorang pemain mendapatkan skor setidaknya 63 di bagian atas, mereka menerima 35 poin bonus.
  • Jika seorang pemain telah meluncurkan Yahtzee tetapi kategori Yahtzee sudah diambil (oleh Yahtzee lain - mengisi 0 untuk kehilangan tidak masuk hitungan), mereka menerima bonus 100 poin. Bonus ini diberikan untuk setiap Yahtzee setelah yang pertama.
    • Selain itu, pemain masih harus memilih untuk mengisi kategori. Mereka harus memilih kategori bagian atas sesuai dengan gulungan mereka (misalnya gulungan 5 6 harus ditempatkan dalam kategori Sixes). Jika kategori bagian atas yang sesuai telah digunakan, Yahtzee dapat digunakan untuk kategori bagian bawah (dalam hal ini, memilih Full House, Small Straight, atau Straight Straight memberi jumlah poin yang normal daripada 0). Jika semua kategori bagian bawah diambil, maka Yahtzee dapat diterapkan ke kategori bagian atas yang tidak digunakan, dengan skor 0.

Tantangan

Dalam tantangan ini, para pesaing akan memainkan 1000 game Yahtzee. Di akhir setiap pertandingan, pengajuan yang mencetak skor tertinggi akan menerima 1 poin. Setelah semua permainan selesai, pengajuan dengan poin terbanyak akan menang. Jika ada seri, tambahan game akan dimainkan hanya dengan pengiriman terikat sampai seri rusak.

Pengendali

Kode pengontrol lengkap dapat ditemukan di repositori GitHub ini . Berikut adalah antarmuka publik yang akan berinteraksi dengan para pemain:

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);

}

Selain itu, ada beberapa metode utilitas di Util.java. Mereka terutama ada di sana untuk menyederhanakan kode pengontrol, tetapi mereka dapat digunakan oleh pemain jika mereka inginkan.

Aturan

  • Pemain tidak diizinkan berinteraksi dengan cara apa pun kecuali menggunakan Scorecard.getScoresmetode ini untuk melihat skor semua pemain saat ini. Ini termasuk berkolusi dengan pemain lain atau menyabotase pemain lain melalui memanipulasi bagian sistem yang bukan bagian dari antarmuka publik.
  • Jika seorang pemain melakukan tindakan ilegal, mereka tidak akan diizinkan untuk bersaing di turnamen. Setiap masalah yang menyebabkan perpindahan ilegal harus diselesaikan sebelum turnamen berjalan.
  • Jika kiriman tambahan dilakukan setelah turnamen dijalankan, turnamen baru akan dijalankan dengan kiriman baru, dan kiriman yang menang akan diperbarui. Saya tidak menjamin ketepatan dalam menjalankan turnamen baru, namun.
  • Kiriman tidak dapat mengeksploitasi bug dalam kode pengontrol yang menyebabkannya menyimpang dari aturan gim yang sebenarnya. Tunjukkan bug kepada saya (dalam komentar dan / atau dalam masalah GitHub), dan saya akan memperbaikinya.
  • Dilarang menggunakan alat refleksi Jawa.
  • Bahasa apa pun yang berjalan pada JVM, atau dapat dikompilasi ke Java atau JVM bytecode (seperti Scala atau Jython) dapat digunakan, selama Anda menyediakan kode tambahan yang diperlukan untuk menghubungkannya dengan Java.

Komentar Terakhir

Jika ada metode utilitas yang Anda ingin saya tambahkan ke controller, cukup tanyakan di komentar dan / atau buat masalah di GitHub, dan saya akan menambahkannya, dengan asumsi itu tidak memungkinkan untuk melanggar aturan atau mengekspos informasi ke pemain mana yang tidak mengetahui rahasia. Jika Anda ingin menulisnya sendiri dan membuat permintaan tarik di GitHub, lebih baik lagi!

Mego
sumber
ACES? Maksudmu ONES? Ini adalah dadu, bukan kartu.
mbomb007
Saya tidak ingat melihatnya disebut saat saya memainkannya, tapi oke.
mbomb007
Apakah ada metode untuk mendapatkan skor untuk kategori yang diberikan diberikan satu set dadu?
mbomb007
@ mbomb007 Tidak, tapi saya pasti bisa membuatnya :)
Mego

Jawaban:

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);
    }

}

Pemain ini ada di sini untuk berfungsi sebagai garis besar dasar tentang cara menggunakan alat yang ada di pengontrol Yahtzee. Ia memilih Yahtzee kapan saja memungkinkan, dan membuat pilihan acak sebaliknya, sambil mematuhi aturan pelawak yang ketat.

Mego
sumber
1

Aces dan Delapan

Yah, ini butuh waktu lebih lama daripada yang saya inginkan berkat betapa sibuknya saya akhir-akhir ini.

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;
    }
}

Bot mencari pola di dadu yang bisa cocok dengan kategori tertentu dan menampung yang diperlukan. Segera memilih kategori pencocokan prioritas tinggi jika ditemukan; jika tidak, ia memilih kategori yang menghasilkan skor tertinggi. Mencetak rata-rata hampir 200 poin per game.

TNT
sumber