iconEuler Home

Most Simple Poker Game

Game with two cards 1 and 2 and two players P1 and P2. Rules:

First we make a function, which computes the outcome of the game for specific cards and tactics.

>function play (c1,c2,t1,t2) ...
 ## compute outcome of game.
 ## c1,c2 - card of first and second player (1,2)
 ## t1,t2 - tactic of first and second player
 ##   (e.g. [0,1] means pass with 1, set with 2)
 ## result: gain as seen from first player
 
 if t1[c1]==0 then return -1
 elseif t2[c2]==0 then return 1
 else
    if c1>c2 then return 2
    elseif c1<c2 then return -2
    else return 0
    endif
 endif
 endfunction

Example: If P1 gets card 2, P2 gets card 1, both have the tactic to pass with card 1 and hold with card 2, then P1 will win 1.

>play(2,1,[0,1],[0,1])
1

We need the matrix of possible tactics.

>T=[0,0;0,1;1,0;1,1]
            0             0 
            0             1 
            1             0 
            1             1 

It is obvious that not all of these tactics are wise. E.g., it is easy to see that passing with card 2 is always inferior to bidding with card 2. For the time being, we simply ignore such considerations.

Now we loop over all tactics for both players and determine the expected result of the game in a matrix. The column index belongs to the first player. It is the row of T he selects for his tactic.

Example: Both players have the unreasonable tactic [1,0]. Then

On average, P1 looses -1/4.

>function makeR (T) ...
 ## make matrix of results
 k=rows(T);
 R=zeros(k,k);
 // loop over all tactics:
 for i1=1 to k
    for i2=1 to k
        v=0;
        // loop over all cards:
        for c1=1 to 2
            for c2=1 to 2
                v=v+play(c1,c2,T[i1],T[i2]);
            end;
        end;
        R[i2,i1]=v/4; // columns refer to first player!
    end;
 end;
 return R
 endfunction
>R=makeR(T);

Here is the matrix of game results.

>fraction R
       -1         0         0         1 
       -1      -1/4      -3/4         0 
       -1       1/4      -1/4         1 
       -1         0        -1         0 

An example is the tactic [1,0] for both players with an average loss of 1/4 for the first player.

The problem now is determine probabilities p[i] for each tactic, such that the minimal expected win is maximized. This amounts to

Most Simple Poker Game

Most Simple Poker Game

Most Simple Poker Game

Let us make the matrix for this linear problem. Variables are the p[i] and the game result G.

>A=R|-1; ...
 A=A_ones(1,4)
           -1             0             0             1            -1 
           -1         -0.25         -0.75             0            -1 
           -1          0.25         -0.25             1            -1 
           -1             0            -1             0            -1 
            1             1             1             1             0 

We have >= in first 4 rows, and = in the last.

>eq=ones(4,1)_0
            1 
            1 
            1 
            1 
            0 

Thus we reformulate

Most Simple Poker Game

>b=zeros(4,1)_1
            0 
            0 
            0 
            0 
            1 

We maximize G.

>c=zeros(1,4)|1
[0,  0,  0,  0,  1]

There is no restriction for G.

>restr=ones(1,4)|0
[1,  1,  1,  1,  0]

The result is boring. Each player should always set (tactic [1,1]).

>res=simplex(A,b,c,eq,restr,>max); res'
[0,  0,  0,  1,  0]

The game is fair.

>c.res
0

The strategy for the other player is the same.

