Eso es BS! (juego de cartas)

29

BS es un juego de cartas donde el objetivo del juego es perder todas tus cartas.

Un juego consta de cuatro jugadores y un mazo de 52 cartas. Cada jugador recibe 13 cartas al azar. Normalmente, las tarjetas se etiquetan 2 - 10, As, Jack, Queen, King, pero por simplicidad, las tarjetas se etiquetarán con un número del 0 al 12 inclusive. Aunque el número de cartas en la mano de un jugador es información pública, solo el jugador sabe qué cartas específicas hay en su mano.

El juego es el siguiente: el primer jugador coloca tantas cartas etiquetadas con 0 como quiera en la pila de descarte (tenga en cuenta que no está obligado a jugar todas sus cartas etiquetadas con 0 , aunque generalmente le conviene hacerlo) ) Debe jugar al menos una carta. El segundo jugador juega tantas cartas como quiera etiquetadas como 1 , el tercer jugador juega 2 , y así sucesivamente. Después de 12, se restablece a 0.

¿Qué sucede si no tienes ninguna de las cartas que se supone que juegas? Recuerda, debes jugar al menos una carta; de hecho, ¡puedes jugar las cartas que quieras! (En realidad, incluso si tienes la carta correcta, puedes mentir y jugar una carta diferente). Sin embargo, alguien puede llamarte y decirte "¡BS!" Si ese alguien está en lo correcto y usted mintió, entonces debe tomar todas las cartas en la pila de descarte; Como recompensa, el jugador que te llamó al azar coloca una de sus cartas en la pila de descarte. Sin embargo, si el acusador está equivocado, debe tomar todas las cartas en la pila de descarte. Tenga en cuenta que no puede mentir sobre la cantidad de cartas que juega.

Información más detallada:

  • Al comienzo del juego, se eligen cuatro jugadores aleatorios para jugar. Como habrá al menos 1000 juegos, cada jugador tendrá la oportunidad de jugar. El orden de turno se decide al azar al comienzo del juego.
  • Si devuelve una tarjeta correcta y una tarjeta incorrecta, entonces se considera mentirosa (es decir, si se suponía que debía dar 2 s, y dio una 2 y una 1 , entonces eso es mentira)
  • Si dos o más jugadores dicen BS al mismo tiempo, se elige uno al azar.
  • Tu puntaje es el porcentaje de juegos que ganas.
  • Hay un máximo de 1000 rondas, donde una ronda es cada jugador que va una vez. Por lo general, alguien gana antes de esto. Si nadie gana, se cuenta para el número total de juegos jugados, pero nadie gana.

Especulación:

Deberías escribir una clase que se extienda Player. Se verá así:

package players;

import java.util.ArrayList;
import java.util.List;

import controller.*;

public class Player1 extends Player {

    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        Card[] hand = getHand();
        List<Card> ret =  new ArrayList<Card>();
        for (Card c : hand) {
            if (c.getNumber() == card) {
                ret.add(c);
            }
        }
        if (ret.size() == 0) ret.add(hand[0]);
        return ret;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards, Controller controller) {
        return numberOfCards >= 3;
    }

    protected void update(Controller controller) {
      // This method gets called once at the end of every round
    }

    protected void initialize(Controller controller) {
      // This method gets called once at the beginning once all the cards are dealt
    }

    public String toString() {
        return "Player 1";
    }
}

El método requestCardsse llama cuando es tu turno. El argumento cardes el número de tarjeta que se supone que debe proporcionar. Devuelve una lista de cartas que desea poner en la pila de descarte. El jugador de arriba verifica si tiene alguna carta del tipo de carta solicitada; si no, simplemente juega su primera carta y espera que nadie la controle.

Se bsllama al método cada vez que alguien más juega una carta. El primer argumento es el jugador, el segundo, la carta que se suponía que debía jugar, y el tercero, el número de ese tipo de carta que dice haber jugado. Regrese truesi desea llamar "BS". En el código anterior, el jugador solo llama "BS" cuando el otro jugador afirma tener 3 o más cartas del tipo solicitado.

El último argumento para ambos métodos es controller, que es solo el controlador que controla el juego. Desde el controlador, puede obtener más información pública, como el número de cartas en la pila de descartes o la lista y el orden de turnos de los jugadores.

