Skip to content

Instantly share code, notes, and snippets.

@Mikulas
Last active December 10, 2015 18:58
Show Gist options
  • Save Mikulas/4478289 to your computer and use it in GitHub Desktop.
Save Mikulas/4478289 to your computer and use it in GitHub Desktop.
Percentage win Omaha vs Texas Holdem when omaha player doesn't have to use two cards from his hand.
<?php
$games = 0;
$score = (object) ['o' => 0, 'or' => 0, 'th' => 0];
while (TRUE) {
// build deck
$cards = [];
for ($i = 2; $i <= 14; $i++) {
foreach (['c', 'd', 'h', 's'] as $c) {
$cards[] = (object) ['card' => $i, 'color' => $c];
}
}
//require 'tests.php'; die;
// deal cards
$keys = array_rand($cards, 2 + 4 + 5);
$hand_th = [
$cards[$keys[0]],
$cards[$keys[1]],
];
$hand_o = [
$cards[$keys[2]],
$cards[$keys[3]],
$cards[$keys[4]],
$cards[$keys[5]],
];
$com = [
$cards[$keys[6]],
$cards[$keys[7]],
$cards[$keys[8]],
$cards[$keys[9]],
$cards[$keys[10]],
];
// find winner
$o = findOmahaValue($hand_o, $com);
$or = findOmageRulesValue($hand_o, $com);
$th = findTHValue($hand_th, $com);
$games++;
if ($o > $th) {
$score->o++;
} else {
$score->th++;
}
if ($or > $th) {
$score->or++;
}
/*
echo "\r \r";
var_dump($games, $score);
if ($score->o > 0)
echo "Omaha is winning in " . $score->o * 100 / $games . "%";
//*/
if ($games % 1e4 === 0) {
echo "\r \r";
echo "Omaha is winning in " . round($score->o * 100 / $games, 3) . "% [" . round($score->or * 100 / $games, 3) . "%] (sample $games hands)";
}
}
function findOmahaValue($hand, $com)
{
return max([
findTHValue([$hand[0], $hand[1]], $com),
findTHValue([$hand[0], $hand[2]], $com),
findTHValue([$hand[0], $hand[3]], $com),
findTHValue([$hand[1], $hand[2]], $com),
findTHValue([$hand[1], $hand[3]], $com),
findTHValue([$hand[2], $hand[3]], $com),
]);
}
function findOmageRulesValue($hand, $com)
{
return max([
_omahaRulesHelper((object) [$hand[0], $hand[1]], $com),
_omahaRulesHelper((object) [$hand[0], $hand[2]], $com),
_omahaRulesHelper((object) [$hand[0], $hand[3]], $com),
_omahaRulesHelper((object) [$hand[1], $hand[2]], $com),
_omahaRulesHelper((object) [$hand[1], $hand[3]], $com),
_omahaRulesHelper((object) [$hand[2], $hand[3]], $com),
]);
}
function _omahaRulesHelper($hand, $com)
{
return max([
findTHValue($hand, (object) [$com[0], $com[1], $com[2]]),
findTHValue($hand, (object) [$com[0], $com[1], $com[3]]),
findTHValue($hand, (object) [$com[0], $com[1], $com[4]]),
findTHValue($hand, (object) [$com[0], $com[2], $com[3]]),
findTHValue($hand, (object) [$com[0], $com[2], $com[4]]),
findTHValue($hand, (object) [$com[0], $com[3], $com[4]]),
findTHValue($hand, (object) [$com[1], $com[2], $com[3]]),
findTHValue($hand, (object) [$com[1], $com[2], $com[4]]),
findTHValue($hand, (object) [$com[1], $com[3], $com[4]]),
findTHValue($hand, (object) [$com[2], $com[3], $com[4]]),
]);
}
function findTHValue($hand, $com)
{
$cards = [];
$bulk = [];
foreach ($hand as $c) {
$bulk[] = $c->card;
$cards[] = $c;
}
foreach ($com as $c) {
$bulk[] = $c->card;
$cards[] = $c;
}
$value = 0;
// 1e16 | straight flush
$colors = ['c' => 0, 'd' => 0, 'h' => 0, 's' => 0];
foreach ($cards as $c) {
$colors[$c->color]++;
}
$flush = [];
foreach ($colors as $col => $v) {
if ($v >= 5) {
foreach ($cards as $c) {
if ($c->color === $col) {
$flush[] = $c->card;
}
}
}
}
rsort($flush);
if ($flush) {
$last = NULL;
$count = 1;
$top = NULL;
foreach ($flush as $c) {
if ($last === $c + 1) { // iterating from top
if ($count === 1) {
$top = $last;
}
$count++;
} else if ($count < 5) {
$count = 1;
}
$last = $c;
}
if ($count >= 5) {
return 1e16 * $top;
}
}
// 1e14 | poker
rsort($bulk);
$last = NULL;
$count = 1;
foreach ($bulk as $c) {
if ($c === $last) {
$count++;
if ($count === 4) {
return 1e14 * $c;
}
} else {
$count = 1;
}
$last = $c;
}
// 1e12 | full house
$last = NULL;
$last2 = NULL;
$trips = NULL;
$b = $bulk; // clone so we may remove cards if we find trips
foreach ($bulk as $c) {
if ($last2 === $c) { // highest trips
$value += 1e6 * $c;
unset($b[array_search($c, $b)]);
unset($b[array_search($c, $b)]);
unset($b[array_search($c, $b)]); // the card is thrice there
rsort($b); // renew keys
$trips = $c;
break;
} else if ($last === $c) {
$last2 = $c;
} else {
$last = $c;
}
}
// if trips found, we are still looking for a pair
if ($trips) {
$last = NULL;
foreach ($b as $c) {
if ($last === $c) { // we found highest pair
return $trips * 1e12 + $c * 1e2;
}
$last = $c;
}
}
$value = 0; // mandatory reset from fullhouse trips without additional pair
// 1e10 | flush
if ($flush) { // already computed in straight flush resolution
for ($i = 0; $i < 5; $i++) {
$value += 1e10 * $flush[$i];
}
return $value;
}
// 1e8 | straight
$top = NULL;
$last = NULL;
$count = 1;
$f = $bulk;
if (in_array(14, $f)) {
$f[] = 1; // add wheel
}
foreach ($f as $c) {
if ($last === $c + 1) { // iterating from top
if ($count === 1) {
$top = $last;
}
$count++;
} else if ($last === $c) {
// continue
} else if ($count < 5) {
$count = 1;
$top = NULL;
}
$last = $c;
}
if ($count >= 5) {
return $top * 1e8;
}
// 1e6 | trips
// already computed in full house resolution
if ($trips) {
$value = 1e6 * $trips;
for ($i = 0; $i < 2; ++$i) {
$value += $bulk[$i]; // value of the two other cards beside trips
}
return $value;
}
// 1e2, 1e4 | one or two pairs
$last = NULL;
$pair = [];
foreach ($bulk as $c) {
if ($c === $last) {
$pair[] = $c;
unset($bulk[array_search($c, $bulk)]);
unset($bulk[array_search($c, $bulk)]); // the card is twice there
if (count($pair) == 2)
break; // do not count more than two pair
}
$last = $c;
}
if ($pair) {
if (count($pair) === 2) {
foreach ($pair as $p) {
$value += 1e4 * $p;
}
} else {
foreach ($pair as $p) {
$value += 1e2 * $p;
}
}
rsort($bulk); // renew keys
for ($i = 0; $i < 5 - 2 * count($pair); ++$i) {
$value += $bulk[$i]; // value of other cards beside pairs
}
return $value;
}
// 1 | board, possible kicker
for ($i = 0; $i < 5; ++$i) {
$value += $bulk[$i];
}
return $value;
}
<?php
// board, kicker
hope($t1 = findTHValue([h('11h'), h('3h')], [h('5s'), h('6s'), h('7s'), h('9d'), h('10d')]), 11+10+9+7+6);
hope(findTHValue([h('2h'), h('3h')], [h('5s'), h('6s'), h('7s'), h('9d'), h('10d')]), 10+9+7+6+5);
// one pair
hope($t2 = findTHValue([h('2h'), h('2c')], [h('5s'), h('6s'), h('7s'), h('9d'), h('10d')]), 2*1e2 + 10+9+7);
// two pair
hope($t3 = findTHValue([h('2h'), h('2c')], [h('3s'), h('3d'), h('7s'), h('9d'), h('10d')]), 2*1e4 + 3*1e4 + 10);
hope(findTHValue([h('14h'), h('14c')], [h('13s'), h('13d'), h('12s'), h('12d'), h('11d')]), 14*1e4 + 13*1e4 + 12); // three pairs, count only top two
// trips
hope($t4 = findTHValue([h('2h'), h('2c')], [h('2s'), h('3d'), h('7s'), h('9d'), h('10d')]), 2*1e6 + 9+10);
// straight
hope($t5 = findTHValue([h('2h'), h('3c')], [h('4s'), h('5d'), h('6s'), h('10d'), h('11d')]), 6*1e8);
hope(findTHValue([h('2h'), h('3c')], [h('4s'), h('5d'), h('14s'), h('10d'), h('11d')]), 5*1e8); // wheel
hope(findTHValue([h('14h'), h('13c')], [h('12d'), h('11s'), h('10s'), h('14d'), h('13d')]), 14*1e8);
// flush
hope($t6 = findTHValue([h('2h'), h('3h')], [h('8h'), h('9h'), h('12h'), h('13d'), h('14d')]), 1e10*(2+3+8+9+12));
// full house
hope($t7 = findTHValue([h('2h'), h('2c')], [h('2h'), h('3c'), h('3h'), h('13d'), h('14d')]), 1e12 * 2 + 100 * 3);
// poker
hope($t8 = findTHValue([h('2h'), h('2c')], [h('2h'), h('2h'), h('12h'), h('13d'), h('14d')]), 1e14 * 2);
// straight flush
hope($t9 = findTHValue([h('6h'), h('7h')], [h('2c'), h('2c'), h('8h'), h('9h'), h('10h')]), 10 * 1e16);
hope(findTHValue([h('6h'), h('7h')], [h('2h'), h('2c'), h('8h'), h('9h'), h('10h')]), 10 * 1e16);
// various
hope(findTHValue([h('2c'), h('3c')], [h('4h'), h('5h'), h('6h'), h('13h'), h('14h')]), 1e10*(4+5+6+13+14)); // flush over straigh
hope(findTHValue([h('14c'), h('14d')], [h('14h'), h('13c'), h('13d'), h('13h'), h('12h')]), 14*1e12 + 13*100); // two trips
hope(findTHValue([h('10c'), h('14d')], [h('10h'), h('14c'), h('10d'), h('14h'), h('12h')]), 14*1e12 + 10*100); // ordering
hope(findTHValue([h('14c'), h('14d')], [h('8h'), h('7d'), h('6c'), h('2d'), h('2s')]), 14*1e4 + 2*1e4 + 8); // ordering
// compare hand values
hope( // pair is more than high card
findTHValue([h('2h'), h('2c')], [h('3c'), h('4s'), h('6s'), h('8d'), h('10d')]) >
findTHValue([h('14h'), h('13c')], [h('2c'), h('4s'), h('6s'), h('8d'), h('10d')])
, TRUE);
hope( // two pair are more than one pair
findTHValue([h('2h'), h('2d')], [h('3c'), h('3s'), h('8h'), h('9h'), h('10h')]) >
findTHValue([h('14h'), h('14c')], [h('2c'), h('4s'), h('6s'), h('8d'), h('10d')])
, TRUE);
hope( // trips over two pair
findTHValue([h('2h'), h('2d')], [h('2c'), h('3s'), h('8h'), h('9h'), h('10h')]) >
findTHValue([h('14h'), h('14c')], [h('13c'), h('13s'), h('6s'), h('8d'), h('10d')])
, TRUE);
hope( // trips kicker
findTHValue([h('2h'), h('2d')], [h('2c'), h('14s'), h('13h'), h('8h'), h('6h')]) >
findTHValue([h('2h'), h('2d')], [h('2c'), h('14s'), h('12h'), h('8h'), h('6h')])
, TRUE);
hope( // straight over trips
findTHValue([h('2h'), h('3d')], [h('4c'), h('5s'), h('6h'), h('9h'), h('10h')]) >
findTHValue([h('14h'), h('14c')], [h('14d'), h('13s'), h('6s'), h('8d'), h('10d')])
, TRUE);
hope( // ace straight over wheel
findTHValue([h('14h'), h('13c')], [h('12d'), h('11s'), h('10s'), h('8d'), h('7d')]) >
findTHValue([h('14h'), h('2d')], [h('3c'), h('4s'), h('5h'), h('9h'), h('10h')])
, TRUE);
hope( // same straights
findTHValue([h('14h'), h('13c')], [h('12d'), h('11s'), h('10s'), h('14d'), h('13d')]) ===
findTHValue([h('14h'), h('13c')], [h('12d'), h('11s'), h('10s'), h('2d'), h('2s')])
, TRUE);
// @TODO add flush
hope( // full house over straight
findTHValue([h('2h'), h('2c')], [h('2d'), h('3s'), h('3d'), h('4d'), h('6h')]) >
findTHValue([h('14h'), h('13c')], [h('12d'), h('11s'), h('10s'), h('2d'), h('2s')])
, TRUE);
hope( // full houses
findTHValue([h('2h'), h('2c')], [h('3d'), h('3s'), h('3d'), h('4d'), h('6h')]) >
findTHValue([h('2h'), h('2c')], [h('2d'), h('3s'), h('3d'), h('4d'), h('6h')])
, TRUE);
hope( // poker over full house
findTHValue([h('2h'), h('2c')], [h('2d'), h('2s'), h('3d'), h('4d'), h('6h')]) >
findTHValue([h('2h'), h('2c')], [h('2d'), h('3s'), h('3d'), h('4d'), h('6h')])
, TRUE);
hope( // straight flush over poker
findTHValue([h('2h'), h('3h')], [h('4h'), h('5h'), h('6h'), h('8s'), h('9c')]) >
findTHValue([h('14h'), h('14c')], [h('14d'), h('14s'), h('13d'), h('4d'), h('6h')])
, TRUE);
hope($t1 < $t2, TRUE);
hope($t2 < $t3, TRUE);
hope($t3 < $t4, TRUE);
hope($t4 < $t5, TRUE);
hope($t5 < $t6, TRUE);
hope($t6 < $t7, TRUE);
hope($t7 < $t8, TRUE);
hope($t8 < $t9, TRUE);
echo "\n";
$test = 0;
function hope($v, $expected) {
global $test;
$test++;
if ($v !== $expected) {
echo "FAIL test #$test: $v (expected $expected)\n";
} else {
echo ".";
}
}
function h($s) {
$m = [];
preg_match('~^(?P<v>\d+)(?P<c>.)$~', $s, $m);
return (object) ['card' => (int) $m['v'], 'color' => $m['c']];
}
@Mikulas
Copy link
Author

Mikulas commented Jan 7, 2013

Omaha is winning in 68.929% (sample of 240000 hands) at revision 432222388d4b12f93007626abd8a60adeb6d408f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment