นั่นคือ BS! (เกมการ์ด)


29

BSเป็นเกมไพ่ที่เป้าหมายของเกมคือสูญเสียไพ่ทั้งหมดของคุณ

เกมประกอบด้วยผู้เล่นสี่คนและสำรับไพ่ 52 ใบ ผู้เล่นแต่ละคนจะสุ่มไพ่ 13 ใบ โดยปกติการ์ดจะมีป้ายกำกับ 2 - 10, Ace, Jack, Queen, King แต่เพื่อความง่ายบัตรจะมีหมายเลขตั้งแต่ 0 - 12 แม้ว่าจำนวนไพ่ในมือของผู้เล่นจะเป็นข้อมูลสาธารณะ แต่ผู้เล่นเท่านั้นที่รู้ว่าการ์ดใดที่อยู่ในมือของเขา

เกมไปดังนี้ตำแหน่งผู้เล่นคนแรกเป็นบัตรจำนวนมากที่มีป้ายกำกับ0ในขณะที่เขาต้องการที่จะอยู่ในกองทิ้ง (หมายเหตุว่าเขาไม่จำเป็นต้องเล่นทั้งหมดของบัตรของเขาที่มีป้ายกำกับ0แต่มักจะอยู่ในความสนใจที่ดีที่สุดของเขาที่จะทำเช่นนั้น ) เขาต้องเล่นไพ่อย่างน้อยหนึ่งใบ ผู้เล่นคนที่สองเล่นไพ่ให้มากที่สุดเท่าที่เขาต้องการจะติดป้าย1ผู้เล่นคนที่สามเล่น2และต่อไปเรื่อย ๆ หลังจาก 12 มันจะรีเซ็ตเป็น 0

จะเกิดอะไรขึ้นถ้าคุณไม่มีการ์ดใด ๆ ที่คุณควรเล่น จำไว้ว่าคุณต้องเล่นไพ่อย่างน้อยหนึ่งใบ - ในความเป็นจริงคุณสามารถเล่นไพ่ใด ๆ ที่คุณต้องการ! (ที่จริงแล้วแม้ว่าคุณจะมีการ์ดที่ถูกต้องคุณสามารถโกหกและเล่นการ์ดอื่น) อย่างไรก็ตามบางคนสามารถโทรหาคุณและพูดว่า "BS!" หากคนนั้นถูกต้องและคุณโกหกคุณต้องนำไพ่ทั้งหมดไปทิ้งในกอง เป็นรางวัลผู้เล่นที่เรียกคุณออกมาวางไพ่ใบใดใบหนึ่งในกองทิ้ง หากผู้กล่าวหาผิดเขาจะต้องนำไพ่ทั้งหมดไปทิ้งในกอง โปรดทราบว่าคุณไม่สามารถโกหกจำนวนไพ่ที่คุณเล่น

ข้อมูลรายละเอียดเพิ่มเติม:

  • ในตอนต้นของเกมผู้เล่นสุ่มสี่คนได้รับเลือกให้เล่น เนื่องจากจะมีอย่างน้อย 1,000 เกมผู้เล่นแต่ละคนจะได้รับโอกาสในการเล่น ลำดับเทิร์นจะถูกสุ่มเลือกในช่วงเริ่มต้นของเกม
  • หากคุณส่งคืนบัตรที่ถูกต้องหนึ่งใบและบัตรที่ไม่ถูกต้องหนึ่งใบก็ถือว่าเป็นการโกหก (เช่นถ้าคุณควรให้2วินาทีและคุณให้1และ1 1นั่นก็โกหก)
  • หากผู้เล่นสองคนขึ้นไปพูด BS ในเวลาเดียวกันจะมีการสุ่มเลือกหนึ่งคน
  • คะแนนของคุณคือเปอร์เซ็นต์ของเกมที่คุณชนะ
  • มีรอบสูงสุด 1,000 รอบโดยหนึ่งรอบคือผู้เล่นทุกคนไปครั้งเดียว โดยปกติแล้วมีคนชนะก่อนหน้านี้ หากไม่มีใครชนะมันจะถูกนับรวมกับจำนวนเกมทั้งหมดที่เล่น แต่ไม่มีใครชนะ

ข้อมูลจำเพาะ:

Playerคุณควรเขียนชั้นเรียนซึ่งทอดตัว มันจะมีลักษณะ:

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

วิธีการที่requestCardsเรียกว่าเมื่อถึงตาคุณ อาร์กิวเมนต์cardคือหมายเลขบัตรที่คุณควรระบุ คุณส่งคืนรายการการ์ดที่คุณต้องการใส่ในกองทิ้ง ผู้เล่นด้านบนตรวจสอบเพื่อดูว่าเขามีไพ่ประเภทใด ๆ ที่ร้องขอ; ถ้าไม่เขาเล่นไพ่ใบแรกของเขาและหวังว่าจะไม่มีใครเช็ค