El toStringmétodo es opcional.

Conroller en GitHub: https://github.com/prakol16/bs

Si desea publicar una solución que no sea Java, puede usar la interfaz provista en https://github.com/LegionMammal978/bs (créditos para LegionMammal978) e intentaré integrarla.

Marcador hasta ahora:

class players.PlayerConMan: 2660/4446 = 59.82905982905983%
class players.CalculatingLiar: 2525/4426 = 57.049254405784005%
class players.PlayerTruthy: 1653/4497 = 36.75783855903936%
class players.Player4: 1446/4425 = 32.67796610169491%
class players.Player1: 536/4382 = 12.23185759926974%
class players.Player3: 493/4425 = 11.141242937853107%
class players.Player2: 370/4451 = 8.312738710402156%
class players.LiePlayer: 317/4432 = 7.152527075812275%
class players.Hoarder: 0/4516 = 0.0%

PlayerConMan está ganando, pero CalculatingLiar está en un segundo lugar. Estos puntajes parecen consistentes: son bastante iguales siempre.

soktinpk
fuente
13
Tienes que estar bromeando. Tengo un controlador casi terminado para BS con el único propósito de crear este desafío exacto. Bueno, supongo que tengo que encontrar otra forma de pasar mi tiempo ahora.
IchBinKeinBaum
3
Puede ser aconsejable no exponer Controller.toString()al público, ya que devuelve las manos de todos los jugadores y la pila de descarte.
es1024
@IchBinKeinBaum, si su controlador puede comunicarse con STDIN / STDOUT, puede considerar publicar el desafío con su controlador para todas las personas que no son Java.
Logic Knight el
@CarpetPython: lo hace. También usa reglas ligeramente diferentes. Si eso no cuenta como un duplicado, lo haré.
IchBinKeinBaum
Acabo de terminar de crear un controlador multilingüe. El uso está en Program.cs. Lo puedes encontrar aquí .
LegionMammal978

Respuestas:

10

Estafador

ConMan observa cada carta que pasa por su mano, llamando a BS cuando no es posible una jugada debido a dónde están las cartas.

Juega la verdad cuando puede, pero miente de manera inteligente usando la última carta si se produjera una victoria

Pasé mucho tiempo ajustando una técnica para llamar a BS cuando la probabilidad era alta de que el oponente estuviera mintiendo, o cuando llamar a BS era beneficioso (como obtener cartas útiles de la pila de descarte), pero en la práctica, no llamar a BS en absoluto Yo la mayoría de los puntos.

package players;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import controller.*;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;

public class PlayerConMan extends Player {

    private enum Location {

        PLAYER_0,
        PLAYER_1,
        PLAYER_2,
        PLAYER_3,
        DISCARD,
        UNKNOWN
    };

    private class MyCard {

        private final int number;
        private Location location;
        private double confidence;
        protected Card card;

        public MyCard(int x) {
            this.number = x;
            location = Location.UNKNOWN;
            confidence = 1.0;
        }

        @Override
        public String toString() {
            if (confidence > 0.75) {
                return ""+number;
            } else if (confidence > 0.25) {
                return number+"*";
            } else {
                return number+"_";
            }
        }
    }

    private final ArrayList<ArrayList<MyCard>> theDeck = new ArrayList();
    private Location myLocation;
    private ArrayList<Player> players;
    private final ArrayList<MyCard> myHand = new ArrayList();
    private final HashMap<Location, Integer> sizes = new HashMap();
    private ArrayList<Integer> lies = new ArrayList();
    private ArrayList<Integer> truths = new ArrayList();


    // Constructor
    public PlayerConMan() {
        for (int i = 0; i < 13; ++i) {
            ArrayList<MyCard> set = new ArrayList();
            for (int j = 0; j < 4; ++j) {
                set.add(new MyCard(i));
            }
            theDeck.add(set);
        }
        sizes.put(Location.PLAYER_0, 13);
        sizes.put(Location.PLAYER_1, 13);
        sizes.put(Location.PLAYER_2, 13);
        sizes.put(Location.PLAYER_3, 13);
        sizes.put(Location.DISCARD, 13);
        sizes.put(Location.UNKNOWN, 39);
    }

