Let's play POKE!

7

3

Poke is a really simple incomplete-information game. It's like "Poker", but simpler. At the start of every round, each player is dealt a hand. To simplify things, a hand is a uniformly distributed random number between 0 and 1. Each player only knows their own hand. The player that wins the round is the one whose hand has the greatest value.

Every turn, all the players who are still in the round make a bet that they're the player with the best hand. Each player has to decide whether or not to match the largest bet. If they match it, that amount is added to the pot from their account. If they choose not to match it, they drop out of the round. Even if they had the best hand, they can't win once they drop out, and they lose everything they had in the pot. At the end of the round, the player with the best hand out of those that haven't dropped wins. (Each player knows how many turns are gonna be in the round since the start of the round).

The winner is the one that makes the most money after a large number of rounds (hundreds, or thousands). In order to compete, write a struct that inherits from this "player" class:

class player
{
private:
    double _hand;
    game* _owner = 0;
public:
    //You must provide
    bool set_new_owner(game* current, game* new_owner) {
    if(current == _owner) { _owner = new_owner; return true; }
    return false;
    }
    bool set_new_hand(game* current, double new_hand) {
    if(current == _owner) { _hand = new_hand; return true; }
    return false;
    }
    double hand() const { return _hand; }
    virtual double bet(const round_info&) = 0;
    virtual bool take_bet(const round_info&) = 0;
    virtual std::string name() { return typeid(*this).name(); }
    virtual ~player() {}
};

