Chơi một trò chơi của Yahtzee


17

Trong trò chơi Yahtzee, người chơi thay phiên nhau lăn 5 viên xúc xắc 6 mặt tối đa ba lần mỗi lượt, có thể tiết kiệm xúc xắc giữa các cuộn, sau đó chọn một danh mục họ muốn sử dụng cho cuộn của mình. Điều này tiếp tục cho đến khi không còn danh mục nữa (xảy ra sau 13 lượt). Sau đó, điểm của người chơi được tính và người chơi có số điểm cao nhất sẽ thắng.

Các danh mục như sau ("tổng xúc xắc" có nghĩa là cộng số pips trên súc sắc đã chỉ định):

  • Phần trên
    • Aces : tổng số xúc xắc hiển thị 1 pip
    • Twos : tổng số xúc xắc hiển thị 2 pips
    • Threes : tổng số xúc xắc hiển thị 3 pips
    • Fours : tổng số xúc xắc hiển thị 4 pips
    • Fives : tổng số xúc xắc hiển thị 5 pips
    • Sixes : tổng của xúc xắc hiển thị 6 pips
  • Phần dưới
    • Three of a Kind : 3 con xúc xắc có cùng giá trị, điểm là tổng của tất cả con xúc xắc
    • Four of a Kind : 4 con xúc xắc có cùng giá trị, điểm là tổng của tất cả con xúc xắc
    • Full House : 3 con xúc xắc với một giá trị và 2 con khác, điểm số là 25
    • Nhỏ thẳng : 4 con xúc xắc liên tiếp, điểm là 30
    • Large Straight : 5 con xúc xắc liên tiếp, điểm số là 40
    • Yahtzee : tất cả 5 con xúc xắc có cùng giá trị, điểm là 50
    • Cơ hội : bất kỳ sự kết hợp của xúc xắc, điểm số là tổng của tất cả các con xúc xắc

Có một vài quy tắc về các lựa chọn danh mục:

  • Nếu người chơi chọn một danh mục không phù hợp với danh sách của họ, họ sẽ nhận được điểm 0 cho danh mục đó.
  • Nếu người chơi kiếm được ít nhất 63 điểm ở phần trên, họ sẽ nhận được 35 điểm thưởng.
  • Nếu người chơi đã cán Yahtzee nhưng danh mục Yahtzee đã bị lấy (bởi một Yahtzee khác - điền 0 cho một lần bỏ lỡ không được tính), họ sẽ nhận được 100 điểm thưởng. Phần thưởng này được trao cho mỗi Yahtzee sau lần đầu tiên.
    • Ngoài ra, người chơi vẫn phải chọn điền vào một danh mục. Họ phải chọn danh mục phần trên tương ứng với cuộn của họ (ví dụ: cuộn 5 6 phải được đặt trong danh mục Sixes). Nếu danh mục phần trên tương ứng đã được sử dụng, Yahtzee có thể được sử dụng cho danh mục phần thấp hơn (trong trường hợp này, chọn Full House, Small Straight hoặc Large Straight sẽ thưởng số lượng điểm bình thường thay vì 0). Nếu tất cả các loại phần dưới được lấy, thì Yahtzee có thể được áp dụng cho danh mục phần trên không được sử dụng, với số điểm là 0.

Các thách thức

Trong thử thách này, các đối thủ sẽ chơi 1000 trận Yahtzee. Vào cuối mỗi trò chơi, (các) bài nộp đạt điểm cao nhất sẽ nhận được 1 điểm. Sau khi tất cả các trò chơi kết thúc, bài nộp có nhiều điểm nhất sẽ giành chiến thắng. Nếu có hòa, các trò chơi bổ sung sẽ được chơi chỉ với các lần gửi bị ràng buộc cho đến khi hòa.

Bộ điều khiển

Mã điều khiển hoàn chỉnh có thể được tìm thấy trên kho GitHub này . Dưới đây là các giao diện công cộng mà người chơi sẽ tương tác:

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

}