    //Gets the MyCard for this card, updating a MyCard with the lowest confidence if not already created
    private MyCard getCard(Card c) {
        ArrayList<MyCard> set = theDeck.get(c.getNumber());
        MyCard unknown = null;
        double confidence = 1.0;
        for (MyCard m : set) {
            if (m.card == c) {
                return m;
            }
            if (m.card == null) {
                if (m.location == Location.UNKNOWN) {
                    unknown = m;
                    confidence = 0.0;
                } else if (m.confidence < confidence || unknown == null) {
                    unknown = m;
                    confidence = m.confidence;
                }
            }
        }
        unknown.card = c;
        return unknown;
    }

    //Returns the Location of a player
    private Location getLocation(Player p) {
        return Location.values()[players.indexOf(p)];
    }

    @Override
    protected void initialize(Controller controller) {
        super.initialize(controller);
        players = new ArrayList(controller.getPlayers());
        for (Player p : players) {
            if (p == this) {
                myLocation = getLocation(p);
            }
        }
        for (Location loc : Location.values()) {
            sizes.put(loc, 0);
        }
    }

    private ArrayList<Integer>[] getTruthesAndLies(Player player, int card, ArrayList<MyCard> myHand) {
            //Determine our next plays
            int offset = players.indexOf(player);
            int myOffset = players.indexOf(this);
            int nextCard = (card + (myOffset - offset + 4) % 4)%13;
            ArrayList<Integer> truths = new ArrayList();
            ArrayList<Integer> lies = new ArrayList();
            ArrayList<MyCard> cardsLeft = new ArrayList(myHand);
            while (!cardsLeft.isEmpty()) {
                boolean isLie = true;
                Iterator<MyCard> it = cardsLeft.iterator();
                while (it.hasNext()) {
                    MyCard m = it.next();
                    if (m.number == nextCard) {
                        it.remove();
                        isLie = false;
                    }
                }
                if (isLie) {
                    lies.add(nextCard);
                } else {
                    truths.add(nextCard);
                }
                nextCard = (nextCard + 4)%13;
            }

            return new ArrayList[]{truths, lies};
    }

    private void updateDeck(Player player, int card, int numberOfCards, Controller controller) {
        Location loc = getLocation(player);

        //Update from BS
        if (sizes.get(Location.DISCARD) + numberOfCards != controller.getDiscardPileSize()) {

            //Move all cards from DISCARD to the losing player
            //  Losing player defaults to player playing, in the rare case of a tie
            Location losingPlayer = loc;
            Location winningPlayer = null;
            for (Player p : players) {
                Location pLoc = getLocation(p);
                int size = p.handSize();
                if (pLoc == loc) size += numberOfCards;
                if (p.handSize() > sizes.get(pLoc)) {
                    losingPlayer = pLoc;
                } else if (size < sizes.get(pLoc)) {
                    winningPlayer = pLoc;
                }
            }

            if (winningPlayer == null) {
                debug(losingPlayer+" lost a BS");
            } else {
                debug(losingPlayer+" lied and "+winningPlayer+" lost a card");
            }

            //Move the cards from the discard to the player
            ArrayList<MyCard> winnersHand = new ArrayList();
            for (ArrayList<MyCard> set : theDeck) {
                for (MyCard m : set) {
                    if (m.location == Location.DISCARD) {
                        if (losingPlayer == myLocation) {
                            //If we lost, update the discard cards to unknown;
                            //  They'll be updated when we look at our hand
                            m.location = Location.UNKNOWN;
                            m.confidence = 1.0;
                        } else {
                            //Move to the losing player
                            m.location = losingPlayer;
                        }
                    } else if (m.location == myLocation && winningPlayer == myLocation) {
                        //Update our old cards to the discard pile, in case we won
                        m.location = Location.DISCARD;
                        m.confidence = 1.0;
                    } else if (m.location == winningPlayer) {
                        //Add the card to the winner's hand for later processing
                        winnersHand.add(m);
                    }
                }
            }

            //If someone else won, adjust the probabilities on their cards
            if (winningPlayer != myLocation && winningPlayer != null) {
                int winningSize = players.get(winningPlayer.ordinal()).handSize();
                if (winningPlayer == loc) winningSize += numberOfCards;
                for (MyCard m : winnersHand) {
                    m.confidence *= 1-(1/winningSize);
                }
            }

        }
        sizes.put(Location.DISCARD, controller.getDiscardPileSize());
        //Update player handSize
        for (Player p : players) {
            sizes.put(getLocation(p), p.handSize());
        }


        //Detect if my hand size has changed to speed processing
        if (myHand.size() != handSize()) {
            //Update values from my hand
            myHand.clear();
            for (Card c : getHand()) {
                MyCard m = getCard(c);
                m.location = myLocation;
                m.confidence = 1.0;
                myHand.add(m);
            }

            //Determine our next plays
            ArrayList<Integer> tl[] = getTruthesAndLies(player, card, myHand);
            truths = tl[0];
            lies = tl[1];
            debug("Truthes: "+truths);
            debug("Lies: "+lies);
        }
    }


    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        updateDeck(this, card, 0, controller);

