Король горы - кости лжеца

22

Игра Liar's Dice - довольно простая игра в кости. Я видел несколько разных вариантов правил, но вот версия, с которой я наиболее знаком:

  • Каждый игрок начинает с 5d6
  • За исключением случаев подсчета игральных костей в конце раунда, каждый игрок может видеть свои собственные кубики, но не у любого оппонента.
  • В начале любого раунда все игроки бросают любые кости, которые у них есть в данный момент.
  • Затем один игрок (обычно это либо победитель предыдущего раунда, либо игрок слева от игрока, начавшего в прошлый раз; мы будем использовать первого для этого KotH; случайный игрок начинает первый раунд) делает предположение о том, сколько из определенного числа на столе (ОДИН ДИКИЙ)
  • Торги продолжаются вправо, повышаясь каждый раз (например, 3 пятерки, 3 шестерки и 4 двойки выше 3 четверок, а 3 тройки - нет; 4 также выше, но ставки на них, вероятно, приведут вас к недостаток); пока любой игрок не называет игрока, предшествующего ему, лжецом
  • В этот момент все игроки показывают свои кости и подсчитывают количество последних ставок на столе.
  • Если общая сумма ниже, чем ставка, игрок, сделавший ставку, должен дать кубик игроку, который назвал их лжецом, в противном случае игрок, который назвал участника аукциона лжецом, должен дать кубик игроку (так что участник выигрывает если их хотя бы столько, сколько он предлагал, точного числа не должно быть)
  • Когда у вас кончаются кости, вы теряете
  • Последний стоящий игрок выигрывает

Например:

У первого игрока 1,1,2,4,6
Второй игрок имеет 1,2,2,3,5
Игрок третий имеет 1,3,3,4,6
Первый игрок: три шестерки.
Второй игрок: четыре пары.
Игрок третий: четыре тройки.
Первый игрок: пять двойок.
Второй игрок: шесть двойок.
Игрок третий: шесть троек.
Первый игрок: шесть четверок.
Игрок второй: Лжец!
Они показывают свои кости и подсчитывают (потому что они дикие) и четверки.
Оказывается, на самом деле ровно шесть четверок.
Таким образом, игрок два дает игроку один кубик.
Они перебираются, и игрок 1 начинает следующий раунд.

Вы должны написать бота, чтобы играть в эту игру. Он должен реализовывать следующий абстрактный класс 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, но это не обязательно. Тем не менее, вы не можете редактировать его любым способом, который мешает удобочитаемости выходных данных контроллера.
  • Вам разрешено вызывать любые другие открытые методы контроллера, но не его основной метод.
  • Вы можете читать и редактировать только файлы в рабочем каталоге с префиксом собственного имени вашего бота
  • Вам не разрешено принимать входные данные из любого другого источника
  • Переменные экземпляра сбрасываются в начале каждой новой игры, но статические переменные - нет.

счет

  • Один набор из 1000 игр с 3-5 игроками в каждой будет симулироваться при каждом добавлении бота (как только будет представлено три или более ботов), как показано в источнике контроллера (в любой данной игре вы получить 1 в начале каждого вашего хода, 10 каждый раз, когда вы захватываете кубик, и 1000 бонусов, если вы выиграете); соблюдение лимита в 5000 TURNS (не раундов) в каждой игре.
  • Ваш бот будет оцениваться по его счету из последних игр; плюс в десять раз больше его голосов, если он не отрицательный. (Последнее вряд ли окажет существенное влияние на оценку)

Источник контроллера можно найти здесь.

Результаты по состоянию на 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
SuperJedi224
источник
1
Вы должны уточнить, что выходные данные должны быть «2 3», а не «две тройки», как показывает ваш пример. Кроме того, есть ли способ в контроллере смотреть один матч?
Каин
Не в официальной версии, но я опубликую альтернативную версию, которая позволит вам сделать это.
SuperJedi224
@ Geobits: если вы хотите. Это поставит вас в невыгодное положение, если кто-то позвонит вам.
SuperJedi224
1
Я предполагаю, что индексы массивов - это «идентификаторы» игроков, так что diceEachPlayerHas[yourId]= количество ваших кубиков и bids[yourId]ваша первая ставка (или ноль, если это ваш первый ход). Это верно?
Не то, что Чарльз
1
Я видел игры, в которых некоторые материалы играют в большее количество игр, чем другие (Никто: 414 игр, Straight Shooter: 409 игр). Это нечестно, не могли бы вы исправить это?
CommonGuy

Ответы:

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!";
    }
}
CommonGuy
источник
Ваш последний набор обновлений действительно помог.
SuperJedi224
6

