山丘之王-骗子的骰子


22

说谎者的骰子是一个相当简单的骰子游戏。我看到了一些不同的规则变体,但这是我最熟悉的版本:

  • 每个玩家以5d6开始
  • 除了在回合结束时计算骰子时,每个玩家都可以看到自己的骰子,但看不到任何对手的骰子
  • 在任何给定回合开始时,所有玩家都掷出他们当前拥有的任何骰子
  • 然后,一个玩家(通常是前一轮的获胜者,或者是上次开始的玩家左侧的玩家;我们将前者用于此KotH;随机玩家在第一轮开始)猜测桌子上有多少个特定数字(很野生)
  • 竞标继续向右移动,每次竞标都更高(例如,3个5、3个6和4个2都高于3个4个,但3个3个不是; 4个也更高,但竞标可能会使您处于坏处); 直到任何玩家称他们之前的玩家为骗子
  • 在这一点上,所有玩家都展示自己的骰子,并在桌子上一并计算最后一次竞标的次数
  • 如果总价低于出价,则出价的玩家必须给称他们为说谎者的玩家致死,否则,称出价者为说谎者的玩家必须给出价者致死(这样,出价者获胜。如果他们的数量至少等于他出价的数量,则不必是确切的数量)
  • 当骰子用完时,您会输
  • 最后一名站长获胜

例如:

玩家一有1,1,2,4,6
玩家二有1,2,2,3,5
玩家三有1,3,3,4,6
玩家一:三分之六。
玩家二:四分之二。
玩家三:四个三分。
玩家一:五分之二。
玩家二:六个二。
玩家三:六个三分。
玩家一:六四。
玩家二:骗子!
他们露出自己的骰子,并数出骰子(因为有些是野生的)和四数。
事实证明,实际上恰好有六个四。
因此,第二个玩家给一个玩家一个死亡。
他们重新投掷,第一人开始下一轮比赛。

您必须编写一个机器人才能玩此游戏。它必须实现以下抽象java类:

public abstract class Player {
    public Player() {}
    public String toString() {
        return this.getClass().getSimpleName();
    }
    public abstract String bid(int yourId, int[] diceEachPlayerHas, int[] yourDice, String[] bids);
}
  • 您必须实施出价方法
    • 第一个参数是您的机器人在回合顺序中的当前位置,第二个参数是显示每个玩家(包括您自己)当前拥有多少个骰子的数组,第三个是显示当前在您自己的骰子上显示的值的数组,第四个是自当前回合开始以来所有出价的数组-如果您是第一轮出价,则长度为0
    • 输出应该是“数字面”形式的字符串,或者是“骗子!”字符串。称前竞标者为骗子。
    • 如果您的输出格式不正确,您将被淘汰。
  • 您可以重写toString方法,但不是必需的。但是,您不得以任何妨碍控制器输出可读性的方式对其进行编辑。
  • 您可以调用控制器的任何其他公共方法,但不能调用其主要方法。
  • 您只能读取和编辑运行目录中以您自己的机器人名称开头的文件
  • 您不得从任何其他来源获取输入
  • 实例变量在每个新游戏开始时都会重置,但静态变量不会重置。

计分

  • 每次添加一个机器人(一旦提交了三个或更多机器人),就会模拟一组1,000个游戏,每个游戏中有3-5个玩家,其得分如控制器来源所示(在任何给定游戏中,在每个回合开始时获得1个,每一次赢得一次死亡获得10个,如果获胜则获得1,000个奖金);每场比赛限制5,000个回合(非回合)。
  • 您的机器人将根据最新游戏中的得分进行评分;如果是非负数,则加上其投票分数的十倍。(后者不太可能对分数产生重大影响)

控制器源可在此处找到

截至2015-06-19得分:

Badnomial: 434,924 + 6x10 = 424,984
Nobody: 282,329 + 6x10 = 282,389
StraightShooter: 265,205 + 5x10 = 265,255
MostlyHonestAbe: 158,958 + 4x10 = 158,998
The Pirate: 157,005 + 1x10 = 157,015
Statistician: 144,012 + 2x10 = 144,032
Fidelio: 49,973 + 2x10 = 49,993
Absurd Bot: 6,831
DrHouse: 2,638 + 3x10 = 2,668

