Dice odds: Simulating a game of Craps

2019-04-20 18:07发布

问题:

My brother turns 21 in a couple of weeks and my parents and I are taking him to Las Vegas. For my 21st, I brought $200 to gamble in Vegas and came home with around $450, mostly from playing craps. I plan on bringing $200 again for this trip and before I go I thought I'd run some craps simulations to see if I can double my money again.

I've read from several sources that the house has the smallest advantage in craps when placing a passline bet with maximum odds. From my memory, and as surveyed by Wizard of Odds, most casinos on the Strip are 3-4-5 odds with a $5 minimum. Taking this into account, here is a simulation of a craps session (of 100 dice rolls) in PHP:

<?php

$stash = 200;
$bet = 5;

for($i=100; $i--;) {

    $dice1 = mt_rand(1, 6);
    $dice2 = mt_rand(1, 6);
    $total = $dice1 + $dice2;

    if(!$button) {
        if($total===7 || $total===11) {
            $stash += $bet;
        }
        elseif($total===2 || $total===3 || $total===12) {
            $stash -= $bet;
        }
        else {
            $button = $total;
            if($total===4 || $total===10) {
                $odds = $bet*3;
            }
            elseif($total===5 || $total===9) {
                $odds = $bet*4;
            }
            elseif($total===6 || $total===8) {
                $odds = $bet*5;
            }
        }
    }
    else {
        if($total===7) {
            $button = 0;
            $stash -= ($bet + $odds);
        }
        elseif($total===$button) {
            $button = 0;
            $stash += $bet;
            if($total===4 || $total===10) {
                $stash += $odds*2/1;
            }
            elseif($total===5 || $total===9) {
                $stash += $odds*3/2;
            }
            elseif($total===6 || $total===8) {
                $stash += $odds*6/5;
            }
        }
    }

    echo 'Stash: $'.$stash.'<br/>';

}

?>

Is there anything wrong with my math here? While there are peaks and troughs throughout each session, this simulation more often doubles its money before going broke. Considering the house always has the edge in craps, even if it's just a fraction of a percent, I'm perplexed by this result.

回答1:

Well, right off the bat, I can see that you've got an error in the simple 7 or 11 win case: You're supposed to win your bet, not twice your bet.

Edit: I believe the payout for the odds bet is commensurate with the actual probability. You are twice as likely to roll 7 (lose your odds) than 10, so you should get paid 2:1 when you win on a 4 or 10; and only paid 6:5 when you win on a 6 or 8.



回答2:

I'd be careful with any piece of code written to "prove" you're more likely to double your money at craps (or any other game of chance) before you go broke. Las Vegas is a sprawling city in the desert of Nevada as a testament to two things:

  1. Eventually, the house always wins
  2. People are bad at math

There is no game any casino would put on their floor that didn't leverage both rules. If your code disagrees with Vegas, I'm putting my money on Vegas.

Update:

Here is some C++ I wrote based on your original code. The original problem you posted was if you could double your money before you went broke more often than not. I followed up the code I wrote with some results.

#include <iostream>

int die_roll()
{
    return std::rand() % 6 + 1;
}

int win_count_g(0);
int loss_count_g(0);

// return true when double our money.
// return false when we can't bet anymore.
bool test_loop(int cash)
{
    static const int bet_k(5);

    int goal(cash * 2);
    int button(0);

    while (true)
    {
        if (cash >= goal)
            return true;
        else if (cash < bet_k)
            return false;

        int roll(die_roll() + die_roll());
        int odds(0); // additional odds bet

        if (button == 0)
        {
            if (roll == 7 || roll == 11)
            {
                ++win_count_g;
                cash += bet_k;
            }
            else if (roll == 2 || roll == 3 || roll == 12)
            {
                ++loss_count_g;
                cash -= bet_k;
            }
            else
            {
                button = roll;

                if (roll == 4 || roll == 10)
                {
                    odds = std::min(cash - bet_k, bet_k * 3);
                }
                else if (roll == 5 || roll == 9)
                {
                    odds = std::min(cash - bet_k, bet_k * 4);
                }
                else // if (roll == 6 || roll == 8)
                {
                    odds = std::min(cash - bet_k, bet_k * 5);
                }
            }
        }
        else
        {
            if (roll == 7)
            {
                ++loss_count_g;
                button = 0;
                cash -= bet_k + odds;
            }
            else if (roll == button)
            {
                ++win_count_g;
                button = 0;
                cash += bet_k;

                if (roll == 4 || roll == 10)
                {
                    cash += odds * 2;
                }
                else if (roll == 5 || roll == 9)
                {
                    cash += odds * 3 / 2;
                }
                else // if (roll == 6 || roll == 8)
                {
                    cash += odds * 6 / 5;
                }
            }
        }
    }
}

void test(int cash)
{
    win_count_g = 0;
    loss_count_g = 0;

    int doubled(0);
    int broke(0);

    for (int i(0); i < 10000; ++i)
        if (test_loop(cash))
            ++doubled;
        else
            ++broke;

    float win_percentage(static_cast<float>(doubled) / (doubled + broke) * 100.0);

    std::cout << "starting cash: $" << cash
              << "; doubled: " << doubled
              << "; broke: " << broke
              << " (" << win_percentage << "% win)"
              << "; loop wins: " << win_count_g
              << "; loop losses: " << loss_count_g
              << std::endl;
}

int main ()
{
    static const int cash_set_k[] =
    {
        5,
        10,
        20,
        50,
        100,
        200,
        400,
        800,
        1000
    };
    static const int cash_set_size_k(sizeof(cash_set_k) / sizeof(cash_set_k[0]));

    std::for_each(&cash_set_k[0], &cash_set_k[cash_set_size_k], &test);

    return 0;
}

Results:

starting cash: $5; doubled: 4944; broke: 5056 (49.44% win); loop wins: 4944; loop losses: 5056
starting cash: $10; doubled: 4862; broke: 5138 (48.62% win); loop wins: 19706; loop losses: 20258
starting cash: $20; doubled: 4755; broke: 5245 (47.55% win); loop wins: 78360; loop losses: 80320
starting cash: $50; doubled: 4345; broke: 5655 (43.45% win); loop wins: 489406; loop losses: 502506
starting cash: $100; doubled: 3553; broke: 6447 (35.53% win); loop wins: 1914393; loop losses: 1972273
starting cash: $200; doubled: 2468; broke: 7532 (24.68% win); loop wins: 7172464; loop losses: 7375024
starting cash: $400; doubled: 861; broke: 9139 (8.61% win); loop wins: 22615369; loop losses: 23277609
starting cash: $800; doubled: 112; broke: 9888 (1.12% win); loop wins: 54556881; loop losses: 56121041
starting cash: $1000; doubled: 31; broke: 9969 (0.31% win); loop wins: 69308617; loop losses: 71296217


回答3:

You're not checking to see if you have enough left in the stash to place your desired odds bet. In fact, you're not checking the size of your stash at all. It's unsurprising that this simulation will be able to beat the house more often if you are able to bet even if your stash size is negative.

By the way, I ran 50,000 iterations of your simulation of 100 dice throws (with my modification of maximum bet of stash remaining) and came up with the following:

wins: 23807

losses: 25465

push (you leave with $200): 728

avg. winnings: $109.07