วิธีการที่bsเรียกว่าเมื่อใดก็ตามที่คนอื่นเล่นไพ่ อาร์กิวเมนต์แรกคือผู้เล่นที่สอง - ไพ่ที่เขาควรจะเล่นและที่สาม - จำนวนของการ์ดชนิดนั้นที่เขาอ้างว่าเขาได้เล่น ส่งคืนtrueหากคุณต้องการโทรหา "BS" ในรหัสด้านบนผู้เล่นเรียกเฉพาะ "BS" เมื่อผู้เล่นคนอื่นอ้างว่ามีไพ่ 3 ใบหรือมากกว่าในประเภทที่ร้องขอ

อาร์กิวเมนต์สุดท้ายสำหรับทั้งสองวิธีคือcontrollerซึ่งเป็นเพียงตัวควบคุมที่ควบคุมเกม จากคอนโทรลเลอร์คุณสามารถรับข้อมูลสาธารณะเพิ่มเติมเช่นจำนวนการ์ดในกองทิ้งหรือรายชื่อและลำดับการเปิดของผู้เล่น

toStringวิธีการเป็นตัวเลือก

Conroller บน GitHub: https://github.com/prakol16/bs

หากคุณต้องการโพสต์โซลูชันที่ไม่ใช่จาวาคุณสามารถใช้ส่วนต่อประสานที่ให้ไว้ในhttps://github.com/LegionMammal978/bs (เครดิตไปที่ LegionMammal978) และฉันจะพยายามรวมเข้าด้วยกัน

กระดานคะแนนจนถึง:

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 ชนะ แต่ CalculatingLiar อยู่ในไม่กี่วินาที คะแนนเหล่านี้ดูเหมือนจะสอดคล้องกัน - พวกเขาค่อนข้างเหมือนกันทุกครั้ง


13
คุณต้องล้อเล่นฉัน ฉันมีผู้ควบคุมเกือบเสร็จสิ้นสำหรับ BS ที่อยู่โดยรอบเพื่อจุดประสงค์เดียวในการสร้างความท้าทายที่แน่นอนนี้ ฉันเดาว่าฉันต้องหาวิธีอื่นเพื่อใช้เวลาของฉันตอนนี้
IchBinKeinBaum

3
อาจเป็นการดีที่จะไม่เปิดเผยController.toString()ต่อสาธารณะเนื่องจากจะส่งคืนมือของผู้เล่นทุกคนและกองทิ้ง
es1024

@IchBinKeinBaum หากคอนโทรลเลอร์ของคุณสามารถสื่อสารกับ STDIN / STDOUT คุณอาจลองเผยแพร่ความท้าทายกับคอนโทรลเลอร์ของคุณสำหรับผู้ที่ไม่ใช่ชาว Java
Logic Knight

@CarpetPython: มัน นอกจากนี้ยังใช้กฎที่แตกต่างกันเล็กน้อย หากไม่นับเป็นซ้ำฉันจะ
IchBinKeinBaum

ฉันเพิ่งสร้างคอนโทรลเลอร์หลายภาษาเสร็จแล้ว การใช้งานอยู่ใน Program.cs คุณสามารถค้นหาได้ที่นี่
LegionMammal978

คำตอบ:


10

นักโทษชาย

ConMan เฝ้าดูไพ่ทุกใบที่ต้องผ่านมือของเขาเรียก BS เมื่อเล่นไม่ได้เนื่องจากตำแหน่งของไพ่

แสดงความจริงเมื่อทำได้ แต่ใช้วิธีสุดท้ายในการใช้การ์ดใบสุดท้ายหากมีชัยชนะเกิดขึ้น

ฉันใช้เวลานานในการปรับเทคนิคเพื่อเรียก BS เมื่อความน่าจะเป็นสูงที่ฝ่ายตรงข้ามโกหกหรือเมื่อเรียก BS เป็นประโยชน์ (เช่นการรับการ์ดที่มีประโยชน์จากกองทิ้ง) แต่ในทางปฏิบัติไม่เรียก BS เลยด้วยวิธีอื่น ฉันคะแนนมากที่สุด

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

    }
}

งานที่ดี. บอทนี้ชนะเกมได้อย่างง่ายดายจนถึงทุกวันนี้
soktinpk

ขอขอบคุณ! เมื่อมองดูรหัสฉันรู้ว่าฉันลืมโทรหาบีเอสเมื่อการ์ดบอกว่าเป็นไปไม่ได้เลย ฉันได้อัปเดตโค้ดด้านบนแล้ว
Wasmoo

