Yahtzee의 게임을


17

Yahtzee 게임에서 플레이어는 턴당 5 번의 6 면체 주사위를 3 번까지 굴려서 롤간에 주사위를 구한 다음 롤에 사용할 카테고리를 선택합니다. 이것은 더 이상 범주가 없을 때까지 계속됩니다 (13 턴 후에 발생). 그런 다음 플레이어의 점수가 계산되고 가장 높은 점수를 얻은 플레이어가 승리합니다.

범주는 다음과 같습니다 ( "주사위의 합"은 지정된 주사위에 핍 수를 더한 것을 의미합니다).

  • 상단 섹션
    • 에이스 : 1 핍을 나타내는 주사위의 합
    • : 주사위 2 개를 보여주는 주사위의 합
    • Threes : 3 핍을 보여주는 주사위의 합
    • Fours : 4 핍을 나타내는 주사위의 합
    • 파이브 : 5 핍을 보여주는 주사위의 합
    • Sixes : 6 핍을 보여주는 주사위의 합
  • 하부
    • 3 가지 종류 : 같은 값을 가진 3 개의 주사위, 점수는 모든 주사위의 합입니다
    • 4 가지 종류 : 같은 값을 가진 4 개의 주사위, 점수는 모든 주사위의 합입니다
    • 풀 하우스 : 한 값으로 3 주사위, 다른 값으로 2 주사위, 점수는 25
    • 스몰 스트레이트 : 4 개의 연속 주사위, 점수는 30
    • 대형 스트레이트 : 5 개의 연속 주사위, 점수는 40
    • Yahtzee : 같은 값을 가진 5 개의 주사위, 점수는 50
    • 기회 : 주사위의 조합, 점수는 모든 주사위의 합입니다

카테고리 선택에 대한 몇 가지 규칙이 있습니다.

  • 플레이어가 자신의 롤과 일치하지 않는 범주를 선택하면 해당 범주에 대해 0 점을받습니다.
  • 플레이어가 상위 섹션에서 63 점 이상을 획득하면 35 보너스 포인트를받습니다.
  • 플레이어가 Yahtzee를 굴 렸지만 Yahtzee 범주가 이미 다른 Yahtzee에 의해 이미 취해진 경우 (미스에 대해 0을 채우는 것은 포함되지 않음) 100 점을 보너스로받습니다. 이 보너스는 모든 Yahtzee에 대해 첫 보너스 이후에 수여됩니다.
    • 또한 플레이어는 여전히 범주를 채우도록 선택해야합니다. 그들은 자신의 롤에 해당하는 상위 섹션 카테고리를 선택해야합니다 (예 : 5 6의 롤이 Sixes 카테고리에 배치되어야 함). 해당 상위 섹션 범주가 이미 사용 된 경우 Yahtzee는 하위 섹션 범주에 사용될 수 있습니다 (이 경우 Full House, Small Straight 또는 Large Straight를 선택하면 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


나는 그것을 연주했을 때 그것을 불렀다는 것을 기억하지 않지만 괜찮습니다.
mbomb007

주사위 세트가 주어지면 주어진 범주에 대한 점수를 얻는 방법이 있습니까?
mbomb007

@ mbomb007 아니오, 그러나 나는 확실히 하나를 만들 수 있습니다 :)
Mego

답변:


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

에이스와 에이트

글쎄, 이것은 내가 최근에 얼마나 바빴는지 덕분에 내가 원했던 것보다 훨씬 오래 걸렸습니다.

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 점을 얻습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.