Badnomial, бот, который принимает неверные решения на основе биномиальных распределений: Edit: Исправлена ​​глупая ошибка в вычислениях вероятности, теперь учитывает как следующего Bidder, так и предыдущего.

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

}

Он пытается определить, должен ли он блефовать или коллировать на лжеца, основываясь на оценочных кумулятивных биноминальных распределениях для себя и шансов предыдущего и следующего претендента на наличие необходимых костей.

По сути, он называет Лжецом, если предыдущий Участник, скорее всего, является Лжецом, или если он чувствует, что и он, и следующий Участник скорее лгут, чем нет.

InactionPotential
источник
С этими изменениями Badnomial фактически кажется отдаленно компетентным против других ботов.
Бездействие Потенциал
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;
    }
}
Geobits
источник
Это и MostlyHonestAbe настолько не решаются лгать или называть лжецом, что некоторые игры идут на 2000 ходов, когда я проверяю хаха. : P
Каин
То же самое в моем. Впрочем, это нормально, потому что каждый ход - это дополнительное очко к окончательному счету. Если я в прошлом 2000 ходов и не выиграл, это лучше, чем победа после 100 в моей книге;)
Geobits
Мне просто нужно было снова посмотреть правила подсчета очков. Совершенно новая игра XD
Cain
Да, при таком выигрыше кажется, что оптимальная стратегия - быть настолько консервативным, насколько это возможно, и просто набирать очки. Может быть, есть что-то лучше, но я не вижу этого.
Geobits
1
Я не уверен, что это будет иметь большое значение. Быть консерватором все равно будет преимуществом, просто потому что у вас меньше шансов потерять кубик. Причина, по которой все больше людей так не играют в реальной жизни, в том, что это просто скучно, но что такое скука для бота?
Geobits
4

MostlyHonestAbe

Эйб делает консервативные предположения о том, что остальные оппоненты умирают, а затем остается честным, пока не решит, что на текущей ставке достаточно костей. В этот момент он блефует один раз, а в следующий раз коллирует.

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
1
Не может быть игры с именем лжеца без упоминания Авраама Линкольна.
Каин
4

Доктор Дом

Все лгут!

public class DrHouse extends Player
{   
  public String bid(int yourId, int[] diceEachPlayerHas, int[] yourDice, String[] bids)
  {
    return "Liar!";
  }
}
Gus314
источник
1
Я предлагаю добавить специальную логику, когда у вас есть первая ставка в раунде.
SuperJedi224
4
@ SuperJedi224 Я предполагаю, что бот затем считает, что диспетчер говорит ему, что его очередь быть лжецом
Натан Меррилл
Сделал мой день LOL
Рохан Jhunjhunwala
2

Fidelio

Этот бот знает, что только его самая постоянная ценность приведет его к победе, поэтому он придерживается ее. Он предполагает, что есть часть всех игральных костей, которые совпадают с его, если кто-то предложит больше, чем эта часть, он предполагает, что он лжец.

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

Я надеюсь, что он сделает хорошую работу :).

Katenkyo
источник
Я получаю IndexOutOfBoundsException в строке 13. Помните, что массивы 0-индексированы в Java.
SuperJedi224 15.06.15
Теперь я получаю один на другом конце в строке 19, для индекса -1. Казалось бы, он пытался прочитать последний элемент из пустого массива, вы должны включить проверку для этого.
SuperJedi224 15.06.15
Исправлено: проверка того, что (bids.length == 0) была сделана после того, как я использовал ставки ...
Katenkyo
О, я только что предложил другое возможное решение, но это, вероятно, также будет работать.
SuperJedi224
Ах, так это предложенное редактирование больше не нужно?
mbomb007
2

статистик

У вас есть 1/3 шансов получить любое количество, кроме тузов. Один парень однажды сказал мне, что если вы не проверяете ваши кубики и не знаете, что такое шансы, вы можете выиграть эту игру. РЕДАКТИРОВАТЬ: Это было слишком высокой ставкой. Но это не сильно улучшит счет.

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
@ Geobits я исправил код. Вот что происходит, когда вы пытаетесь перейти на язык программирования, на котором вы раньше не программировали ...
Фредерик
@Geobits Спасибо за помощь. Я думаю, что теперь это наконец работает правильно. Является ли? (Java сбивает с толку)
Фредерик
Да, это работает сейчас. Однако эта стратегия безумно самоубийственна. Он набирает только ~ 2% от следующего самого низкого игрока.
Geobits
@Geobits Я никогда не пытался запустить его против других игроков. Вы управляли этим против других?
Фредерик
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!";
    }
}
SuperJedi224
источник