        ArrayList<Card> ret = new ArrayList();
        int pick = card;
        boolean all = true;
        if (truths.get(0) != card) {
            pick = truths.get(truths.size()-1);
            all = false;
        }

        for (MyCard m : myHand) {
            if (m.number == pick) {
                m.location = Location.DISCARD;
                ret.add(m.card);
                if (!all) break;
            }
        }

        sizes.put(Location.DISCARD, controller.getDiscardPileSize() + ret.size());
        sizes.put(myLocation, myHand.size() - ret.size());
        printTheDeck();

        return ret;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards, Controller controller) {
        updateDeck(player, card, numberOfCards, controller);
        Location loc = getLocation(player);

        //Get total number of unknown cards and total number of cards the player must have
        int handSize = player.handSize() + numberOfCards;
        ArrayList<MyCard> playerHand = new ArrayList();
        ArrayList<MyCard> discardPile = new ArrayList();
        double totalUnknown = 0;
        double playerUnknown = handSize;
        double cardsHeld = 0;
        double cardsNotHeld = 0;
        for (ArrayList<MyCard> set : theDeck) {
            for (MyCard m : set) {
                if (m.location == Location.UNKNOWN) {
                    totalUnknown++;
                } else if (m.location == loc) {
                    playerHand.add(m);
                    playerUnknown -= m.confidence;
                    totalUnknown += 1.0 - m.confidence;
                    if (m.number == card) {
                        cardsHeld += m.confidence;
                    }
                } else {
                    if (m.location == Location.DISCARD) {
                        discardPile.add(m);
                    }
                    totalUnknown += 1.0 - m.confidence;
                    if (m.number == card) {
                        cardsNotHeld += m.confidence;
                    }
                }
            }
        }

        boolean callBS = false;
        double prob;
        int possible = (int)Math.round(4-cardsNotHeld);
        int needed = (int)Math.round(numberOfCards - cardsHeld);
        if (needed > possible) {
            //Player can't possibly have the cards
            prob = 0.0;
            debug("impossible");
            callBS = true;
        } else if (needed <= 0) {
            //Player guaranteed to have the cards
            prob = 1.0;
            debug("guaranteed");
        } else {
            //The probability that player has needed or more of the possible cards
            double successes = 0;
            for (int i = (int)needed; i <= (int)possible; i++) {
                successes += choose(possible, i) * choose(totalUnknown-possible, playerUnknown-i);
            }
            double outcomes = choose(totalUnknown, playerUnknown);
            prob = successes / outcomes;
            if (Double.isNaN(prob)) {
                prob = 0;
                callBS = true;
            }
            debug("prob = "+new DecimalFormat("0.000").format(prob));
        }

        //Update which cards they may have put down
        //  Assume they put down as many as they could truthfully
        int cardsMoved = 0;
        Iterator<MyCard> it = playerHand.iterator();
        while (it.hasNext()) {
            MyCard m = it.next();
            if (m.number == card) {
                it.remove();
                m.location = Location.DISCARD;
                discardPile.add(m);
                cardsMoved++;
                if (cardsMoved >= numberOfCards) {
                    break;
                }
            }
        }

        //We can't account for all the cards they put down
        //  Adjust existing probabilities and move our lowest confidence cards to the discard
        if (cardsMoved < numberOfCards) {
            //  Reduce the confidence of all remaining cards, in case they lied
            //  Assumes they lie at random
            double cardsLeft = handSize-cardsMoved;
            double cardsNeeded = numberOfCards-cardsMoved;
            double probChosen = 1 * choose(cardsLeft-1, cardsNeeded-1) / choose(cardsLeft, cardsNeeded);
            if (Double.compare(cardsLeft, cardsNeeded) == 0) {
                //They're gonna win, call their bluff
                callBS = true;
                for (MyCard m : playerHand) {
                    m.location = Location.DISCARD;
                }
            } else {
                for (MyCard m : playerHand) {
                    m.confidence *= (1-probChosen) * (1-prob) + prob;
                }
            }

            //  Move any UNKNOWN cards they could have played, assuming they told the truth
            Collections.sort(theDeck.get(card), new Comparator<MyCard>() {
                @Override
                public int compare(MyCard o1, MyCard o2) {
                    double p1 = o1.confidence - (o1.location == Location.UNKNOWN ? 10 : 0);
                    double p2 = o2.confidence - (o2.location == Location.UNKNOWN ? 10 : 0);
                    return (int)Math.signum(p1-p2);
                }
            });
            for (MyCard m : theDeck.get(card)) {
                if (m.location == Location.UNKNOWN || m.confidence < prob) {
                    m.location = Location.DISCARD;
                    m.confidence = prob;
                    cardsMoved++;
                    discardPile.add(m);
                    if (cardsMoved >= numberOfCards) break;
                }
            }
        }

        //Get the confidence of the discardPile
        double discardPileConfidence = 1.0;
        for (MyCard m : discardPile) {
            discardPileConfidence *= m.confidence;
        }
        discardPileConfidence *= Math.pow(0.5, controller.getDiscardPileSize() - discardPile.size());

        //Call BS if the cards in the discard pile consists only of cards we need / will play
        if (discardPileConfidence > 0.5 && discardPile.size() == controller.getDiscardPileSize()) {
            double truthCount = 0;
            double lieCount = 0;
            double unknownCount = 0;
            for (MyCard m : discardPile) {
                if (truths.contains(m.number)) {
                    truthCount += m.confidence;
                    unknownCount += 1-m.confidence;
                } else if (lies.contains(m.number)) {
                    lieCount += m.confidence;
                    unknownCount += 1-m.confidence;
                } else {
                    unknownCount += 1;
                    break;
                }
            }
            if (lieCount > 0 && unknownCount < 1) {
                debug("Strategic BS");
                //callBS = true;
            }
        }

        //What's the worst that could happen?
        //Test the decks' 
        ArrayList<MyCard> worstHand = new ArrayList<MyCard>(myHand);
        worstHand.addAll(discardPile);
        ArrayList<Integer> loseCase[] = getTruthesAndLies(player, card, worstHand);
        int winPlaysLeft = truths.size() + lies.size();
        int losePlaysLeft = loseCase[0].size() + loseCase[1].size();
        double randomPlaysLeft = Math.max(losePlaysLeft,7);
        double expectedPlaysLeft = losePlaysLeft * discardPileConfidence + randomPlaysLeft * (1-discardPileConfidence);
        double threshold = 0.0 - (expectedPlaysLeft - winPlaysLeft)/13.0;
        debug("winPlaysLeft = "+winPlaysLeft);
        debug("expectedPlaysLeft   = "+expectedPlaysLeft);
        debug("Threshold    = "+threshold);

        if(lies.isEmpty()) {
            threshold /= 2;
        }
        //callBS = callBS || prob < threshold;

        printTheDeck();
        return callBS;
    }

    static double logGamma(double x) {
        double tmp = (x - 0.5) * Math.log(x + 4.5) - (x + 4.5);
        double ser = 1.0 + 76.18009173 / (x + 0) - 86.50532033 / (x + 1)
                + 24.01409822 / (x + 2) - 1.231739516 / (x + 3)
                + 0.00120858003 / (x + 4) - 0.00000536382 / (x + 5);
        return tmp + Math.log(ser * Math.sqrt(2 * Math.PI));
    }

    static double gamma(double x) {
        return Math.exp(logGamma(x));
    }

    static double factorial(double x) {
        return x * gamma(x);
    }

    static double choose(double n, double k) {
        if (Double.compare(n, k) == 0 || Double.compare(k, 0) == 0) return 1.0;
        if (k < 0 || k > n) {
            return 0.0;
        }
        return factorial(n) / (factorial(n-k) * factorial(k));
    }

    public String toString() {
        return "ConMan";
    }

    public void printTheDeck() {
        HashMap<Location, ArrayList<MyCard>> map = new HashMap();
        for (Location loc : Location.values()) {
            map.put(loc, new ArrayList());
        }
        for (ArrayList<MyCard> set : theDeck) {
            for (MyCard m : set) {
                map.get(m.location).add(m);
            }
        }
        String ret = "";
        for (Player p : players) {
            ret += p.toString()+": "+map.get(getLocation(p))+"\n";
        }
        ret += "Discard pile: "+map.get(Location.DISCARD)+"\n";
        ret += "Unknown: ("+map.get(Location.UNKNOWN).size()+" cards)\n";
        debug(ret);
    }

    public void debug(Object s) {

    }
}
Wasmoo
fuente
Buen trabajo. Este bot gana fácilmente la mayoría de los juegos hasta ahora.
soktinpk
¡Gracias! Al revisar el código, me di cuenta de que olvidé llamar aBS cuando las tarjetas decían que era absolutamente imposible. He actualizado el código anterior.
Wasmoo
4

