Last active
December 10, 2015 18:58
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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']]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Omaha is winning in 68.929% (sample of 240000 hands) at revision 432222388d4b12f93007626abd8a60adeb6d408f