1
您应该阐明输出应为“ 2 3”,而不是如示例所示的“两个三”。另外,控制器中是否可以观看一场比赛?
该隐2015年

不在正式版本中,但是我将发布一个替代版本,让您做到这一点。
SuperJedi224 2015年

@Geobits:如果您愿意。如果有人打电话给您,它将使您处于不利地位。
SuperJedi224

1
我假设数组的索引是玩家的“ id”,因此diceEachPlayerHas[yourId]=您的骰子数,bids[yourId]是您的第一个出价(如果是第一次,则为null)。那是对的吗?
不是查尔斯(Charles)

1
我见过一些游戏,其中一些提交的游戏比其他提交的游戏更多(没人:414个游戏,Straight Shooter:409个游戏)。这不公平,您能解决这个问题吗?
CommonGuy

Answers:


6

没有人

尝试猜测其他玩家的骰子。如果不知道该怎么做,请致电其他漫游器骗子。

编辑:修复了一个人永远不会出价,从不致电骗子的问题。

public class Nobody extends Player{

    @Override
    public String bid(int myId, int[] diceEachPlayerHas, int[] myDice,
            String[] bids) {
        if (bids.length == 0)
            return "1 2";
        int wilds = 0;
        int players = Controller.numPlayers();
        double myKnowledge = (double)diceEachPlayerHas[myId]/Controller.diceInPlay();
        double previousKnowledge = (double)diceEachPlayerHas[(myId-1+players)%players] / Controller.diceInPlay();
        int[] dice = new int[5];
        for (int i = 0; i < myDice.length; i++) {
            if (myDice[i] == 1) {
                wilds++;
            } else {
                dice[myDice[i]-2]++;
            }
        }
        wilds = (int) (1/myKnowledge+wilds-1)+1;
        for (int i = 2; i <= 6; i++) {
            dice[i-2] += wilds;
        }
        String best = "0 0";
        for (int i = 2; i <= 6; i++) {
            if (Controller.isGreaterThan(dice[i-2] + " " + i, best)) {
                best = dice[i-2] + " " + i;
            }
        }
        if (Controller.isGreaterThan(best, bids[bids.length - 1])) {
            return best;
        }
        if (previousKnowledge > 0.4) {
            int prev = Integer.valueOf(bids[bids.length - 1].split(" ")[0]);
            int prevFace = Integer.valueOf(bids[bids.length - 1].split(" ")[1]);
            if (dice[prevFace - 2] +2 >= prev)
                return (prev+1) + " " + bids[bids.length - 1].split(" ")[1];
        }
        return "Liar!";
    }
}

您最近的更新似乎确实有所帮助。
SuperJedi224 2015年

6