Jugador 3131961357_10

Elige a un jugador aleatorio en cada juego y siempre llama a BS en ese jugador.

package players;

import java.util.ArrayList;
import java.util.List;

import controller.*;

public class Player3131961357_10 extends Player{
    private int[] ducks = new int[13];
    private Player target = null;
    private int cake = 0;

    @Override
    protected List<Card> requestCards(int bacon, Controller controller){
        Card[] hand = getHand();
        List<Card> ret = new ArrayList<Card>();
        List<Card> others = new ArrayList<Card>();
        for(Card c:hand){
            if(c.getNumber() == bacon){
                ret.add(c);
            }else{
                others.add(c);
            }
        }
        if(ret.size() == 0){
            ImperfectPlayer.moveRandom(others, ret);
        }
        if(others.size() > 0 && ret.size() < 3 && handSize() > ret.size() + 1){
            ImperfectPlayer.moveRandom(others, ret);
        }
        return ret;
    }

    private final int someoneLied = 0;
    @Override
    protected boolean bs(Player player, int bacon, int howMuchBacon, Controller controller){
        if(target == null){
            // Could not find my cake.
            // Someone must have taken it.
            // They are my target.
            List<Player> players = controller.getPlayers();
            do target = players.get((int)Math.floor(Math.random() * players.size()));
            while(target != this);
        }

        int count = 0;
        Card[] hand = getHand();
        for(Card c:hand){
            if(c.getNumber() == bacon) 
                ++count;
        }
        if(cake >= controller.getDiscardPileSize()){
            ducks = new int[13];
            cake = someoneLied;
        }
        ducks[bacon] += howMuchBacon;
        cake += howMuchBacon;

        if(player.handSize() == 0) return true;
        return player.handSize() == 0 
            || howMuchBacon + count > 4 
            || ducks[bacon] > 5 
            || player == target 
            || Math.random() < 0.025; // why not?
    }