All you need to do is specialize the "bet" function (which determines how much your player bets each turn), and the "take_bet" function (which determines whether or not to take the bet. Here are some examples:

struct grog : player
{
    //Always bets the minimum amount
    double bet(const round_info&) { return 1.0; }
    //Takes any bet
    bool take_bet(const round_info&) { return true; }
    string name() { return "Grog"; }
};
struct bob : player
{
    //Bet the minimum amount
    double bet(const round_info&) { return 1.0; }
    //Only take bets when you have a good hand
    bool take_bet(const round_info&) { return hand() > 0.95; }
    string name() { return "Bob"; }
};
struct james : player
{
    //Raise the bet a lot if you have a *really* good hand
    double bet(const round_info&) {
        if(hand() > 0.99) return 1000.0;
        else return 1.0; 
    }
    //Only take bets when you have a *really* good hand
    bool take_bet(const round_info&) { return hand() > 0.99; }
    string name() { return "James"; }
};
struct rainman : player
{
    //Play it safe
    double bet(const round_info&) { return 1.0; }
    bool take_bet(const round_info& r)
    {
        double cost = r.current_max_bet();
        double winnings = r.winnings() + r.current_max_bet() * r.n_active_players();
        int players = r.n_players();
        double hand = this->hand();
        double p_win = pow(hand,players - 1);
        double p_loss = 1 - p_win;
        double gain = -(cost * p_loss) + p_win * winnings;
        if(gain > 0) return true;
        return false;
    }
    string name() { return "Rainman"; }
};

If all four people are playing, then over a few thousand games, James will be the winner and Rainman will do worse than anyone but Grog. But if only Bob, Grog, and Rainman are playing, Rainman will clean the floor with the other players (and win tons of money).

Edit: Here is the code for round_info and for the class that will manage the game. https://github.com/Artifexian/PokeGame

Edit 2: If you submit something not in C++, I can try to translate it into C++ for you. If you want me to, I will edit your answer and append my translated version.

Edit 3: I will work to write a wrapper for non-c++ languages. Your solution must include 2 functions: something to determine how much to bet (which should return a floating point value), and something to determine whether or not to stay in the round (which should return true or false). Contact me about your preferring input format. I will write something to translate "round_info" into text. The data provided as text will be the same data provided to everyone else. It will include:

  1. The current turn number
  2. The maximum number of turns (when the round ends)
  3. The number of players
  4. The number of players who haven't dropped out
  5. The history of what each player has bet
  6. A list of which players haven't dropped out
  7. The size of the pot
  8. The current maximum bet amount (what you have to put down to stay in the game)
  9. Your hand

Edit 4: Any bets made by players will be clipped to be between 1 and 10^6

J. Antonio Perez

Posted 2017-05-23T23:42:41.727

Reputation: 1 480

2Could you make this not exclusive to C++? I know C++ can run system shell commands, could you change this to allow any programs that output to STDOUT, and take input from STDIN? I am interested in this challenge, but I only know javascript and node.js. – programmer5000 – 2017-05-24T00:54:35.883

Write something in Javascript, and I'll translate it into it into C++ :) – J. Antonio Perez – 2017-05-24T01:06:04.070

what about other languages? java? python? – Quintec – 2017-05-24T01:11:28.073

I know java and python, so I can also translate for those. Java is exceedingly similar to c++, and python is close enough. – J. Antonio Perez – 2017-05-24T01:15:44.737

2Please do consider writing a wrapper, this challenge would be incredibly interesting if it was open to anyone. – Rohan Jhunjhunwala – 2017-05-24T01:32:38.880

Is the random number [0-1] uniform or gaussian? – Draco18s no longer trusts SE – 2017-05-25T20:41:01.840

It's Uniform! Sorry! – J. Antonio Perez – 2017-05-25T20:41:26.873

If I understood it correctly, you begin with 0 points and can bet any amout you want. So you could get negative without any problem. – Masclins – 2017-06-08T15:23:08.687

ping @Poke ? Are you there? – sergiol – 2017-07-01T21:55:58.100

Answers

4

Frequent Folder

The more players there are, the lower the odds of winning. Thus, this player only takes bets when he thinks his hand is high, using the starting number of players as a guideline.

struct frequent_folder : player
{   
    //Bets fairly heavily under same conditions a bet is taken
    //Bet varies based on how good hand is
    double bet(const round_info& r) {
        double numer = hand() - (1 - (1/r.n_players)), denom = 1 - (1 - (1/r.n_players));
        double qty = 250 * (numer/denom);
        return qty > 1 ? qty : 1.0;
    }

    //Take bet only if likely to have highest hand
    //e.g. 2 players -> 0.5+, 3 players -> 0.67+, 4 players -> 0.75+
    bool take_bet(const round_info& r) { return(hand() >= 1 - (1/r.n_players())); }

    string name() { return "Frequent Folder"; }
};

Bluffer

Where the Frequent Folder plays a very conservative strategy, the Bluffer lives on the edge. He bets heavily with a hand he thinks can win... or a hand he knows will lose. He takes favorable bets more often than he bluffs, hoping this will mean net gain, but the probability of a bluff is high enough that strategies analyzing previous bets may be thrown off by the Bluffer's erratic behavior.

struct bluffer : player
{   
    //Bets hard with an excellent hand... or a very poor one!  Small bet otherwise
    double bet(const round_info&) { (return hand() > 0.9 || hand() < 0.05) ? 500.0 : 1.0; }

    //Take bet with an excellent hand... or a very poor one!
    bool take_bet(const round_info& r) { return(hand() > 0.9 || hand() < 0.05; }

    string name() { return "Bluffer"; }
};

These are my first entries on KotH as well as SE as a whole. I hope everyone finds them up to snuff.

MSet

Posted 2017-05-23T23:42:41.727

Reputation: 141

3

Gambling Russian

Simulates a person who is also playing Russian Roulette. There is a 1 in 6 chance he dies playing Russian Roulette, thus he will stop making bets once he has died. Every time he survives he gets so excited he bets as much as he possibly can because he believes he is on a good luck streak. If there is something smelly about my code let me know.

struct gambling_russian : player
{
  bool alive = true;

  double bet(const round_info&) { return DBL_MAX; };

  bool take_bet(const round_info&)
  {
    if(hand < (1.0/6.0)){
      alive = false;
    }
    return alive;
  }

  string name() { return "Gambling Russian"; }
}

TitusLucretius

Posted 2017-05-23T23:42:41.727

Reputation: 121

1

I'll get this started with a simple player, who will randomly take bets based on the quality of his hand.

struct ronald : player
{
  double bet(const round_info&) { return fmax( 1.0, hand()*2 ); };

  bool take_bet(const round_info&)
  {
    double p = (double)rand() / RAND_MAX;
    return hand()>p;
  }

  string name() { return "Ronald"; }
}

KSmarts

Posted 2017-05-23T23:42:41.727

Reputation: 1 830

1

The Thinker

struct theThinker: player
{
    //Raise the bet depending on hand value, but less if the pot has a lot already
    double bet(const round_info&) {
        double b = hand() * hand() * 1000.0 - round_info.winnings() / 10.0;
        return b > 1.0 ? b : 1.0;
    }
    //Take bets when the pot and hand look better than the amount you may loose
    bool take_bet(const round_info&) {
        return hand() * (round_info.winnings() + 1000.0) > current_max_bet();
    }
    string name() { return "The Thinker"; }
};

MegaTom

Posted 2017-05-23T23:42:41.727

Reputation: 3 787