Badnomial,根据二项式分布做出错误决定的机器人:编辑:修复了概率计算中的一个愚蠢错误,现在考虑下一个竞标者以及上一个竞标者。

    public class Badnomial extends Player{
    public String toString() {return "Badnomial";}

  public String bid(int myId, int[] diceEachPlayerHas, int[] myDice, String[] bids) {
  int[] dieCounts = new int[7];
  for(int i:myDice)
   dieCounts[i]++;
  for(int i=2; i<7; i++)
   dieCounts[i] += dieCounts[1];

  if(bids.length > 0)
  {
   String[] lastBid = bids[bids.length - 1].split(" ");
   int bidCount = Integer.valueOf(lastBid[0]);
   int bidDie = Integer.valueOf(lastBid[1]);
   // Check if I hold a better bid
   boolean betterBid = false;
   int myBidDie;
   int myBidCount;
   int myHighestCount = 0;
   int myHighDie = bidDie +1;

   for(int i = 2; i < 7; i++) {
    if(dieCounts[i] >= myHighestCount) {
     myHighestCount = dieCounts[i];
     myHighDie = i;
    }
   } 
    if((myHighestCount > bidCount) || ((myHighestCount == bidCount) && (myHighDie > bidDie))) {
     betterBid = true;
     myBidDie = myHighDie;
     myBidCount = myHighestCount;
     }

   if(betterBid == false) {
    int unknownDice = Controller.diceInPlay() - myDice.length;
    int myDiceNeeded = bidCount - myHighestCount;
 if(myHighDie <= bidDie)
  myDiceNeeded++;
    int previousBidder = myId - 1;
    if(previousBidder < 0)
     previousBidder = Controller.numPlayers() -1;
    int bidderDiceNeeded = bidCount - dieCounts[bidDie] - (int)(diceEachPlayerHas[previousBidder]/3 +1);
    int bidderUnknown = Controller.diceInPlay() - diceEachPlayerHas[previousBidder] -myDice.length;
 int nextBidder = myId + 1;
 if(nextBidder == Controller.numPlayers())
  nextBidder = 0;
 int nbDiceNeeded = myDiceNeeded - (int)(diceEachPlayerHas[nextBidder]/3 +1);
    int nbUnknown = Controller.diceInPlay() - diceEachPlayerHas[nextBidder];
    //float myChances = (unknownDice/3 - myDiceNeeded)/((float)unknownDice/9);
    //float bidderChances = (bidderUnknown/3 - bidderDiceNeeded)/((float)bidderUnknown/9);
    double myChances = 1 - cumBinomialProbability(unknownDice, myDiceNeeded -1);
    double bidderChances;
    if(bidderDiceNeeded > 0)
     bidderChances = 1- cumBinomialProbability(bidderUnknown, bidderDiceNeeded -1);
    else bidderChances = 1.0;
    double nbChances;
    if(nbDiceNeeded > 0)
      nbChances = 1- cumBinomialProbability(nbUnknown, nbDiceNeeded -1 );
    else nbChances = 1.0;
    if(((myChances < .5) && (nbChances <.5)) || (bidderChances < .2))
     return "Liar!";
   }

   return (bidCount+1) + " " + myHighDie;
  }

  return 2 + " " + 2;
 } 

 private double cumBinomialProbability(int n, int k) {
   double sum = 0;
   for(int i = 0; i <=k; i++)
     sum += binomialProbability(n, i);
   return sum;
 }

 private double binomialProbability(int n, int k) {
   double nfact = 1;
   double dfact = 1;
   int greater;
   int lesser;
   if((n-k) > k) {
     greater = n - k;
     lesser = k;
   }
   else {
     greater = k;
     lesser = n-k;
   }
   for(int i = greater+1; i <= n; i++)
     nfact = nfact * i;
   for(int i = 2; i <= lesser; i++)
     dfact = dfact * i;
   return (nfact/dfact)*(Math.pow((1.0/3), k))*Math.pow(2.0/3, (n-k));
 }

}

它尝试根据自身的估计累积二项式分布以及前次和下一次竞标者出现所需骰子的机会来确定是否应该虚张声势或致电Liar。

基本上,如果前一个竞标者很可能是说谎者,或者感觉到它和下一个竞标者都更有可能说谎,则称其为说谎者。


通过这些更改,Badnomial实际上似乎比其他僵尸程序更胜任。
InactionPotential

5

直射手

他打得很直并且没有虚张声势。他还很天真地认为其他人也这样做,因此除非出价超过了游戏中骰子的总数(减去与出价不匹配的他自己的骰子),否则他从不叫骗子。

为了比每个死者的确切预期数字更为保守,他没有算出自己的荒野,而是假设其他人的分布均匀。在目前的四名球员中,他或MostlyHonestAbe每次都排名第一,得分相当接近。

我假设最低出价是2 2。如果允许一个(或多个)投标,请告诉我,以便我进行更改。