    public String toString(){
        return "Player 3131961357_10";
    }

    public static <T> void moveRandom(List<T> from, List<T> to){
        T a = from.remove((int)Math.floor(Math.random() * from.size()));
        to.add(a);
    }
}
es1024
fuente
4

Verdad

No del todo terminado, ya que no sé cómo decir el resultado de llamar a BS (si tomaron la pila, o alguien más en caso de empate, o lo hice).

Por el momento, solo llame a BS si puedo probarlo. No mientas a menos que tenga que hacerlo. Necesito mejorar el algoritmo de mentira. Estoy tratando de acercarme lo más posible a la forma en que juego BS contra otros jugadores (menos poner al azar cartas adicionales debajo para jugar 5 o 6 sin que ellos lo sepan).

package players;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import controller.*;

public class PlayerTruthy extends Player {

    private List<Card> played;
    private int discardPileSize;
    private HashMap<String,Integer> handSizes;
    private boolean initialized;
    //controller.getDiscardPileSize()

    // Constructor
    public PlayerTruthy() {
        played = new ArrayList<Card>();
        handSizes = new HashMap<String,Integer>();
        discardPileSize = 0;
        initialized = false;
    }

    // Initialize (do once)
    private void init(Controller controller) {
        for (Player p : controller.getPlayers()) {
            handSizes.put(p, 0);
        }
        initialized = true;
    }

    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        if (!initialized) {
            init(controller);
        }
        List<Card> cards = getCards(card);
        if (cards.size() == 0) {
            cards = lieCards(card);
        }
        played.addAll(cards);
        return cards;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards, Controller controller) {
        if (!initialized) {
            init(controller);
        }
        List<Card> hand = Arrays.asList(getHand());
        int count = countCards(hand, card);
        return numberOfCards > 4-count;
    }

    public String toString() {
        return "Truthy";
    }

    private int countCards(List<Card> list, int card) {
        int count = 0;
        for (Card c : list) {
            if (c.getNumber() == card) {
                count++;
            }
        }
        return count;
    }

    private List<Card> getCards(int card) {
        List<Card> cards = new ArrayList<Card>();
        Card[] hand = getHand();
        for (Card c : hand) {
            if (c.getNumber() == card) {
                cards.add(c);
            }
        }
        return cards;
    }

    private List<Card> lieCards(int card) {
        List<Card> hand = Arrays.asList(getHand());
        List<Card> cards = new ArrayList<Card>();
        int limit = 1;
        int count = 0;
        int index = (card+9) % 13;
        while (cards.size() == 0) {
            count = countCards(hand, index);
            if (count <= limit) {
                cards = getCards(index);
            }
            if (limit >= 3) {
                cards.removeRange(1, cards.size());
            }
            if (index == card) {
                limit++;
            }
            index = (index+9) % 13;
        }
        return cards;
    }
}
mbomb007
fuente
1
También puede realizar un seguimiento de las cartas jugadas.
seequ
No estoy seguro de qué estás tratando de hacer cards = cards.get(0). cardses una lista, por lo que no puede asignar un Carda a List<Card>. ¿Estás tratando de eliminar todo excepto el primer elemento?
soktinpk
Sí, lo arreglé.
mbomb007
Encontré los resultados de BS memorizando los tamaños de las manos de cada jugador y luego comparando mi memoria con lo que tiene el controlador. Un aumento significa que el jugador perdió; una disminución significa que ese jugador ganó. (El tamaño de la mano del jugador actual debe ser compensado por el numberOfCardsporque ya se descartan cuando bsse llama)
Wasmoo
No sé si alguna vez tendré tiempo para implementar lo que quería. Cuanto más pensaba en cómo jugaría BS de manera óptima, más difícil sería la programación. Tenía muchas ganas de hacer lo que ConMan tiene hasta cierto punto, pero la complejidad es un poco demasiado para mí.
mbomb007
4