>simplex(-A',b,c,eq,restr,>max)'
[0,  0,  0,  1,  0]

Game Function

Let us make a function of the matrix manipulations.

>function game (R) ...
 ## compute best strategy for column player.
 ## return: {p,G}
 
 m=rows(R); n=cols(R);
 M=R|-1_ones(1,n); b=zeros(m,1)_1; eq=ones(m,1)_0;
 c=zeros(1,m)|1; restr=ones(1,m)|0;
 res=simplex(M,b,c,eq,restr,>max);
 return {res[1:n]',res[-1]};
 endfunction

Here is our example so far.

>{p,G}=game(R); G, p
0
[0,  0,  0,  1]

Different Stakes

Assume, we change the rules.

The game depends on the values of A and B.

>A=1; B=2;

We have to modify the function play(). For the moment, we refer to the global values of A and B.

>function play (c1,c2,t1,t2) ...
 global A,B;
 if t1[c1]==0 then return -A
 elseif t2[c2]==0 then return A
 else
    if c1>c2 then return A+B
    elseif c1<c2 then return -(A+B)
    else return 0
    endif
 endif
 endfunction

Suddenly, the first player is in a disadvantage.

>{p,G}=game(makeR(T)); G, p,
-0.25
[0,  1,  0,  0]

Both players have to pass with 1 and hold with 2 (tactic [0,1]).

>{p,G}=game(-makeR(T)'); G, p,
0.25
[0,  1,  0,  0]
>T[2]
[0,  1]

To get a function for G depending on B, we define G(x). In the function we set the global variable B to x.

>function G(x) ...
 global B,T;
 B=x;
 {p,G}=game(makeR(T));
 return G;
 endfunction

The result is that for B/A<1 the game is fair, and for B/A>1 the first player is in a disadvantage losing A/4 on average. For B/A>2 his loss is -0.25.

>plot2d("G",0,3,xl="B",yl="G"):

Most Simple Poker Game

Up to B/A=2, the first player has to set always.

>B=1.9; {p,G}=game(makeR(T)); G, p,
-0.225
[0,  0,  0,  1]

From the matrix, it is clear that any other tactics is inferior.

>makeR(T)
           -1             0             0             1 
           -1         -0.25        -0.975        -0.225 
           -1         0.475         -0.25         1.225 
           -1         0.225        -1.225             0 

For B/A>2, the tactics must change, and the first player takes a more conservative approach, passing with 1.

>B=3; {p,G}=game(makeR(T)); G, p,
-0.25
[0,  1,  0,  0]

Three Card Values

For three card values, we can use the same algorithm with minor changes.

First, we take all possible tactics into consideration, even though some of them are clearly inferior. To compute the tactics, the following recursive function will do.

>function makeT (n) ...
 if n==1 then return (0:1)';
 else
    TL = makeT(n-1);
    return (0|TL)_(1|TL);
 endif;
 endfunction
>T=makeT(3)
            0             0             0 
            0             0             1 
            0             1             0 
            0             1             1 
            1             0             0 
            1             0             1 
            1             1             0 
            1             1             1 

We have to modify makeR() to take the value of m into consideration.

>function makeR (T,m) ...
 ## make matrix of results
 
 k=rows(T);
 R=zeros(k,k);
 for i1=1 to k
    for i2=1 to k
        v=0;
        for c1=1 to m
            for c2=1 to m
                v=v+play(c1,c2,T[i1],T[i2]);
            end;
        end;
        R[i2,i1]=v/m^2; // column belongs to first player!
    end;
 end;
 return R
 endfunction

For A=B=1, the result is to always hold with a card value of 2,3.

>A=1; B=1; {p,G}=game(makeR(T,3)); p, G,
[0,  0,  0,  1,  0,  0,  0,  0]
-0.111111111111

For A=1, B=2, things change.

>A=1; B=2; {p,G}=game(makeR(T,3)); p, G,
[0,  0,  0,  0.75,  0,  0,  0,  0.25]
-0.166666666667

The optimal strategy for B/A=2 is

Setting with a card value of 2 can be considered a moderate form of a bluff.

>T[4], T[8]
[0,  1,  1]
[1,  1,  1]
>function G(x) ...
 global T,B;
 B=x;
 {p,G}=game(makeR(T,3));
 return G
 endfunction

Surprisingly, the expected win G for the optimal tactics is a quadratic function from B/A=1 to B/A=4.

>plot2d("G",0,5,n=20):

Most Simple Poker Game

More Card Values

For more card values, we restrict to the promising tactics. We consider only the tactics "set with a card value greater or equal c".

We have to modify our play() function once more.

>function play (c1,c2,t1,t2,A,B) ...
 ## compute outcome of game.
 ## c1,c2 - card of first and second player (1,...,n)
 ## t1,t2 - tactic of first and second player
 ##   e.g. t1=c means to set with card values c,c+1,...,n
 ## result: gain as seen from first player
 
 if c1<t1 then return -A
 elseif c2<t2 then return A
 else
    if c1>c2 then return (A+B)
    elseif c1<c2 then return -(A+B)
    else return 0
    endif
 endif
 endfunction

The matrix of game results now has to use all possible tactics 1 to n, and simulate all possible card values 1 to n for both players.

>function makeR (n,A,B) ...
 ## make matrix of results
 R=zeros(n,n);
 for t1=1 to n
    for t2=1 to n
        v=0;
        for c1=1 to n
            for c2=1 to n
                v=v+play(c1,c2,t1,t2,A,B);
            end;
        end;
        R[t2,t1]=v/n^2; // column belongs to first player!
    end;
 end;
 return R
 endfunction

Let us test with the example above. We have 3 card values, A=1 and B=2.

>{p,G}=game(makeR(3,1,2)); p, G,
[0.25,  0.75,  0]
-0.166666666667

The result gets more complicated with more card values, even if A=B=1.

>{p,G}=game(makeR(10,1,1)); p, G,
[0.555556,  0,  0,  0.444444,  0,  0,  0,  0,  0,  0]
-0.106666666667

With B/A=10, we have to be more careful.

>{p,G}=game(makeR(10,1,10)); p, G,
[0,  0,  0,  0,  0,  0,  0,  0.75,  0.25,  0]
-0.67

And with B/A=100, we have to be very careful.

>{p,G}=game(makeR(10,1,100)); p, G,
[0,  0,  0,  0,  0,  0,  0,  0,  0,  1]
-0.81
>function G(x,n) ...
 {p,G}=game(makeR(n,1,x));
 return G;
 endfunction

The game is no longer fair, even if B/A<1.

>plot2d("G(x,10)",0,1,n=40):

Most Simple Poker Game

But the loss is limited for large B/A.

>plot2d("G(x,10)",0,20,n=40):

Most Simple Poker Game

Let us determine the value for high B/A. In that case, it is optimal for both players to hold only with their largest card. Then the first player

>function r(n) &= ratsimp((n-1)/n^2-(n-1)/n)
                               2
                            - n  + 2 n - 1
                            --------------
                                   2
                                  n

For n=10.

>r(10)
-0.81

That is exactly the value of the result matrix for these tactics.

>makeR(10,1,1)[10,10]
-0.81

Euler Home