public class StraightShooter extends Player{
    public String toString(){return "Straight Shooter";}
    public String bid(int me, int[] numDices, int[] dice, String[] bids){
        int[] counts = new int[7];
        double[] expected = new double[7];
        int unknown = Controller.diceInPlay() - dice.length;
        for(int i:dice)
            counts[i]++;
        for(int i=2;i<7;i++)
            expected[i] = counts[i] + unknown / 3d;
        int bidCount = 2;
        int bidDie = 2;
        if(bids.length > 0){
            String[] lastBid = bids[bids.length-1].split(" ");
            bidCount = Integer.valueOf(lastBid[0]);
            bidDie = Integer.valueOf(lastBid[1])+1;
            int possible = Controller.diceInPlay();
            for(int i=2;i<7;i++)
                if(i != bidDie)
                    possible -= counts[i];
            if(bidCount > possible)
                return "Liar!";

            if(bidDie > 6){
                bidDie = 2;
                bidCount++;
            }
        }
        double best = Double.MAX_VALUE;
        int bestCount = bidCount;
        int bestDie = bidDie;
        for(int count=bidCount;count<=Controller.diceInPlay();count++){
            for(int die=bidDie;die<7;die++){
                double score = Math.abs(expected[die]-bidCount);
                if(score < best){
                    best = score;
                    bestCount = count;
                    bestDie = die;
                }
            }
            bidDie = 2;
        }   
        return bestCount + " " + bestDie;
    }
}

This和MostlyHonestAbe都非常不愿意说谎或打电话说谎,当我测试哈哈时,有些游戏将进行2000轮。:P
该隐2015年

我的也是。没关系,因为每个转弯都指向最终比分。如果我最后2000转并没有赢,那比在我的书中赢得100后更好;)
Geobits 2015年

我只需要再次查看评分规则。全新游戏XD
该隐2015年

是的,通过这种评分方式,看来最佳策略可能是尽可能保守一些,然后累积分数。也许有更好的东西,但我看不到。
Geobits 2015年

1
不确定这会有什么不同。保持保守仍然是一个优势,只是因为您丧生的机会较低。越来越多的人在现实生活中不这么玩的原因是因为它很无聊,但是机器人对无聊的兴趣是什么?
Geobits,2015年

4

最诚实的安倍

安倍对其余对手的死亡做出保守的猜测,然后保持诚实,直到他认为没有足够的骰子来击败当前的出价。这时他虚张声势一次,然后下一次打电话给骗子。

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class MostlyHonestAbe extends Player{

    final boolean debug = false;
    boolean bluffedOnce = false;
    PrintStream out;
    @Override
    public String bid(int myId, int[] diceEachPlayerHas, int[] myDice, String[] bids) {
        try {
            File f = new File("abe.log.txt");
            out = new PrintStream(f);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            //e.printStackTrace();
        }
        if(debug){
            out = System.out;
        }

        //reset bluff counter on the first round
        if(bids.length < diceEachPlayerHas.length){
            bluffedOnce = false;
        }

        //Is it the first bid?
        if(bids.length == 0){
            out.println("I go first");
            return lowestViableBid(1,1, myDice, diceEachPlayerHas, true);
        }

        out.println("Last bid = " + bids[bids.length - 1]);
        out.print("My Dice = ");
        for(int d : myDice){
            out.print(d + ", ");
        }
        out.println();

        //What was the last bid?
        String[] lastBid = bids[bids.length -1].split(" ");
        return lowestViableBid(Integer.parseInt(lastBid[1]), Integer.parseInt(lastBid[0]), myDice, diceEachPlayerHas, false);


    }

    //Lowest honest bid, or liar
    private String lowestViableBid(int highestVal, int highestCount, int[] myDice, int[] otherDice, boolean firstTurn){

        //Make a better array for the dice
        //Include what the other players probably have
        int wilds = numDie(1, myDice);
        int[] diceCount = new int[6];
        diceCount[0] = wilds;
        int otherPlayerExpectedValue = 0;
        for(int d : otherDice){
            otherPlayerExpectedValue += d;
        }
        otherPlayerExpectedValue -= myDice.length;
        out.println("Number of other dice = " + otherPlayerExpectedValue);
        otherPlayerExpectedValue = otherPlayerExpectedValue / 4;
        //Note: Other player expected value is biased low, counting wilds the number should be divided by 3.

        out.println("playerExpectedVal = " + otherPlayerExpectedValue);
        for(int i = 1; i < 6; i++){
            diceCount[i] = numDie(i + 1, myDice) + wilds + otherPlayerExpectedValue;
        }


        //What's my array look like?
        for(int i = 0; i < diceCount.length; i++){
            out.println("diceVal = " + (i + 1) + ", diceCount = " + diceCount[i]);
        }

        //Can I bid the same number, but higher dice val?
        for(int diceVal = highestVal + 1; diceVal <= 6; diceVal++){
            if(diceCount[diceVal - 1] >= highestCount){ 
                out.println("1.Returning " + highestCount + " " + diceVal);
                return highestCount + " " + diceVal; }  
        }

        //What about more dice?
        for(int diceNum = highestCount + 1; diceNum <= myDice.length; diceNum++){
            for(int diceVal = highestVal + 1; diceVal <= 6; diceVal++){
                if(diceCount[diceVal - 1] == diceNum){ 
                    out.println("2.Returning " + (diceNum) + " " + diceVal);
                    return (diceNum) + " " + diceVal; } 
            }
        }

        if(firstTurn){ return "1 2"; }
        //If this is the first time I'm out of my league, bluff a round before calling liar.
        if(!bluffedOnce){
            out.println("bluffing " + (highestCount + 1) + " " + highestVal);
            bluffedOnce = true;
            return (highestCount + 1) + " " + highestVal;
        }
        out.println("Returning Liar!");
        //Well, wouldn't want to lie
        return "Liar!";
    }

    private int numDie(int i, int[] myDice){
        int result = 0;
        for(int j : myDice){
            if(i == j){ result++; }
        }
        return result;
    }
}

