Skip to content

Instantly share code, notes, and snippets.

@mgritter
Last active November 19, 2019 08:00
Show Gist options
  • Select an option

  • Save mgritter/9f1f73cdc145e164ea9603e54b9d2be4 to your computer and use it in GitHub Desktop.

Select an option

Save mgritter/9f1f73cdc145e164ea9603e54b9d2be4 to your computer and use it in GitHub Desktop.
LP solver for three-card-poker, one round of limit betting
Starting solver...
6669 variables
6669 constraints
hand F CF CC CRF CRC RF RC RR
-------- ------- ------- ------- ------- ------- ------- ------- -------
AKQs 1.00000
KQJs 1.00000
QJTs 1.00000
JT9s 1.00000
T98s 1.00000
987s 1.00000
876s 1.00000
765s 1.00000
654s 1.00000
543s 1.00000
432s 1.00000
AAAo 1.00000
KKKo 1.00000
QQQo 1.00000
JJJo 1.00000
TTTo 1.00000
999o 1.00000
888o 1.00000
777o 1.00000
666o 1.00000
555o 1.00000
444o 1.00000
333o 1.00000
222o 1.00000
AKQo 1.00000
KQJo 1.00000
QJTo 1.00000
JT9o 1.00000
T98o 0.79878 0.20122
987o 0.66444 0.33556
876o 0.23389 0.02566 0.74044
765o 0.37705 0.62295
654o 0.04671 0.66690 0.28638
543o 0.24198 0.52064 0.23738
432o 0.14349 0.85651
AKJs 1.00000
AKTs 1.00000
AK9s 1.00000
AK8s 0.33608 0.66392
AK7s 1.00000
AK6s 1.00000
AK5s 1.00000
AK4s 1.00000
AK3s 1.00000
AK2s 1.00000
AQJs 1.00000
AQTs 1.00000
AQ9s 1.00000
AQ8s 1.00000
AQ7s 1.00000
AQ6s 1.00000
AQ5s 1.00000
AQ4s 1.00000
AQ3s 1.00000
AQ2s 1.00000
AJTs 1.00000
AJ9s 1.00000
AJ8s 1.00000
AJ7s 1.00000
AJ6s 1.00000
AJ5s 1.00000
AJ4s 1.00000
AJ3s 1.00000
AJ2s 1.00000
AT9s 1.00000
AT8s 0.13573 0.86427
AT7s 0.69937 0.30063
AT6s 0.36892 0.63108
AT5s 0.34756 0.19378 0.45866
AT4s 1.00000
AT3s 1.00000
AT2s 0.38983 0.61017
A98s 1.00000
A97s 1.00000
A96s 1.00000
A95s 0.08516 0.91484
A94s 1.00000
A93s 1.00000
A92s 0.64934 0.35066
A87s 1.00000
A86s 1.00000
A85s 1.00000
A84s 1.00000
A83s 1.00000
A82s 1.00000
A76s 1.00000
A75s 1.00000
A74s 1.00000
A73s 1.00000
A72s 1.00000
A65s 1.00000
A64s 1.00000
A63s 1.00000
A62s 1.00000
A54s 1.00000
A53s 1.00000
A52s 1.00000
A43s 0.38063 0.61937
A42s 1.00000
A32s 1.00000
KQTs 1.00000
KQ9s 1.00000
KQ8s 1.00000
KQ7s 1.00000
KQ6s 1.00000
KQ5s 1.00000
KQ4s 1.00000
KQ3s 1.00000
KQ2s 1.00000
KJTs 1.00000
KJ9s 1.00000
KJ8s 1.00000
KJ7s 1.00000
KJ6s 0.82202 0.17798
KJ5s 1.00000
KJ4s 1.00000
KJ3s 1.00000
KJ2s 1.00000
KT9s 1.00000
KT8s 1.00000
KT7s 0.80021 0.19979
KT6s 0.09532 0.90468
KT5s 1.00000
KT4s 1.00000
KT3s 0.69096 0.30904
KT2s 1.00000
K98s 1.00000
K97s 1.00000
K96s 1.00000
K95s 1.00000
K94s 0.36804 0.63196
K93s 0.78480 0.21520
K92s 1.00000
K87s 0.05290 0.94710
K86s 1.00000
K85s 1.00000
K84s 1.00000
K83s 1.00000
K82s 1.00000
K76s 1.00000
K75s 1.00000
K74s 1.00000
K73s 1.00000
K72s 1.00000
K65s 1.00000
K64s 1.00000
K63s 1.00000
K62s 1.00000
K54s 1.00000
K53s 1.00000
K52s 1.00000
K43s 1.00000
K42s 1.00000
K32s 1.00000
QJ9s 1.00000
QJ8s 0.03509 0.96491
QJ7s 1.00000
QJ6s 1.00000
QJ5s 1.00000
QJ4s 1.00000
QJ3s 1.00000
QJ2s 0.60934 0.39066
QT9s 1.00000
QT8s 1.00000
QT7s 0.00465 0.99535
QT6s 1.00000
QT5s 0.28963 0.56633 0.14405
QT4s 1.00000
QT3s 1.00000
QT2s 0.25354 0.74646
Q98s 1.00000
Q97s 1.00000
Q96s 0.75838 0.24162
Q95s 1.00000
Q94s 1.00000
Q93s 1.00000
Q92s 1.00000
Q87s 1.00000
Q86s 0.09163 0.57181 0.33656
Q85s 0.71520 0.28480
Q84s 1.00000
Q83s 1.00000
Q82s 1.00000
Q76s 1.00000
Q75s 1.00000
Q74s 1.00000
Q73s 0.65050 0.34950
Q72s 1.00000
Q65s 1.00000
Q64s 1.00000
Q63s 1.00000
Q62s 1.00000
Q54s 1.00000
Q53s 1.00000
Q52s 1.00000
Q43s 1.00000
Q42s 1.00000
Q32s 1.00000
JT8s 1.00000
JT7s 0.96945 0.03055
JT6s 0.60160 0.39840
JT5s 1.00000
JT4s 1.00000
JT3s 1.00000
JT2s 1.00000
J98s 1.00000
J97s 0.69381 0.30619
J96s 1.00000
J95s 1.00000
J94s 1.00000
J93s 1.00000
J92s 1.00000
J87s 1.00000
J86s 1.00000
J85s 0.93792 0.06208
J84s 1.00000
J83s 0.44785 0.55215
J82s 1.00000
J76s 0.70079 0.29921
J75s 1.00000
J74s 0.17478 0.82522
J73s 0.26994 0.73006
J72s 1.00000
J65s 1.00000
J64s 0.61295 0.38705
J63s 1.00000
J62s 1.00000
J54s 0.32853 0.67147
J53s 1.00000
J52s 1.00000
J43s 1.00000
J42s 1.00000
J32s 1.00000
T97s 0.10660 0.89340
T96s 1.00000
T95s 1.00000
T94s 1.00000
T93s 1.00000
T92s 1.00000
T87s 1.00000
T86s 1.00000
T85s 1.00000
T84s 0.53205 0.46795
T83s 1.00000
T82s 1.00000
T76s 1.00000
T75s 1.00000
T74s 0.14524 0.85476
T73s 0.47330 0.52670
T72s 1.00000
T65s 1.00000
T64s 1.00000
T63s 0.85589 0.14411
T62s 1.00000
T54s 1.00000
T53s 0.63565 0.36435
T52s 1.00000
T43s 1.00000
T42s 1.00000
T32s 1.00000
986s 1.00000
985s 0.30854 0.69146
984s 1.00000
983s 1.00000
982s 1.00000
976s 1.00000
975s 0.85414 0.14586
974s 0.23950 0.76050
973s 1.00000
972s 0.76990 0.23010
965s 1.00000
964s 1.00000
963s 0.87788 0.12212
962s 1.00000
954s 0.58430 0.41570
953s 1.00000
952s 1.00000
943s 0.33330 0.66670
942s 1.00000
932s 0.55619 0.44381
875s 0.23191 0.76809
874s 1.00000
873s 1.00000
872s 1.00000
865s 0.92814 0.07186
864s 1.00000
863s 0.41006 0.58994
862s 1.00000
854s 0.35933 0.64067
853s 1.00000
852s 1.00000
843s 0.48403 0.51597
842s 0.58716 0.41284
832s 1.00000
764s 0.62717 0.37283
763s 1.00000
762s 1.00000
754s 0.99539 0.00461
753s 1.00000
752s 0.39304 0.60696
743s 1.00000
742s 1.00000
732s 1.00000
653s 1.00000
652s 1.00000
643s 1.00000
642s 1.00000
632s 1.00000
542s 1.00000
532s 1.00000
AAKo 1.00000
AAQo 1.00000
AAJo 1.00000
AATo 1.00000
AA9o 0.45918 0.54082
AA8o 1.00000
AA7o 1.00000
AA6o 1.00000
AA5o 1.00000
AA4o 1.00000
AA3o 1.00000
AA2o 1.00000
KKAo 1.00000
KKQo 1.00000
KKJo 1.00000
KKTo 1.00000
KK9o 1.00000
KK8o 1.00000
KK7o 1.00000
KK6o 1.00000
KK5o 1.00000
KK4o 1.00000
KK3o 1.00000
KK2o 1.00000
QQAo 0.26796 0.73204
QQKo 1.00000
QQJo 1.00000
QQTo 1.00000
QQ9o 0.62606 0.37394
QQ8o 0.63100 0.36900
QQ7o 0.83414 0.16586
QQ6o 0.37108 0.62892
QQ5o 0.27837 0.72163
QQ4o 0.61656 0.38344
QQ3o 0.61491 0.38509
QQ2o 0.71415 0.28585
JJAo 1.00000
JJKo 1.00000
JJQo 1.00000
JJTo 1.00000
JJ9o 0.96829 0.03171
JJ8o 1.00000
JJ7o 0.41650 0.58350
JJ6o 0.63631 0.36369
JJ5o 0.34680 0.65320
JJ4o 0.92599 0.07401
JJ3o 0.74261 0.25739
JJ2o 0.79661 0.20339
TTAo 1.00000
TTKo 1.00000
TTQo 1.00000
TTJo 1.00000
TT9o 0.08427 0.06078 0.85495
TT8o 0.74101 0.25899
TT7o 1.00000
TT6o 0.43522 0.56478
TT5o 0.82852 0.17148
TT4o 0.96969 0.03031
TT3o 0.84868 0.15132
TT2o 0.68188 0.31812
99Ao 0.82172 0.17828
99Ko 1.00000
99Qo 1.00000
99Jo 1.00000
99To 1.00000
998o 0.72651 0.03358 0.23991
997o 0.71591 0.28409
996o 0.54140 0.01857 0.44003
995o 0.87659 0.12341
994o 0.74288 0.25712
993o 1.00000
992o 0.84429 0.15571
88Ao 0.29321 0.70679
88Ko 1.00000
88Qo 0.58737 0.41263
88Jo 0.72571 0.27429
88To 0.55372 0.44628
889o 0.68614 0.31386
887o 1.00000
886o 0.89289 0.03829 0.06882
885o 0.68620 0.31380
884o 0.75018 0.24982
883o 0.48848 0.51152
882o 0.59760 0.40240
77Ao 0.24799 0.75201
77Ko 1.00000
77Qo 0.54249 0.45751
77Jo 1.00000
77To 0.92885 0.07115
779o 0.90018 0.09982
778o 1.00000
776o 0.57092 0.42908
775o 0.37279 0.62721
774o 0.72582 0.27418
773o 0.62657 0.37343
772o 0.35834 0.64166
66Ao 1.00000
66Ko 1.00000
66Qo 1.00000
66Jo 0.46024 0.53976
66To 0.79676 0.20324
669o 1.00000
668o 0.01796 0.98204
667o 1.00000
665o 0.92203 0.07797
664o 0.45659 0.54341
663o 0.68987 0.27688 0.03325
662o 1.00000
55Ao 0.48988 0.51012
55Ko 1.00000
55Qo 0.36829 0.63171
55Jo 1.00000
55To 0.86457 0.13543
559o 0.68859 0.31141
558o 0.07265 0.92735
557o 1.00000
556o 0.80834 0.05714 0.13452
554o 0.83608 0.16392
553o 0.54546 0.30064 0.15390
552o 1.00000
44Ao 1.00000
44Ko 1.00000
44Qo 1.00000
44Jo 0.76000 0.24000
44To 0.36637 0.34912 0.28451
449o 0.86116 0.13884
448o 0.71298 0.28702
447o 0.95160 0.04840
446o 0.30722 0.69278
445o 0.21482 0.39363 0.39155
443o 0.44798 0.55202
442o 0.63469 0.36531
33Ao 0.67254 0.32746
33Ko 0.45392 0.54608
33Qo 0.73823 0.26177
33Jo 0.55889 0.44111
33To 0.21432 0.66213 0.03200 0.09155
339o 0.09322 0.84799 0.05880
338o 0.94623 0.05377
337o 0.44707 0.46898 0.08395
336o 0.51553 0.48447
335o 0.20977 0.13828 0.65195
334o 1.00000
332o 0.72753 0.27247
22Ao 1.00000
22Ko 0.41438 0.58562
22Qo 1.00000
22Jo 1.00000
22To 0.75537 0.24463
229o 1.00000
228o 0.34977 0.39632 0.25392
227o 0.49338 0.45169 0.05493
226o 0.01303 0.38096 0.60601
225o 0.77299 0.20523 0.02178
224o 1.00000
223o 1.00000
AKJo 0.73050 0.26950
AKTo 0.83291 0.16709
AK9o 0.48386 0.51614
AK8o 0.61893 0.38107
AK7o 0.72748 0.27252
AK6o 0.56741 0.43259
AK5o 0.75854 0.24146
AK4o 0.52190 0.47810
AK3o 0.81739 0.13184 0.05077
AK2o 0.90695 0.09305
AQJo 0.92604 0.07396
AQTo 0.59071 0.40929
AQ9o 0.35160 0.64840
AQ8o 0.73773 0.26227
AQ7o 0.62715 0.37285
AQ6o 0.75806 0.24194
AQ5o 0.46765 0.53235
AQ4o 0.79330 0.19539 0.01131
AQ3o 0.68621 0.31379
AQ2o 0.40504 0.59496
AJTo 0.51124 0.48876
AJ9o 0.03361 0.65923 0.01224 0.21736 0.07757
AJ8o 0.65207 0.34793
AJ7o 0.68837 0.31163
AJ6o 0.53608 0.27946 0.18446
AJ5o 0.82507 0.16343 0.01151
AJ4o 0.65394 0.32683 0.01923
AJ3o 0.23632 0.49717 0.11648 0.15003
AJ2o 0.38969 0.44802 0.16230
AT9o 0.63164 0.06053 0.19396 0.07336 0.04051
AT8o 0.67663 0.30761 0.01576
AT7o 0.46573 0.13336 0.40091
AT6o 0.41845 0.20317 0.11476 0.26362
AT5o 0.64824 0.28331 0.06845
AT4o 0.17513 0.59629 0.05026 0.15609 0.02224
AT3o 0.65089 0.13319 0.21452 0.00140
AT2o 0.47783 0.14417 0.37800
A98o 0.23302 0.76698
A97o 0.61992 0.30310 0.07698
A96o 0.65542 0.21124 0.13334
A95o 0.70946 0.23441 0.05613
A94o 0.54538 0.12083 0.07651 0.18226 0.07502
A93o 0.27617 0.37533 0.01117 0.02571 0.26346 0.04816
A92o 0.57136 0.39666 0.03198
A87o 0.52581 0.13689 0.33730
A86o 0.72473 0.27527
A85o 0.67798 0.28433 0.03770
A84o 0.45958 0.36953 0.01961 0.07987 0.07141
A83o 0.39366 0.07687 0.06958 0.42475 0.03515
A82o 0.86605 0.13231 0.00165
A76o 0.48488 0.51512
A75o 0.89190 0.08820 0.01990
A74o 0.21548 0.24365 0.12501 0.32380 0.09205
A73o 0.70168 0.19454 0.03570 0.06808
A72o 0.44404 0.52754 0.02842
A65o 0.39398 0.10224 0.04968 0.32071 0.13338
A64o 0.81454 0.16505 0.02041
A63o 0.36904 0.63096
A62o 0.01752 0.98248
A54o 0.49585 0.06245 0.44170
A53o 0.76936 0.15884 0.07180
A52o 0.35308 0.29675 0.35016
A43o 0.86996 0.13004
A42o 0.42809 0.09302 0.47889
A32o 0.96217 0.03783
KQTo 1.00000
KQ9o 0.38602 0.61398
KQ8o 0.82526 0.17474
KQ7o 0.71970 0.28030
KQ6o 0.72589 0.27411
KQ5o 0.59160 0.40840
KQ4o 0.82809 0.17191
KQ3o 0.64174 0.29597 0.06229
KQ2o 0.42260 0.44563 0.13177
KJTo 0.07759 0.92241
KJ9o 0.85705 0.14295
KJ8o 0.61767 0.10995 0.27238
KJ7o 0.74531 0.01629 0.23840
KJ6o 0.50214 0.00764 0.49022
KJ5o 0.16750 0.56079 0.17727 0.09445
KJ4o 0.30536 0.46668 0.05190 0.15325 0.02282
KJ3o 0.43997 0.52570 0.03433
KJ2o 0.46093 0.01683 0.52224
KT9o 0.59728 0.14840 0.25432
KT8o 0.30854 0.52167 0.16978
KT7o 0.22816 0.35317 0.41867
KT6o 0.67843 0.00583 0.31575
KT5o 0.07036 0.89736 0.00047 0.03181
KT4o 0.57710 0.01759 0.40530
KT3o 0.29900 0.16186 0.01303 0.52611
KT2o 0.62000 0.38000
K98o 0.62518 0.19865 0.17617
K97o 0.15681 0.33918 0.50401
K96o 0.44723 0.55277
K95o 0.02425 0.66084 0.31491
K94o 0.57171 0.42829
K93o 0.47230 0.52770
K92o 0.75669 0.24331
K87o 0.80327 0.19673
K86o 0.81966 0.01771 0.16262
K85o 0.44009 0.19379 0.12529 0.24083
K84o 0.29561 0.11105 0.03499 0.55835
K83o 0.70425 0.01063 0.28512
K82o 0.12391 0.41558 0.01208 0.44843
K76o 0.95897 0.02816 0.01287
K75o 0.34359 0.12849 0.52792
K74o 0.55809 0.13367 0.30824
K73o 0.34487 0.09050 0.55420 0.01044
K72o 0.40315 0.54733 0.04951
K65o 0.74146 0.25854
K64o 0.37598 0.51213 0.11189
K63o 0.92641 0.06345 0.01014
K62o 0.51784 0.48216
K54o 0.27355 0.46249 0.26397
K53o 0.06443 0.59682 0.33875
K52o 0.54026 0.45974
K43o 0.42013 0.16118 0.08334 0.33535
K42o 0.64503 0.04679 0.30819
K32o 0.55294 0.18844 0.11902 0.13960
QJ9o 0.78077 0.21923
QJ8o 0.64187 0.35813
QJ7o 0.74855 0.19673 0.05472
QJ6o 0.76128 0.23872
QJ5o 0.24873 0.01268 0.73859
QJ4o 0.57812 0.42188
QJ3o 0.84669 0.15331
QJ2o 0.39713 0.03217 0.57070
QT9o 0.94746 0.05254
QT8o 0.68641 0.10614 0.20745
QT7o 1.00000
QT6o 0.52444 0.47556
QT5o 0.13283 0.10537 0.76180
QT4o 0.88393 0.02094 0.09513
QT3o 0.39477 0.09608 0.50915
QT2o 0.63035 0.36965
Q98o 0.81778 0.12386 0.05836
Q97o 0.90406 0.09594
Q96o 0.25043 0.74957
Q95o 0.52352 0.04056 0.43591
Q94o 0.67855 0.11831 0.20314
Q93o 1.00000
Q92o 1.00000
Q87o 1.00000
Q86o 1.00000
Q85o 1.00000
Q84o 1.00000
Q83o 1.00000
Q82o 0.12951 0.87049
Q76o 1.00000
Q75o 1.00000
Q74o 1.00000
Q73o 1.00000
Q72o 1.00000
Q65o 1.00000
Q64o 1.00000
Q63o 0.21824 0.78176
Q62o 1.00000
Q54o 1.00000
Q53o 0.51274 0.48726
Q52o 0.55494 0.44506
Q43o 1.00000
Q42o 0.58442 0.41558
Q32o 1.00000
JT8o 0.44988 0.55012
JT7o 0.89994 0.10006
JT6o 1.00000
JT5o 1.00000
JT4o 0.26429 0.73571
JT3o 1.00000
JT2o 0.59758 0.40242
J98o 0.12500 0.87500
J97o 1.00000
J96o 1.00000
J95o 0.98844 0.01156
J94o 0.79992 0.20008
J93o 0.76416 0.23584
J92o 1.00000
J87o 1.00000
J86o 1.00000
J85o 1.00000
J84o 1.00000
J83o 1.00000
J82o 1.00000
J76o 0.44526 0.55474
J75o 0.70521 0.29479
J74o 1.00000
J73o 1.00000
J72o 1.00000
J65o 0.18275 0.81725
J64o 1.00000
J63o 0.99551 0.00449
J62o 1.00000
J54o 1.00000
J53o 1.00000
J52o 1.00000
J43o 1.00000
J42o 1.00000
J32o 1.00000
T97o 0.35037 0.64963
T96o 1.00000
T95o 0.57662 0.42338
T94o 1.00000
T93o 0.90433 0.09567
T92o 0.52523 0.47477
T87o 0.18021 0.81979
T86o 0.54166 0.45834
T85o 0.72049 0.27951
T84o 1.00000
T83o 1.00000
T82o 1.00000
T76o 0.66950 0.33050
T75o 0.60528 0.39472
T74o 1.00000
T73o 1.00000
T72o 1.00000
T65o 1.00000
T64o 0.91529 0.08471
T63o 0.90036 0.09964
T62o 0.60572 0.39428
T54o 1.00000
T53o 1.00000
T52o 1.00000
T43o 1.00000
T42o 1.00000
T32o 1.00000
986o 1.00000
985o 1.00000
984o 1.00000
983o 1.00000
982o 1.00000
976o 1.00000
975o 0.64056 0.35944
974o 1.00000
973o 1.00000
972o 1.00000
965o 1.00000
964o 0.57862 0.42138
963o 0.84225 0.15775
962o 0.95695 0.04305
954o 0.94279 0.05721
953o 1.00000
952o 1.00000
943o 1.00000
942o 1.00000
932o 1.00000
875o 1.00000
874o 1.00000
873o 1.00000
872o 1.00000
865o 0.46383 0.53617
864o 0.96793 0.03207
863o 0.84348 0.15652
862o 1.00000
854o 1.00000
853o 1.00000
852o 1.00000
843o 1.00000
842o 1.00000
832o 1.00000
764o 0.67980 0.32020
763o 1.00000
762o 1.00000
754o 1.00000
753o 1.00000
752o 1.00000
743o 1.00000
742o 1.00000
732o 1.00000
653o 0.76076 0.23924
652o 0.54852 0.45148
643o 1.00000
642o 1.00000
632o 1.00000
542o 1.00000
532o 1.00000
Game value to SB: -0.012590931333943185
# three-card poker solver
# Deck: AKQJT98765432 in 3 suits
# Hand ranking:
# AKQs - 432s (straight flush)
# AAA - 222 (trips)
# AKQo - 432o (straight)
# AKJs - 532s (flush)
# AAQ - 223 (pair)
# AKJo - 532o (high card)
import ortools.linear_solver.pywraplp as lp
import itertools
from fractions import Fraction
ranks = "AKQJT98765432"
suits = "cdhs"
def hand_classes():
"""Return all hand classes, with a ranking of each: lower = better hand."""
order = 1
classes = []
# straight flushes
for i in range( len( ranks ) - 2 ):
classes.append( ( ranks[i] + ranks[i+1] + ranks[i+2] + "s", order ) )
order += 1
# trips
for i in range( len( ranks ) ):
classes.append( ( ranks[i] + ranks[i] + ranks[i] + "o", order ) )
order += 1
# unsuited straight
for i in range( len( ranks ) - 2 ):
classes.append( ( ranks[i] + ranks[i+1] + ranks[i+2] + "o", order ) )
order += 1
# flush
for i in range( len( ranks ) ):
for j in range( i+1, len( ranks ) ):
for k in range( j+1, len( ranks ) ):
if j == i+1 and k == j+1:
continue
classes.append( ( ranks[i] + ranks[j] + ranks[k] + "s", order ) )
order += 1
# pair
for i in range( len( ranks ) ):
for j in range( len( ranks ) ):
if i == j:
continue
classes.append( ( ranks[i] + ranks[i] + ranks[j] + "o", order ) )
order += 1
# high card
for i in range( len( ranks ) ):
for j in range( i+1, len( ranks ) ):
for k in range( j+1, len( ranks ) ):
if j == i+1 and k == j+1:
continue
classes.append( ( ranks[i] + ranks[j] + ranks[k] + "o", order ) )
order += 1
return classes
def suit_possibilities( hc ):
if hc[-1] == "s":
return [ tuple( s*3 ) for s in suits ]
if hc[0] == hc[1]:
if hc[1] == hc[2]:
return list( itertools.combinations( suits, r=3 ) )
else:
return [ (a,b,c)
for a,b in itertools.combinations( suits, r=2 )
for c in suits ]
return [ (a,b,c)
for a,b,c in itertools.product( suits, repeat=3 )
if a != b or b != c ]
def slower_joint_probability( hc1, hc2 ):
n = 52 * 51 * 50 * 49 * 48 * 47
deck = [ (r,s) for s in suits for r in ranks ]
count = 0
hc1_ranks = list( hc1[:3] )
hc1_ranks.sort()
hc2_ranks = list( hc2[:3] )
hc2_ranks.sort()
for h in itertools.permutations( deck, r=3 ):
r1 = [ r for r,s in h ]
r1.sort()
if r1 != hc1_ranks:
continue
suited = ( h[0][1] == h[1][1] and h[1][1] == h[2][1] )
if hc1[-1] == "s" and not suited:
continue
if hc1[-1] == "o" and suited:
continue
remainder = list( deck )
remainder.remove( h[0] )
remainder.remove( h[1] )
remainder.remove( h[2] )
for g in itertools.permutations( remainder, r=3 ):
r2 = [ r for r,s in g ]
r2.sort()
if r2 != hc2_ranks:
continue
suited = ( g[0][1] == g[1][1] and g[1][1] == g[2][1] )
if hc2[-1] == "s" and not suited:
continue
if hc2[-1] == "o" and suited:
continue
count += 1
return Fraction( count, n )
def choose2( n ):
return n * (n-1) // 2
def joint_probability( hc1, hc2 ):
# 52C3 * 49C3 possiblities for the deal
n = 407170400
num_removed = {}
lhs_distinct = len( set( hc1[:3] ) )
if hc1[-1] == 's':
num_removed[hc1[0]] = 1
num_removed[hc1[1]] = 1
num_removed[hc1[2]] = 1
lhs = 4
elif lhs_distinct == 3:
num_removed[hc1[0]] = 1
num_removed[hc1[1]] = 1
num_removed[hc1[2]] = 1
# 4*4*4 - 3
lhs = 60
elif lhs_distinct == 2:
num_removed[hc1[0]] = 2
num_removed[hc1[2]] = 1
# 4C2 * 4
lhs = 24
elif lhs_distinct == 1:
num_removed[hc1[0]] = 3
# 4C3
lhs = 4
else:
assert False
def available( rank ):
return max( 4 - num_removed.get( rank, 0 ), 0 )
def single_suit():
n_avail = [ available(r) for r in hc2[:3] ]
n_avail.sort()
if n_avail == [4,4,4]:
return 4
elif n_avail == [3,4,4]:
return 3
elif n_avail == [2,4,4]:
return 2
elif n_avail == [1,4,4]:
return 1
elif n_avail == [2,3,4]:
# Problem case, looks like AAKo AKQs
# The suit of K might match A or it might not
# of the 4C2 ways we made AA,
# 1/2 of the time the K will match
# e.g.:
# cdc h or s
# cdd h or s
# cdh s only
# cds h only
return Fraction( 3, 2 )
elif n_avail == [3,3,3]:
if hc1[-1] == 's':
# AKQs AKQs
return 3
else:
# AKQo AKQs
# 4*4*4-4 possiblities
# We can make ABC all different in 4*3*2 ways, 1 suit remaining
# We can make AAB, ABA, BAA in 3*4*3 ways, 2 suits remaining
return Fraction( 24, 60 ) * 1 + Fraction( 36, 60 ) * 2
elif n_avail == [3,3,4]:
if hc1[-1] == 's':
# AKQs AKJs
return 3
else:
# AKQo AKJs
# 4*4*4-4 possibilities
# We can make ABC all different in 4*3*2 ways, 2 suits remaining
# We can make AAB in 4*3 ways, 3 suits remaining
# We can make ABA, BAA in 2*4*3 ways, 2 suits remaining
return Fraction( 24, 60 ) *2 + Fraction( 12, 60 ) * 3 + Fraction ( 24, 60 ) * 2
else:
assert False
rhs_distinct = len( set( hc2[:3] ) )
if hc2[-1] == 's':
rhs = single_suit()
elif rhs_distinct == 3:
least = min( available(r) for r in hc2[:3] )
# if one suit missing, then 4*4*3 - 3
rhs = available(hc2[0]) * available(hc2[1]) * available(hc2[2]) - \
single_suit()
elif rhs_distinct == 2:
pair_suits = available(hc2[0])
kicker_suits = available(hc2[2])
rhs = choose2( pair_suits ) * kicker_suits
elif rhs_distinct == 1:
trip_suits = available(hc2[0])
if trip_suits < 3:
rhs = 0
elif trip_suits == 3:
rhs = 1
elif trip_suits == 4:
rhs = 4
else:
assert False
# print( hc1, lhs, hc2, rhs )
return Fraction( lhs * rhs, n )
def slow_joint_probability( hc1, hc2 ):
# Enumerate all possibilities and count them
ranks = hc1[:3] + hc2[:3]
# 52C3 * 49C3 possiblities for the deal
n = 407170400
#n = 1
distinct = len( set( ranks ) )
if distinct == 6:
if hc1[-1] == "s" and hc2[-1] == "s":
return Fraction(16, n)
elif hc1[-1] == "s":
return Fraction(4 * 60, n)
elif hc2[-1] == "s":
return Fraction(60 * 4, n)
else:
return Fraction(60 * 60, n)
count = 0
loop_size = 0
sc1 = suit_possibilities( hc1 )
sc2 = suit_possibilities( hc2 )
for s1 in sc1:
for s2 in sc2:
cards = zip( ranks, s1 + s2 )
loop_size += 1
if len( set( cards ) ) == 6:
count += 1
return Fraction( count, n )
import random
def test_joint_probability():
test_cases = [ "AKQs", "KQJs", "KT6s", "654s",
"AAAo", "AAKo", "KKAo", "664o",
"AKQo", "KQJo", "KT6o", "654o" ]
for a in test_cases:
for b in test_cases:
p = joint_probability( a, b )
p2 = slow_joint_probability( a, b )
p3 = slower_joint_probability( a, b )
print( a, b, p, p2, p3 )
assert p == p2
assert p2 == p3
def test_joint_probability_fast( short = False):
if short:
test_cases = [ "AKQs", "KQJs", "KT6s", "654s",
"AAAo", "AAKo", "KKAo", "664o",
"AKQo", "KQJo", "KT6o", "654o" ]
else:
test_cases = [ hc for hc,r in hand_classes() ]
for a in test_cases:
for b in test_cases:
p = slow_joint_probability( a, b )
p2 = joint_probability( a, b )
print( a, b, p, p2 )
assert p == p2
def test_total_probability():
total = 0
hc = hand_classes()
for a, _ in hc:
for b, _ in hc:
p = joint_probability( a, b )
total += p
#print( a, b, p, total )
print( total )
assert total == 1
# 4-bet cap
# P1 has 1 simplex
p1_strategies = [
"F",
"CF", "CC", "CRF", "CRC", # complete the SB
"RF", "RC", "RR" # raise
]
# P2 has 2 simplices, depending on the P1 first action.
p2_strategies = [
"C/K", "C/RF", "C/RC", "C/RR", # P1 calls
"R/F", "R/C", "R/RF", "R/RC" # P1 raises
]
p2_simplicies = [ p2_strategies[:4], p2_strategies[4:] ]
# P2\P1 F CF CC CRF CRC RF RC RR
# C/K x x x x x
# C/RF x x x x x
# C/RC x x x x x [zeros]
# C/RR x x x x x
# R/F x x x
# R/C x x x
# R/RF [zeros] x x x
# R/RC x x x
def payoff( p1, p2, sb, winner ):
# P1 is the small blind
# P2 is the big blind
# Return the payoff to P1
if p1[0] == "F":
# Count a small-blind fold in only one simplex.
if p2[0] == "C":
return 0 - sb
else:
return 0
if p1[0] == "C":
if p2[0] != 'C':
# not applicable
return 0
if p2[2] == 'K':
# C/K, pot size = 2
return 2 * winner - 1
assert p2[2] == 'R'
if p1[1] == 'F':
# C/R/F, pot size = 2
return -1
if p1[1] == 'C':
# C/R/C, pot size = 4
return 4 * winner - 2
assert p1[1] == 'R'
if p2[3] == 'F':
# C/R/R/F, pot size = 4
return 2
if p2[3] == 'C':
# C/R/R/C, pot size = 6
return 6 * winner - 3
assert p2[3] == 'R'
if p1[2] == 'F':
# C/R/R/R/F
return -3
if p1[2] == 'C':
# C/R/R/R/C
return 8 * winner - 4
assert False
assert p1[0] == "R"
if p2[0] != 'R':
# not applicable
return 0
if p2[2] == 'F':
# R/F, win the big blind
return 1
if p2[2] == 'C':
# R/C
return 4 * winner - 2
assert p2[2] == 'R'
if p1[1] == 'F':
# R/R/F
return -2
if p1[1] == 'C':
# R/R/C, pot size = 6
return 6 * winner - 3
assert p1[1] == 'R'
if p2[3] == 'F':
# R/R/R/F
return 3
if p2[3] == 'C':
# R/R/R/C
return 8 * winner - 4
assert False
def show_payoffs():
print( "P1 payoffs:")
for winner in [0, 0.5, 1]:
if winner == 0:
print( "\nP2 wins at showdown:" )
elif winner == 0.5:
print( "\nTie:" )
else:
print( "\nP1 wins at showdown:" )
print( "P2 \ P1 " +
"".join( "{:>8}".format( p1) for p1 in p1_strategies ) )
for p2 in p2_strategies:
line = "{:8}".format( p2 )
for p1 in p1_strategies:
line += "{:8}".format( payoff( p1, p2, 0.5, winner ) )
print( line )
def setup_problem( sb_classes, bb_classes ):
solver = lp.Solver( "ThreeCardPoker", lp.Solver.GLOP_LINEAR_PROGRAMMING )
small_blind = 0.5
sb_vars = {}
for sb_hand, sb_rank in sb_classes:
d = {}
for sb_strat in p1_strategies:
name = "p_" + sb_hand + "_" + sb_strat
d[sb_strat] = solver.NumVar( 0, 1.0, name )
# mix of strategies sums to 1.0
constraint = solver.Constraint( 1.0, 1.0 )
for v in d.values():
constraint.SetCoefficient( v, 1.0 )
sb_vars[sb_hand] = d
objective = solver.Objective()
# Player 2 has one dummy variable per hand
# hand/strategy expected value
# = sum over SB hands and strategies Y of
# p(SB,Y)*p(SB,BB)*SB_payoff(Y,winner)
# BB hand value for H <= (sum of EV of strategies) for H
# That is, BB picks the best (most negative) strategy against SB's mix.
# Then we maximize sum of all BB hand values to get SB's optimal
#
# c_1 sb_1 + c_2 sb_2 + ... + c_n sb_n >= bb_h
# c_1 sb_1 + c_2 sb_2 + ... + c_n sb_n - bb_h >= 0
bb_vars = {}
for bb_hand, bb_rank in bb_classes:
# Could be bounded by -4 and 4?
bb_vars[bb_hand] = solver.NumVar( -solver.infinity(),
solver.infinity(),
"bb_" + bb_hand )
objective.SetCoefficient( bb_vars[bb_hand], 1.0 )
for bb_strat in p2_strategies:
# One constraint per strategy BB could play
constraint = solver.Constraint(0, solver.infinity() )
constraint.SetCoefficient( bb_vars[bb_hand], -1.0 )
for sb_hand, sb_rank in sb_classes:
if sb_rank < bb_rank:
winner = 1
elif sb_rank > bb_rank:
winner = 0
else:
winner = 0.5
p_hand_pair = float( joint_probability( sb_hand, bb_hand ) )
for sb_strat in p1_strategies:
c = ( p_hand_pair *
payoff( sb_strat, bb_strat, small_blind, winner ) )
constraint.SetCoefficient( sb_vars[sb_hand][sb_strat], c )
objective.SetMaximization()
return solver, sb_vars, bb_vars
def show_solution( sb_classes, bb_classes ):
solver, sb_vars, bb_vars = setup_problem( sb_classes, bb_classes )
print( "Starting solver..." )
print( solver.NumVariables(), "variables")
print( solver.NumConstraints(), "constraints" )
solver.EnableOutput()
status = solver.Solve()
if status != solver.OPTIMAL:
print( "Couldn't solve, status", status )
return
header = "hand "
for sb_strat in p1_strategies:
header += "{:>8}".format( sb_strat )
print( header )
print( "--------" + " -------" * len( p1_strategies ) )
for sb_hand, sb_rank in sb_classes:
hand = "{:8}".format( sb_hand )
for sb_strat in p1_strategies:
var = sb_vars[sb_hand][sb_strat]
p = var.solution_value()
if p > 0.000001:
hand += " {:7.5f}".format( var.solution_value() )
else:
hand += " "
print( hand )
print( "\nGame value to SB: ", solver.Objective().Value() )
if __name__ == "__main__":
hc = hand_classes()
show_solution( hc, hc )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment