计算扑克股票的蒙特卡罗方法

时间:2014-01-09 08:44:37

标签: c random montecarlo poker

我开始自己学习C语言,并决定建立一个计算扑克股票的程序。我正在尝试用蒙特卡罗计算公平性,但结果不对。

所以这是一个例子:我拿着JsTs(黑桃杰克和黑桃10)。我有两个对手,我正在分配特定的手牌范围。第一个对手只玩AA(任何Ace-Ace口袋,总共6个不同的组合),第二个玩KK +(任何Ace-Ace或King-King口袋)。因此,计算过程首先从对手范围中随机选择一个范围(对于对手1,这总​​是AA)。然后我确定该范围内的不同组合(Ah Ac,Ah Ad,Ah As ...等)并随机选择该范围内的一个组合。所以我为这两个对手做了这个。然后我随机选择五张棋牌并评估玩家手牌和对手牌。然后我看看我是赢了还是打成平手并保持结果。

所以我这样做了1000万次,我的资产达到了20.5%,但应该是19.1%。这是我的代码的main()函数:

int main()
{
randctx rctx;
Deck[4][13];
randinit(&rctx, TRUE);
numberOfOpponents = 2;

//opponent ranges selected
rangeIsSelected[0][0]= 1;
rangeIsSelected[1][0]= 1;
rangeIsSelected[1][14]= 1;

//player cards
Player_Card_Flush[0] = 0;
Player_Card_Rank[0] = 8;
Player_Card_Flush[1] = 0;
Player_Card_Rank[1] = 9;

//insert player cards to dealt cards
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 1;
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 1;

checkForErrors(0);

if (impossibleError==1) {
    printf("Impossible to calculate equity");
    return;
}

gamesSimulated = 0;
totalTies = 0;
totalWins = 0;
int opponentToBeDealt = 0;

//let's see what ranges are selected by opponents
for (int i=0; i<numberOfOpponents; i++) {
    findSelectedRanges(i);
}

//beginning of Monte Carlo method
while (gamesSimulated<maxTrials) {
    int b = 0;
    int opponentsDealt = 0;

    //randomly select hand for opponents
    while (opponentsDealt<numberOfOpponents) {

        opponentCardsForMonteCarlo(opponentToBeDealt);

        opponentsDealt += 1;

        if (opponentsDealt==numberOfOpponents) break;
        if (opponentToBeDealt==numberOfOpponents-1) {
            opponentToBeDealt = 0;
        }
        else {
            opponentToBeDealt += 1;
        }
    }

    //randomly choose 5 board cards
    while (b<5) {

        int randomCardTag = rand(&rctx) % 52;

        randomCardFlush[b] = randomCardTag / 13;
        randomCardRank[b] = randomCardTag % 13;

        //if this card hasn't been dealt then add to dealt cards
        if (Deck[randomCardFlush[b]][randomCardRank[b]]==0) {
            Deck[randomCardFlush[b]][randomCardRank[b]] = 1;
            b++;
        }
    }

    //evaluate hands (this function also removes random opponent hands when it's done)
    calculateMonteCarloEquity();

    //remove random board cards from dealt cards
    for (int x=0; x<b; x++){
        Deck[randomCardFlush[x]][randomCardRank[x]]=0;
    }
}

因为我已经编写了自己的评估代码,我首先怀疑,但是当我使用完全相同的代码进行详尽的枚举时,我得到了正确的结果(通过PokerStove确认)。然后我开始研究我为对手处理牌的方式是否有任何偏见。这是我的输出:

对手1手 作为Ac:1665806次 如啊:1667998次 广告:1666631次 Ac Ah:1665767次 广告广告:1666595次 啊广告:1667203次

对手2手 如:Ac:833847次 如啊:833392次 广告:832396次 Ac Ah:833406次 广告广告:834542次 啊广告:833703次 Ks Kc:832585次 Ks Kh:835641次 Ks Kd:832483次 Kc Kh:833013次 Kc Kd:831558次 Kh Kd:833434次

这看起来很随机。我也看了看板卡,似乎也没有任何偏见,基本上所有的卡都被处理大约1,08xxxx万次,除了As - 271 812次,Ac - 272 856次,Ah - 271 898次,Ad - 272 062次,Ks - 815 113,Kc - 816 871,Kh - 814 955次,Kd - 814 866次,当然Js - 0次和Ts - 0次。我还尝试创建一系列undealt卡,所以我的随机板卡不会是rand(&amp; rctx)%52,但根据情况rand(&amp; rctx)%46,rand(&amp; rctx)%45等(基本上我只选择undealt卡)。然而,这并没有太大改变结果。

我正在使用ISAAC随机数生成器,尽管我使用内置的rand()函数几乎得到了相同的结果。我试图像时间randinit(&rctx, time(NULL))一样播种它,但最终结果没有多大差异。是的,我知道时间对于加密目的而言是一个糟糕的种子,但对于像这样的模拟应该没问题。

所以我的想法已经用完了,也许有人可以看到我失踪的东西?

修改 的 这是我的权益计算代码

void calculateMonteCarloEquity() {
opponentsBeaten = 0;
opponentsTied = 0;
opponentsLost = 0;

//remove all opponent cards from dealt cards, because we need 7 dealt cards to evaluate hand
for (int x=0; x<numberOfOpponents; x++) {
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 0;
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 0;
}

//at this point we have 5 board cards and 2 player cards left in dealt cards
//so let's evaluate that hand
Evaluate_Hand();
playerHandScore = Hand_Score;

//now remove player hand form dealt cards
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 0;
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 0;

//let's evaluate opponent hands and save their scores
for (int x=0; x<numberOfOpponents; x++) {

    //insert opponent x hand to dealt cards
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 1;
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 1;

    Evaluate_Hand();
    opponentHandScore[x] = Hand_Score;

    //remove opponent x hand from dealt cards when evaluated
    Deck[opponentCardFlush[x][0]][opponentCardRank[x][0]] = 0;
    Deck[opponentCardFlush[x][1]][opponentCardRank[x][1]] = 0;
}

//compare  opponent hand scores with player hand score
for (int x=0; x<numberOfOpponents; x++) {

    if (playerHandScore > opponentHandScore[x]) {
        opponentsBeaten += 1;
        continue;
    }
    else if (playerHandScore == opponentHandScore[x]) {
        opponentsTied += 1;
        continue;
    }
    else if (playerHandScore < opponentHandScore[x]) {
        opponentsLost += 1;
        continue;
    }
}

//if player beats all opponents he wins the hand
if (opponentsBeaten==numberOfOpponents) {
    totalWins += 1;
}
//if player doesn't beat all the opponents, but neither loses to anyone, there must be a tie
if (opponentsLost==0 && opponentsBeaten!=numberOfOpponents) {
    totalTies += 1/(opponentsTied+1);
}

//one game has been evaluated
gamesSimulated += 1;
playerEquity = (totalWins+totalTies)/gamesSimulated;

//let's insert player cards back to dealt cards
Deck[Player_Card_Flush[0]][Player_Card_Rank[0]] = 1;
Deck[Player_Card_Flush[1]][Player_Card_Rank[1]] = 1;

if (gamesSimulated>=maxTrials) return;
}

2 个答案:

答案 0 :(得分:4)

你为对手2指定了AA和KK的概率相等。

在现实生活中,AA KK比AA AA更可能。 KK有6个与KK组合,只有一个与AA组合。

答案 1 :(得分:0)

我不知道这是否是这个特定应用的问题,但这是使用蒙特卡罗方法使用RNG的一种不好的方法:

int randomCardTag = rand(&rctx) % 52;

因为两个原因:

  1. 低位不是非常随机 - 使用模数运算符,您将丢弃所有好的高位。随机性主要是通过大多数生成器中的携带来创建的(当然,在大多数(所有?)实现中,LCG都是rand())。

  2. 不同randomCardTag的状态数不同:RNG有RAND_MAX + 1个状态,除RAND_MAX + 1是52的倍数外,一些randomCardTag还有1个状态。这是一个很小的差异,大约为1 /(RAND_MAX / 52),但是这个发电机的RAND_MAX通常不大。

  3. 使用它的正确方法应该是划分:

    int randomCardTag = rand(&rctx) / (RAND_MAX/52);
    

    请注意,您必须以这种方式使用它,因此尽管存在提醒,但每个randomCardTag的RNG状态数相同。但是有一些状态导致randomCardTag = 52,所以:

    int randomCardTag;
    do {
      randomCardTag = rand(&rctx) / (RAND_MAX/52);
    } while (randomCardTag > 51);
    

    我会使用Mersenne-Twister RNG,但是为了确保你没有遇到与rand()的某些相关性。

    如果您在修复后仍然遇到问题,那么其他地方还有其他问题。