1
你在跟我开玩笑吗?从发布HonestAbe不到五分钟。现在我必须考虑一个新名称:P
Geobits,2015年

1
没有亚伯拉罕·林肯(Abraham Lincoln)引用的地方,就不可能有以骗子(Liar)为名的游戏。
该隐2015年

4

豪斯博士

大家都撒谎!

public class DrHouse extends Player
{   
  public String bid(int yourId, int[] diceEachPlayerHas, int[] yourDice, String[] bids)
  {
    return "Liar!";
  }
}

1
我建议为您有第一轮出价时添加特殊的逻辑。
SuperJedi224

4
@ SuperJedi224我以为机器人会认为控制器告诉他轮到他是骗子了
Nathan Merrill

令我开心的一天
Rohan Jhunjhunwala

2

菲德利奥

这个机器人知道只有他最经常出现的价值才能带领他获得胜利,所以他坚持下去。他假定每个人的骰子中都有一部分与他的骰子相同,如果有人出价高于该部分,则他认为他是骗子。

public class Fidelio extends Player
{
    final String LIAR ="Liar!";
    @Override
    public String bid(int yourId, 
            int[] diceEachPlayerHas, 
            int[] yourDice,
            String[] bids) 
    {
        int[] myDices = new int[6];
        int valueToBid=1;
        for(int i : yourDice)
            myDices[i-1]++;
        for(int i=2;i<myDices.length;i++)
            if(myDices[i]>=myDices[valueToBid])
                valueToBid=i;
        if(bids.length==0)
            return 2+" "+valueToBid;
        int sum=0;
        String[] lastBidString=bids[bids.length-1].split(" ");
        int[] lastBid = new int[2];
        lastBid[0] = Integer.parseInt(lastBidString[0]);
        lastBid[1] = Integer.parseInt(lastBidString[1])-1;
        for(int i : diceEachPlayerHas)
            sum+=i;
        sum-=yourDice.length;
        if(lastBid[0]>sum/3+myDices[lastBid[1]]+myDices[0])
            return LIAR;
        if(lastBid[1]>= valueToBid)
        {
            if(lastBid[0]>=myDices[0]+myDices[valueToBid]+sum*2/5)
                return LIAR;
            return (lastBid[0]+1)+" "+myDices[valueToBid];
        }
        return lastBid[0]+" "+valueToBid;
    }
}

我希望他会做一些好工作:)。


我在第13行上获得了IndexOutOfBoundsException。请记住,数组在Java中是0索引的。
SuperJedi224

现在,在第19行的另一端,索引为-1。看来它正在尝试从空数组中读取最后一个元素,您应该对此进行检查。
SuperJedi224