4

ผู้เล่น 3131961357_10

เลือกผู้เล่นสุ่มในแต่ละเกมและเรียก BS กับผู้เล่นคนนั้นเสมอ

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

4

Truthy

ยังไม่เสร็จเนื่องจากฉันไม่รู้วิธีบอกผลลัพธ์ของการโทร BS (หากพวกเขาเอาเสาเข็มหรือคนอื่นในกรณีที่ผูกหรือฉันทำ)

ในขณะนี้โทร BS เท่านั้นหากฉันสามารถพิสูจน์ได้ อย่าโกหกเว้นแต่ฉันจะต้องทำ ฉันจำเป็นต้องปรับปรุงอัลกอริทึมการโกหก ฉันพยายามทำให้มันใกล้เคียงที่สุดกับวิธีที่ฉันเล่น BS กับผู้เล่นคนอื่น ๆ (ลบด้วยการวางไพ่พิเศษใต้เพื่อเล่น 5 หรือ 6 โดยที่พวกเขาไม่รู้)

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

1
คุณสามารถติดตามไพ่ที่เล่นได้
seequ

ฉันไม่แน่ใจว่าคุณกำลังพยายามทำอะไรcards = cards.get(0)อยู่ cardsคือรายการเพื่อให้คุณไม่สามารถกำหนดได้ไปCard List<Card>คุณกำลังพยายามลบทุกอย่างยกเว้นองค์ประกอบแรกหรือไม่
soktinpk

ใช่แก้ไขมัน
mbomb007

ฉันพบผลลัพธ์ของ BS โดยจดจำขนาดมือของผู้เล่นแต่ละคนแล้วเปรียบเทียบหน่วยความจำของฉันกับสิ่งที่คอนโทรลเลอร์มี การเพิ่มขึ้นหมายถึงผู้เล่นสูญเสีย การลดลงหมายถึงผู้เล่นที่ชนะ (ขนาดมือของผู้เล่นปัจจุบันจะต้องถูกชดเชยด้วยnumberOfCardsเพราะพวกเขาถูกยกเลิกไปแล้วเมื่อbsถูกเรียก)
Wasmoo

ฉันไม่รู้ว่าฉันจะมีเวลาใช้สิ่งที่ฉันต้องการหรือไม่ ยิ่งฉันคิดถึงวิธีการเล่น BS อย่างเหมาะสมที่สุดเท่าไหร่การเขียนโปรแกรมก็จะยิ่งมากขึ้นเท่านั้น ฉันอยากจะทำในสิ่งที่ ConMan ทำได้บ้าง แต่ความซับซ้อนนั้นค่อนข้างมากสำหรับฉัน
mbomb007

4

CalculatingLiar

อันนี้พยายามเล่นจริง ถ้าเขาโกหกเขาใช้การ์ดที่เขาจะไม่ใช้ในอนาคตอันใกล้ นอกจากนี้ยังพยายามที่จะชนะรางเรียก BS ในผู้เล่นคนอื่นเนื่องจากไพ่ใบสุดท้ายแทบจะไม่เหมาะ

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

2

ผู้สะสม

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

}

กลยุทธ์ที่ง่ายมากรวบรวมการ์ดจนกว่าจะสามารถไปในแนวความซื่อสัตย์และชนะ ไม่สามารถทดสอบได้หวังว่า Java ของฉันจะไม่เป็นสนิมเกินไป


remainingCards.remove(card)ควรมีการส่งไปที่Integerมิฉะนั้น Java คิดว่าคุณกำลังโทร.remove(int)ซึ่งจะถูกลบออกโดยดัชนี
es1024

2

LiePlayer

วางไพ่อย่างน้อย 2 ใบแม้ว่านั่นหมายถึงการยืดความจริง

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

1
Card[] hand = getHand();เป็นสิ่งจำเป็นที่ด้านบนของbs(..)( Player.handเป็นส่วนตัว) นอกจากนี้ยังเกิดปัญหาถ้าคุณมีไพ่ในมือน้อยกว่า 2 ใบ
es1024

น่าเสียดายที่รหัสของคุณมีข้อผิดพลาด: การ์ดไม่ได้ถูกกำหนดไว้ที่i<cards.length; Card c : handมือไม่ได้กำหนดไว้ใน และบางครั้งมันก็เข้าสู่วงวนไม่สิ้นสุดเพราะคุณไม่ได้++iวนรอบ ฉันจะเพิ่มสิ่งเหล่านี้ แต่ฉันไม่แน่ใจว่าเป็นวิธีที่คุณต้องการอย่างแน่นอน
soktinpk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.