Mentiroso

Este intenta jugar a la verdad. Si miente, usa una carta que no usará en el futuro cercano. También trata de ganar a través de BS llamando a otros jugadores, ya que la última carta casi nunca encaja.

package players;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import controller.Card;
import controller.Controller;
import controller.Player;

public class CalculatingLiar extends Player {
    private final List<Integer> knownCardsOnDeck = new ArrayList<>();
    private int lastDeckSize = 0;

    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        Card[] hand = getHand();
        List<Card> ret =  new ArrayList<Card>();
        for (Card c : hand) {
            if (c.getNumber() == card) {
                ret.add(c);
            }
        }
        if (ret.size() == 0) {
            ret.add(calculateWorstCard(card));
        }

        update(controller);

        for (Card c : ret) {
            knownCardsOnDeck.add(c.getNumber());
        }
        lastDeckSize = controller.getDiscardPileSize() + ret.size();
        return ret;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards,
            Controller controller) {
        Card[] hand = getHand();
        int myCards = 0;
        for (Card c : hand) {
            if (c.getNumber() == card)
                myCards++;
        }       
        update(controller);
        for (Integer number : knownCardsOnDeck) {
            if (number == card) {
                myCards++;
            }
        }

        return player.handSize() == 0
                || numberOfCards > 4
                || myCards + numberOfCards > 4
                || (player.handSize() < 5 && handSize() == 1);
    }

    @Override
    protected void initialize(Controller controller) {
        knownCardsOnDeck.clear();
        lastDeckSize = 0;
    }

    @Override
    protected void update(Controller controller) {
        if (lastDeckSize > controller.getDiscardPileSize()) {
            knownCardsOnDeck.clear();
            lastDeckSize = controller.getDiscardPileSize();
        } else {
            lastDeckSize = controller.getDiscardPileSize();
        }
    }

    private Card calculateWorstCard(int currentCard) {
        List<Integer> cardOrder = new ArrayList<>();

        int nextCard = currentCard;
        do {
            cardOrder.add(nextCard);
            nextCard = (nextCard + 4) % 13;
        } while (nextCard != currentCard);
        Collections.reverse(cardOrder);

        Card[] hand = getHand();
        for (Integer number : cardOrder) {
            for (Card card : hand) {
                if (card.getNumber() == number) {
                    return card;
                }
            }
        }
        //never happens
        return null;
    }

    @Override
    public String toString() {
        return "(-";
    }
}
CommonGuy
fuente
2