Ngoài ra, có một số phương pháp tiện ích trong Util.java. Chúng chủ yếu ở đó để đơn giản hóa mã điều khiển, nhưng chúng có thể được người chơi sử dụng nếu muốn.

Quy tắc

  • Người chơi không được phép tương tác dưới bất kỳ hình thức nào ngoại trừ sử dụng Scorecard.getScoresphương pháp để xem điểm số hiện tại của tất cả người chơi. Điều này bao gồm thông đồng với những người chơi khác hoặc phá hoại những người chơi khác thông qua việc thao túng các phần của hệ thống không phải là một phần của giao diện công cộng.
  • Nếu một người chơi thực hiện một động thái bất hợp pháp, họ sẽ không được phép cạnh tranh trong giải đấu. Bất kỳ vấn đề gây ra di chuyển bất hợp pháp phải được giải quyết trước khi chạy giải đấu.
  • Nếu các bài nộp bổ sung được thực hiện sau khi giải đấu được tổ chức, một giải đấu mới sẽ được chạy với (các) bài dự thi mới và bài dự thi chiến thắng sẽ được cập nhật tương ứng. Tuy nhiên, tôi không đảm bảo về sự nhanh chóng trong việc điều hành giải đấu mới.
  • Đệ trình có thể không khai thác bất kỳ lỗi nào trong mã điều khiển khiến nó đi chệch khỏi quy tắc trò chơi thực tế. Chỉ ra lỗi cho tôi (trong một bình luận và / hoặc trong vấn đề GitHub), và tôi sẽ sửa chúng.
  • Việc sử dụng các công cụ phản chiếu của Java bị cấm.
  • Bất kỳ ngôn ngữ nào chạy trên JVM hoặc có thể được biên dịch sang mã byte Java hoặc JVM (như Scala hoặc Jython) đều có thể được sử dụng, miễn là bạn cung cấp bất kỳ mã bổ sung nào cần thiết để giao tiếp với Java.

Nhận xét cuối cùng

Nếu có bất kỳ phương thức tiện ích nào bạn muốn tôi thêm vào bộ điều khiển, chỉ cần hỏi ý kiến ​​và / hoặc đưa ra vấn đề trên GitHub, và tôi sẽ thêm nó, giả sử nó không cho phép phá vỡ quy tắc hoặc tiết lộ thông tin cho mà người chơi không bí mật. Nếu bạn muốn tự viết nó và tạo một yêu cầu kéo trên GitHub, thậm chí tốt hơn!


ACES? Ý bạn là ONESsao? Đây là xúc xắc, không phải thẻ.
mbomb007


Tôi không nhớ là đã thấy nó khi tôi chơi nó, nhưng không sao.
mbomb007

Có một phương pháp để có được điểm số cho một thể loại nhất định cho một bộ súc sắc?
mbomb007

@ mbomb007 Không, nhưng tôi chắc chắn có thể tạo một cái :)
Mego

Câu trả lời:


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

}

Trình phát này ở đây để phục vụ như một phác thảo cơ bản về cách sử dụng các công cụ có trong bộ điều khiển Yahtzee. Nó chọn Yahtzee bất cứ khi nào có thể, và đưa ra các lựa chọn ngẫu nhiên khác, trong khi tuân thủ các quy tắc joker nghiêm ngặt.


1

Ách và đêm

Chà, điều này mất nhiều thời gian hơn tôi muốn nhờ vào sự bận rộn của tôi gần đây.

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 tìm kiếm các mẫu trong súc sắc có thể phù hợp với các loại nhất định và giữ các mẫu cần thiết. Nó ngay lập tức chọn một danh mục phù hợp ưu tiên cao nếu tìm thấy; mặt khác, nó chọn một thể loại mang lại số điểm cao nhất. Điểm trung bình gần 200 điểm cho mỗi trò chơi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.