已修复,在我使用出价后检查if(bids.length == 0)是否完成...
Katenkyo 2015年

哦,我刚刚提出了另一种可能的解决方案,但这也可能会起作用。
SuperJedi224

嗯,因此不再需要此建议的编辑?
mbomb007

2

统计员

您有1/3的机会获得除ace以外的任何数字。曾经有人告诉我,不检查骰子而知道赔率可以使您赢得这场比赛。编辑:出价太高。但这并不能提高很多分数。

public class Statistician extends Player{
    public String toString(){return "Statistician";}
    public String bid(int me, int[] numDices, int[] dice, String[] bids){
        int totalDices = 0;
        int currentBid, max;
        for (int i : numDices)
            totalDices += i;
        max = totalDices/3;
        if(bids.length>0){
            currentBid = Integer.valueOf(bids[bids.length-1].split(" ")[0]);
            if(currentBid>max)
                return "Liar!";
        }
        return max+" 6";
    }
}

1

荒诞机器人

声称所有骰子均为6,除非不能如此。如果机器人无法做到这一点,则意味着这是不可能的情况或几乎不可能的情况。因此,它称为骗子。我对这个漫游器的有效性感到好奇。

public class AbsurdBot extends Player {
    @Override
    public String bid(int yourId, int[] diceEachPlayerHas,int[] yourDice,String[] bids)
    {
        String[] lastbid;
        int a, b, d;
        d = 0;
        for (int dice : diceEachPlayerHas)
            d += dice;
        if (bids.length != 0)
            {
                lastbid = bids[bids.length-1].split(" ");
                a = Integer.parseInt(lastbid[0]);
                b = Integer.parseInt(lastbid[1]);
                if (a > d || a == d && b == 6)
                    return "Liar!";
            }
        return d + " 6";
    }
}

至于效果如何:它的主要功能似乎是将骰子交给任何跟随它的玩家:P
Geobits,2015年

我修复了@Geobits代码。当您尝试跳入以前从未使用过的编程语言时,就会发生这种情况……
frederick 2015年

@Geobits感谢您的所有帮助。我认为现在终于可以正常使用了。可以?(Java令人困惑)
frederick

是的,它现在运行。但是,该策略令人难以置信的自杀。它仅得分第二低的玩家的2%。
Geobits,2015年

@Geobits我从未尝试过与其他玩家对抗。您是否与其他人竞争?
frederick 2015年

1

海盗

我在测试控制器时做了几个简单的机器人,这是唯一真正有用的机器人。

以后可能会改进。

import java.util.Arrays;
import java.util.Scanner;

public class Pirate extends Player{
    public Pirate() {
    }
    public String toString(){
        return "The Pirate";
    }
    private String bid(int[] t,int tol){
        int[]z=t.clone();
        Arrays.sort(z);
        int j=0;
        for(int i=0;i<6;i++){
            if(t[i]==z[5]){j=i;break ;}
        }
        return (tol+t[j])+" "+(j+1);
    }
    @Override
    public String bid(int yourId, int[] diceEachPlayerHas, int[] yourDice,
            String[] bids) {
        int[] t=new int[6];
        for(int i=0;i<yourDice.length;i++){
            t[yourDice[i]-1]++;
        }
        for(int i=1;i<t.length;i++)t[i]+=t[0];
        int tol=(Controller.diceInPlay()-yourDice.length)/4;
        if(bids.length==0)return bid(t,1);
        Scanner i=new Scanner(bids[bids.length-1]);
        int x=i.nextInt(),y=i.nextInt();
        i.close();
        if(t[y-1]>x)return (t[y-1]+2)+" "+y;
        int nd=Controller.diceInPlay();
        if(x>nd+t[y-1]-yourDice.length)return "Liar!";
        if(Controller.isGreaterThan(bid(t,tol), bids[bids.length-1])){
            int z=Controller.valueOf(bids[bids.length-1]);
            for(int j=1;j<=tol;j++)if(Controller.valueOf(bid(t,j))>z)return bid(t,j);
        }
        return "Liar!";
    }
}
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.