Acaparador

package players;

import java.util.ArrayList;
import java.util.List;

import controller.*;

public class Hoarder extends Player{
    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        Card[] hand = getHand();
        List<Card> ret =  new ArrayList<Card>();
    if( canWinHonestly(card) ) { //Hoarded enough cards that I won't have to bs ever again, time to win.
      for (Card c : hand) {
            if (c.getNumber() == card) {
                ret.add(c);
            }
        }
    }
    else { // Don't have the cards I'll need in the future. Play my entire hand. Either get more cards or instantly win.
      for (Card c : hand) {
                ret.add(c);
      }
    }
        return ret;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards, Controller controller) {
    //Don't call unless I have to, don't want to lose a random card
        return (player.handSize() <= numberOfCards);
    }

  @Override
    public String toString() {
        return "Hoarder";
    }

  private boolean canWinHonestly(int card) {
    Card[] hand = getHand();
    List<Integer> remainingCards = new ArrayList<Integer>();
    for (Card c : hand) {
      remainingCards.add(c.getNumber());
    }
    while( remainingCards.size() > 0 ) {
      if(remainingCards.contains(card)) {
        remainingCards.remove((Integer) card);
        card = (card + 4) % 13;
      }
      else {
        return false;
      }
    }
    return true;
  }

}

Estrategia muy simple, recoge cartas hasta que pueda ir en una racha de honestidad y ganar. No pude probarlo, espero que mi Java no esté demasiado oxidado.

histocrat
fuente
remainingCards.remove(card)debe tener una conversión a Integer, de lo contrario, Java cree que está llamando .remove(int), que se elimina por índice.
es1024
2

LiePlayer

Establece al menos 2 cartas, incluso si eso significa estirar la verdad.

package players;

import java.util.ArrayList;
import java.util.List;

import controller.*;

public class LiePlayer extends Player {

    @Override
    protected List<Card> requestCards(int card, Controller controller) {
        Card[] hand = getHand();
        List<Card> ret =  new ArrayList<Card>();
        for (Card c : hand) {
            if (c.getNumber() == card) {
                ret.add(c);
            }
        }
        int i=0;
        while(ret.size()<2 && i<cards.length){
            if(c.getNumber() != card){
               ret.add(hand[i])
            }
            i++;
        }
        return ret;
    }

    @Override
    protected boolean bs(Player player, int card, int numberOfCards, Controller controller) {
        Card[] hand = getHand();
        int myCards = 0;//How meny of that card do I have.
        for (Card c : hand) {
            if (c.getNumber() == card) {
                myCards += 1;
            }
        }
        return numberOfCards+myCards >= 4;
        //for that to work, he would have to have all the other cards of that number.
    }

    public String toString() {
        //Why would we admit to lying?
        return "Truthful Player";
    }
}
MegaTom
fuente
1
Card[] hand = getHand();se necesita en la parte superior de bs(..)( Player.handes privado). Además, esto se bloquea si tienes menos de 2 cartas en tu mano.
es1024
Desafortunadamente, su código tiene algunos errores: las tarjetas no están definidas en i<cards.length; la mano no está definida en Card c : hand. Y a veces entra en un bucle infinito porque no lo haces ++ien el bucle. Añadiría estos, pero no estoy seguro de que los quiera exactamente así.
soktinpk