玩Yahtzee游戏


18

在Yahtzee游戏中,玩家轮流滚动5个6面骰子,每回合最多3次,有可能在掷骰子之间节省骰子,然后选择他们希望用于掷骰子的类别。这将继续进行,直到没有其他类别(在13圈之后发生)。然后,计算出玩家的分数,得分最高的玩家获胜。

类别如下(“骰子总和”是指将指定骰子上的点数相加):

  • 上段
    • 王牌:骰子总和显示1点
    • 二进制补码:骰子的总和示出2点
    • Threes:骰子总和显示3点
    • Fours:骰子总和显示4点
    • 法夫:骰子总和显示5点
    • Sixes:骰子总和显示6点
  • 下段
    • 三种:3个相同价值的骰子,总和是所有骰子的总和
    • 同一样四个:4个相同值的骰子,总和是所有骰子的总和
    • 客满:3个骰子带一个值,另外2个带另一个值,得分为25
    • 小直:4个连续骰子,得分为30
    • 大直:5个连续骰子,得分为40
    • Yahtzee:所有5个相同价值的骰子,得分为50
    • 机会:骰子的任何组合,分数是所有骰子的总和

关于类别选择,有一些规则:

  • 如果玩家选择与自己的掷骰不符的类别,则该类别的得分为0。
  • 如果玩家在上半部分至少获得63分,他们将获得35点奖励积分。
  • 如果玩家掷出Yahtzee,但Yahtzee类别已经被占用(另一个Yahtzee-错过0则不计入),他们将获得100分的奖励。此奖金是在第一个Yahtzee之后颁发的。
    • 此外,玩家还必须选择填写类别。他们必须选择与他们的掷骰相对应的上半部分类别(例如,在Sixes类别中必须放置5 6的掷骰)。如果已经使用了对应的上节类别,则Yahtzee可以用于下节类别(在这种情况下,选择“满屋”,“小平直”或“大平直”将奖励正常数量的积分,而不是0分)。如果采用了所有下半部分类别,则Yahtzee可以应用于得分为0的未使用的上半部分类别。

挑战

在此挑战中,竞争对手将参加Yahtzee的1000场比赛。每场比赛结束时,得分最高的参赛作品将获得1分。所有游戏结束后,得分最高的作品将获胜。如果出现平局,将仅使用并列提交项来玩其他游戏,直到并列打破。

控制者

完整的控制器代码可在此GitHub存储库中找到。以下是玩家将与之互动的公共接口:

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

}

此外,还有一些实用程序方法 Util.java。它们主要用于简化控制器代码,但如果需要,可以供玩家使用。

规则

  • 不允许玩家以任何方式进行互动,除非使用 Scorecard.getScores方法查看所有玩家的当前得分。这包括通过操纵系统的不属于公共接口的部分来与其他玩家勾结或破坏其他玩家。
  • 如果球员采取非法举动,将不允许他们参加比赛。任何引起非法举动的问题必须在比赛开始前解决。
  • 如果在锦标赛运行后进行了其他提交,则将使用新提交的内容进行新的锦标赛,并且获胜的提交将进行相应的更新。但是,我不能保证运行新锦标赛的及时性。
  • 提交内容可能不会利用控制器代码中的任何错误,这些错误会导致其偏离实际的游戏规则。向我指出错误(在注释中和/或在GitHub问题中),我将对其进行修复。
  • 禁止使用Java的反射工具。
  • 只要您提供将其与Java接口所需的任何其他代码,就可以使用在JVM上运行或可以编译为Java或JVM字节码(例如Scala或Jython)的任何语言。

最后评论

如果您希望我将任何实用程序方法添加到控制器中,只需在注释中提出问题和/或在GitHub上发布问题,然后添加它,前提是它不允许违反规则或将信息公开给哪些玩家不具有特权。如果您想自己编写它并在GitHub上创建拉取请求,那就更好了!


ACES?你的意思是ONES?这些是骰子,而不是纸牌。
mbomb007 '16


我不记得在演奏时看到它叫它了,但是可以。
mbomb007 '16

是否有一种方法可以在给定骰子的情况下获得给定类别的分数?
mbomb007 '16

@ mbomb007不,但我当然可以做一个:)
Mego

Answers:


4

虚拟人

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

}

该播放器在这里用作如何使用Yahtzee控制器中存在的工具的基本概述。它会尽可能选择Yahtzee,否则会随机选择,同时遵守严格的小丑规则。


1

ace和八

好吧,这要比我想要的时间长得多,这要归功于我最近很忙。

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

该机器人在骰子中寻找可能与某些类别匹配并保存必要类别的模式。如果找到一个,它会立即选择一个高优先级的匹配类别。否则,它将选择得分最高的类别。平均每场得分近200分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.