Spaces:
Sleeping
Sleeping
Yazhou Cao
commited on
Commit
·
346f4c8
1
Parent(s):
b372c40
Initial commit
Browse files- .gitignore +7 -0
- README.md +24 -0
- app.py +132 -0
- holdem.py +97 -0
- poker_functions.py +356 -0
- requirements.txt +4 -0
- simulation.py +267 -0
.gitignore
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/venv
|
| 2 |
+
/.pytest_cache/*
|
| 3 |
+
/.idea/*
|
| 4 |
+
.env
|
| 5 |
+
holdem_pck
|
| 6 |
+
holdem_calc
|
| 7 |
+
__pycache__/
|
README.md
CHANGED
|
@@ -10,3 +10,27 @@ pinned: false
|
|
| 10 |
---
|
| 11 |
|
| 12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 13 |
+
|
| 14 |
+
## Introduction
|
| 15 |
+
|
| 16 |
+
This example is a Streamlit app that supports running OCR inference.
|
| 17 |
+
|
| 18 |
+
In particular, this app allows you to run inference with uploaded images or images from your camera.
|
| 19 |
+
|
| 20 |
+
### Target Audience
|
| 21 |
+
We recommend that users have:
|
| 22 |
+
|
| 23 |
+
- Basic Python programming skills.
|
| 24 |
+
- A basic understanding of the Streamlit library. For more information, go [here](https://docs.streamlit.io/library/get-started/main-concepts).
|
| 25 |
+
|
| 26 |
+
### Installation
|
| 27 |
+
|
| 28 |
+
```
|
| 29 |
+
pip install -r examples/apps/ocr/requirements.txt
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
### Launch the App
|
| 33 |
+
|
| 34 |
+
```
|
| 35 |
+
streamlit run examples/apps/ocr/app.py
|
| 36 |
+
```
|
app.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
from typing import List
|
| 3 |
+
import extra_streamlit_components as stx
|
| 4 |
+
|
| 5 |
+
import numpy as np
|
| 6 |
+
import streamlit as st
|
| 7 |
+
from landingai.common import Prediction
|
| 8 |
+
from landingai.predict import Predictor
|
| 9 |
+
from landingai.st_utils import render_svg, setup_page
|
| 10 |
+
from landingai.visualize import overlay_predictions
|
| 11 |
+
from PIL import Image
|
| 12 |
+
|
| 13 |
+
from holdem import run_simulation
|
| 14 |
+
import simulation as s
|
| 15 |
+
|
| 16 |
+
setup_page(page_title="LandingLens")
|
| 17 |
+
|
| 18 |
+
Image.MAX_IMAGE_PIXELS = None
|
| 19 |
+
|
| 20 |
+
_NUM_PLAYERS = 2
|
| 21 |
+
_HAND = "hand"
|
| 22 |
+
_FLOP = "flop"
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def main():
|
| 26 |
+
# render_svg(Path("./examples/apps/ocr/static/LandingLens_OCR_logo.svg").read_text())
|
| 27 |
+
# image_file = st.camera_input("Camera View")
|
| 28 |
+
tab1, tab2 = st.tabs(["Your hand", "Flop"])
|
| 29 |
+
with tab1:
|
| 30 |
+
# image_file_hand = st.file_uploader("Your hand")
|
| 31 |
+
image_file_hand = image_file = st.camera_input("Your hand")
|
| 32 |
+
if image_file_hand is not None:
|
| 33 |
+
preds = _run_inference(image_file_hand)
|
| 34 |
+
if len(preds) != 2:
|
| 35 |
+
_show_error_message(2, preds, image_file_hand, "Your hand")
|
| 36 |
+
image_file_hand = None
|
| 37 |
+
return
|
| 38 |
+
st.session_state[_HAND] = preds, image_file_hand
|
| 39 |
+
with tab2:
|
| 40 |
+
# image_file_flop = st.file_uploader("Flop")
|
| 41 |
+
image_file_flop = image_file = st.camera_input(label="Flop")
|
| 42 |
+
if image_file_flop is not None:
|
| 43 |
+
preds = _run_inference(image_file_flop)
|
| 44 |
+
if len(preds) != 3:
|
| 45 |
+
_show_error_message(3, preds, image_file_flop, "Flop")
|
| 46 |
+
image_file_flop = None
|
| 47 |
+
return
|
| 48 |
+
st.session_state[_FLOP] = preds, image_file_flop
|
| 49 |
+
|
| 50 |
+
if _HAND not in st.session_state:
|
| 51 |
+
st.info("Please take a photo for your hand.")
|
| 52 |
+
return
|
| 53 |
+
if _FLOP not in st.session_state:
|
| 54 |
+
_show_predictions(*st.session_state[_HAND], "Your hand")
|
| 55 |
+
hand = [_convert_name(det.label_name) for det in st.session_state[_HAND][0]]
|
| 56 |
+
run_simulation(hand=hand)
|
| 57 |
+
st.info("Please take a photo for flop to continue.")
|
| 58 |
+
return
|
| 59 |
+
col1, col2 = st.columns(2)
|
| 60 |
+
with col1:
|
| 61 |
+
_show_predictions(*st.session_state[_HAND], "Your hand")
|
| 62 |
+
with col2:
|
| 63 |
+
_show_predictions(*st.session_state[_FLOP], "Flop")
|
| 64 |
+
|
| 65 |
+
hand = [_convert_name(det.label_name) for det in st.session_state[_HAND][0]]
|
| 66 |
+
flop = [_convert_name(det.label_name) for det in st.session_state[_FLOP][0]]
|
| 67 |
+
if not _validate_cards(hand, flop):
|
| 68 |
+
return
|
| 69 |
+
run_simulation(hand=hand, flop=flop)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def _validate_cards(hand, flop) -> bool:
|
| 73 |
+
check = hand + flop
|
| 74 |
+
if s.dedup(check):
|
| 75 |
+
st.error("There is a duplicate card. Please check the board and your hand and try again.", icon="🚨")
|
| 76 |
+
return False
|
| 77 |
+
if not s.validate_card(check):
|
| 78 |
+
st.error("At least one of your cards is not valid. Please try again.", icon="🚨")
|
| 79 |
+
return False
|
| 80 |
+
return True
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _convert_name(name: str) -> str:
|
| 84 |
+
if name.startswith("10"):
|
| 85 |
+
return f"T{name[2:].lower()}"
|
| 86 |
+
else:
|
| 87 |
+
return f"{name[0].upper()}{name[1:].lower()}"
|
| 88 |
+
|
| 89 |
+
# TODO Rename this here and in `main`
|
| 90 |
+
def _show_error_message(arg0, preds, arg2, arg3):
|
| 91 |
+
msg = (
|
| 92 |
+
f"{arg0 - len(preds)} card in your hand is not detected. Please try again with a new image."
|
| 93 |
+
if len(preds) < arg0
|
| 94 |
+
else f"More than {len(preds) - arg0} card in your hand is detected. Please try again with a new image."
|
| 95 |
+
)
|
| 96 |
+
st.error(msg)
|
| 97 |
+
_show_predictions(preds, arg2, arg3)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
@st.cache_data
|
| 101 |
+
def _run_inference(image_file) -> List[Prediction]:
|
| 102 |
+
image = Image.open(image_file).convert("RGB")
|
| 103 |
+
predictor = Predictor(endpoint_id="ecab49f5-9047-4d5a-8c92-b7ad8e908324")
|
| 104 |
+
logging.info("Running Poker prediction")
|
| 105 |
+
preds = predictor.predict(image)
|
| 106 |
+
preds = _dedup_preds(preds)
|
| 107 |
+
logging.info(
|
| 108 |
+
f"Poker prediction done successfully against {image_file} with {len(preds)} predictions."
|
| 109 |
+
)
|
| 110 |
+
# st.write(preds)
|
| 111 |
+
return preds
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def _show_predictions(preds, image_file, caption: str) -> None:
|
| 115 |
+
image = Image.open(image_file).convert("RGB")
|
| 116 |
+
display_image = overlay_predictions(preds, image)
|
| 117 |
+
st.image(
|
| 118 |
+
display_image,
|
| 119 |
+
channels="RGB",
|
| 120 |
+
caption=caption,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def _dedup_preds(preds: List[Prediction]) -> List[Prediction]:
|
| 125 |
+
"""Deduplicate predictions by the prediction value."""
|
| 126 |
+
result = {p.label_name: p for p in preds}
|
| 127 |
+
return list(result.values())
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
# Run the app
|
| 131 |
+
if __name__ == "__main__":
|
| 132 |
+
main()
|
holdem.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from prettytable import PrettyTable
|
| 2 |
+
import simulation as s
|
| 3 |
+
import streamlit as st
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def run_simulation(
|
| 7 |
+
hand: list[str],
|
| 8 |
+
flop: list[str] = None,
|
| 9 |
+
turn: list[str] = None,
|
| 10 |
+
river: list[str] = None,
|
| 11 |
+
sims=100000,
|
| 12 |
+
) -> None:
|
| 13 |
+
sim = s.simulation_one_player(hand, flop, turn, river, sims)
|
| 14 |
+
hc_pct = s.percent(sim[1], sim[0])
|
| 15 |
+
hc_ratio = s.ratio(sim[1], sim[0])
|
| 16 |
+
pair_pct = s.percent(sim[2], sim[0])
|
| 17 |
+
pair_ratio = s.ratio(sim[2], sim[0])
|
| 18 |
+
two_pair_pct = s.percent(sim[3], sim[0])
|
| 19 |
+
two_pair_ratio = s.ratio(sim[3], sim[0])
|
| 20 |
+
three_ok_pct = s.percent(sim[4], sim[0])
|
| 21 |
+
three_ok_ratio = s.ratio(sim[4], sim[0])
|
| 22 |
+
straight_pct = s.percent(sim[5], sim[0])
|
| 23 |
+
straight_ratio = s.ratio(sim[5], sim[0])
|
| 24 |
+
flush_pct = s.percent(sim[6], sim[0])
|
| 25 |
+
flush_ratio = s.ratio(sim[6], sim[0])
|
| 26 |
+
boat_pct = s.percent(sim[7], sim[0])
|
| 27 |
+
boat_ratio = s.ratio(sim[7], sim[0])
|
| 28 |
+
quads_pct = s.percent(sim[8], sim[0])
|
| 29 |
+
quads_ratio = s.ratio(sim[8], sim[0])
|
| 30 |
+
strt_flush_pct = s.percent(sim[9], sim[0])
|
| 31 |
+
strt_flush_ratio = s.ratio(sim[9], sim[0])
|
| 32 |
+
|
| 33 |
+
hole_card_str = f"{hand[0]} {hand[1]}"
|
| 34 |
+
|
| 35 |
+
table = PrettyTable()
|
| 36 |
+
table.field_names = ["Your Hand", "Board"]
|
| 37 |
+
if flop is None:
|
| 38 |
+
flop = []
|
| 39 |
+
if turn is None:
|
| 40 |
+
turn = []
|
| 41 |
+
if river is None:
|
| 42 |
+
river = []
|
| 43 |
+
board_str = "".join(f"{card} " for card in (flop + turn + river))
|
| 44 |
+
table.add_row([hole_card_str, board_str])
|
| 45 |
+
|
| 46 |
+
odds = PrettyTable()
|
| 47 |
+
odds.add_column(
|
| 48 |
+
"Best Final Hand",
|
| 49 |
+
[
|
| 50 |
+
"High Card",
|
| 51 |
+
"Pair",
|
| 52 |
+
"Two Pair",
|
| 53 |
+
"Three of a Kind",
|
| 54 |
+
"Straight",
|
| 55 |
+
"Flush",
|
| 56 |
+
"Full House",
|
| 57 |
+
"Four of a Kind",
|
| 58 |
+
"Straight Flush",
|
| 59 |
+
],
|
| 60 |
+
)
|
| 61 |
+
odds.add_column(
|
| 62 |
+
"% Prob",
|
| 63 |
+
[
|
| 64 |
+
hc_pct,
|
| 65 |
+
pair_pct,
|
| 66 |
+
two_pair_pct,
|
| 67 |
+
three_ok_pct,
|
| 68 |
+
straight_pct,
|
| 69 |
+
flush_pct,
|
| 70 |
+
boat_pct,
|
| 71 |
+
quads_pct,
|
| 72 |
+
strt_flush_pct,
|
| 73 |
+
],
|
| 74 |
+
)
|
| 75 |
+
odds.add_column(
|
| 76 |
+
"Odds",
|
| 77 |
+
[
|
| 78 |
+
hc_ratio,
|
| 79 |
+
pair_ratio,
|
| 80 |
+
two_pair_ratio,
|
| 81 |
+
three_ok_ratio,
|
| 82 |
+
straight_ratio,
|
| 83 |
+
flush_ratio,
|
| 84 |
+
boat_ratio,
|
| 85 |
+
quads_ratio,
|
| 86 |
+
strt_flush_ratio,
|
| 87 |
+
],
|
| 88 |
+
)
|
| 89 |
+
st.divider()
|
| 90 |
+
st.subheader("Odds")
|
| 91 |
+
st.write(table, "\n")
|
| 92 |
+
st.write(f"We ran your hand {sims} times. Here's the odds:\n")
|
| 93 |
+
st.write(odds, "\n")
|
| 94 |
+
st.divider()
|
| 95 |
+
|
| 96 |
+
if __name__ == "__main__":
|
| 97 |
+
run_simulation(["As", "Ah"], ["2s", "3s", "4s"], sims=10000)
|
poker_functions.py
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import random
|
| 2 |
+
from collections import Counter
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
card_values = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
|
| 6 |
+
ranks = ['2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K', 'A']
|
| 7 |
+
rank_value = dict(zip(ranks, card_values))
|
| 8 |
+
value_rank = dict(zip(card_values, ranks))
|
| 9 |
+
suits = ['c', 'd', 'h', 's']
|
| 10 |
+
hand_values = {'hc': 1,
|
| 11 |
+
'pair': 2,
|
| 12 |
+
'2pair': 3,
|
| 13 |
+
'3ok': 4,
|
| 14 |
+
'straight': 5,
|
| 15 |
+
'flush': 6,
|
| 16 |
+
'boat': 7,
|
| 17 |
+
'4ok': 8,
|
| 18 |
+
'straight_flush': 9
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
HAND_REGISTRY = []
|
| 22 |
+
|
| 23 |
+
##### CLASSES #####
|
| 24 |
+
|
| 25 |
+
@dataclass
|
| 26 |
+
class Card:
|
| 27 |
+
def __init__(self, card_str):
|
| 28 |
+
self.rank = str(card_str[0])
|
| 29 |
+
self.suit = card_str[1]
|
| 30 |
+
self.name = self.rank + self.suit
|
| 31 |
+
self.value = rank_value[self.rank]
|
| 32 |
+
|
| 33 |
+
def __str__(self):
|
| 34 |
+
return self.name
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def __getitem__(self, item):
|
| 38 |
+
if item == 'rank':
|
| 39 |
+
return self.rank
|
| 40 |
+
elif item == 'suit':
|
| 41 |
+
return self.suit
|
| 42 |
+
elif item == 'name':
|
| 43 |
+
return self.name
|
| 44 |
+
elif item == 'value':
|
| 45 |
+
return self.value
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@dataclass()
|
| 49 |
+
class Hand:
|
| 50 |
+
def __init__(self, type, high_value, low_value = 0, kicker=0):
|
| 51 |
+
"""Type = name of hand (e.g. Pair)
|
| 52 |
+
value = value of the hand (i.e. Straight Flush is the most valuable)
|
| 53 |
+
high_value = value. either the high card in straight or flush, the set in full house, the top pair in 2pair, etc
|
| 54 |
+
low_value = the lower pair in 2 pair or the pair in a full house
|
| 55 |
+
kicker = value of the kicker in the hand. Can be null
|
| 56 |
+
"""
|
| 57 |
+
kicker_rank = value_rank[kicker] if kicker in card_values else 0
|
| 58 |
+
low_rank = value_rank[low_value] if low_value in card_values else 0
|
| 59 |
+
self.type = type
|
| 60 |
+
self.hand_value = hand_values[type]
|
| 61 |
+
self.kicker = kicker
|
| 62 |
+
self.kicker_rank = kicker_rank
|
| 63 |
+
self.high_value = high_value
|
| 64 |
+
self.high_rank = value_rank[self.high_value]
|
| 65 |
+
self.low_value = low_value
|
| 66 |
+
self.low_rank = low_rank
|
| 67 |
+
|
| 68 |
+
def __str__(self):
|
| 69 |
+
return f'{self.type}-{self.high_rank}'
|
| 70 |
+
|
| 71 |
+
def __getitem__(self, item):
|
| 72 |
+
if item in ['hand_value', 'high_value']:
|
| 73 |
+
return self.high_value
|
| 74 |
+
elif item == 'high_rank':
|
| 75 |
+
return self.high_rank
|
| 76 |
+
elif item == 'kicker':
|
| 77 |
+
return self.kicker
|
| 78 |
+
elif item == 'kicker_rank':
|
| 79 |
+
return self.kicker_rank
|
| 80 |
+
elif item == 'low_rank':
|
| 81 |
+
return self.low_rank
|
| 82 |
+
elif item == 'low_value':
|
| 83 |
+
return self.low_value
|
| 84 |
+
elif item == 'type':
|
| 85 |
+
return self.type
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class Deck(list):
|
| 89 |
+
def __init__(self, deck):
|
| 90 |
+
self.deck = deck
|
| 91 |
+
|
| 92 |
+
def __getitem__(self, item):
|
| 93 |
+
return self.deck[item]
|
| 94 |
+
|
| 95 |
+
def __iter__(self):
|
| 96 |
+
yield from self.deck
|
| 97 |
+
|
| 98 |
+
def __len__(self):
|
| 99 |
+
return len(self.deck)
|
| 100 |
+
|
| 101 |
+
def deal_card(self):
|
| 102 |
+
"""Select a random card from the deck. Return the card and the deck with the card removed"""
|
| 103 |
+
i = random.randint(0, len(self)-1)
|
| 104 |
+
card = self[i]
|
| 105 |
+
self.deck.pop(i)
|
| 106 |
+
return card, self
|
| 107 |
+
|
| 108 |
+
def update_deck(self, card):
|
| 109 |
+
"""Remove card from deck"""
|
| 110 |
+
deck_names = [card.name for card in self.deck]
|
| 111 |
+
card_name = card.name if isinstance(card, Card) else card
|
| 112 |
+
deck_idx = deck_names.index(card_name)
|
| 113 |
+
self.deck.pop(deck_idx)
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
##### USEFUL FUNCTIONS #####
|
| 117 |
+
|
| 118 |
+
def register(func):
|
| 119 |
+
"""Add a function to the hand register"""
|
| 120 |
+
HAND_REGISTRY.append(func)
|
| 121 |
+
return func
|
| 122 |
+
|
| 123 |
+
def make_card(input_list):
|
| 124 |
+
"""Input_list is either a list of Card objects or string Objects. If Cards, return the cards.
|
| 125 |
+
If string, convert to Card and return"""
|
| 126 |
+
if len(input_list) == 0:
|
| 127 |
+
return input_list
|
| 128 |
+
elif isinstance(input_list[0], Card):
|
| 129 |
+
return input_list
|
| 130 |
+
else:
|
| 131 |
+
card_list = [Card(card) for card in input_list]
|
| 132 |
+
return card_list
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def generate_deck():
|
| 136 |
+
deck = []
|
| 137 |
+
for rank in ranks:
|
| 138 |
+
for suit in suits:
|
| 139 |
+
card_str = rank + suit
|
| 140 |
+
_card = Card(card_str)
|
| 141 |
+
deck.append(_card)
|
| 142 |
+
deck = Deck(deck)
|
| 143 |
+
return deck
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
##### POKER #####
|
| 147 |
+
def find_multiple(hand, board, n=2):
|
| 148 |
+
"""Is there a pair, three of a kind, four of a kind/?"""
|
| 149 |
+
hand = make_card(hand)
|
| 150 |
+
board = make_card(board)
|
| 151 |
+
multiple = False
|
| 152 |
+
multiple_hand = None
|
| 153 |
+
total_hand = hand + board
|
| 154 |
+
values = [card.value for card in total_hand]
|
| 155 |
+
c = Counter(values)
|
| 156 |
+
for value in set(values):
|
| 157 |
+
if c[value] == 2 and n == 2:
|
| 158 |
+
multiple = True
|
| 159 |
+
hand_type = 'pair'
|
| 160 |
+
high_value = value
|
| 161 |
+
low_value = max([value for value in values if value != high_value])
|
| 162 |
+
kicker = max([value for value in values if value not in [high_value, low_value]])
|
| 163 |
+
multiple_hand = Hand(hand_type, high_value, low_value=low_value, kicker=kicker)
|
| 164 |
+
return multiple_hand
|
| 165 |
+
elif c[value] == 3 and n == 3:
|
| 166 |
+
multiple = True
|
| 167 |
+
hand_type = '3ok'
|
| 168 |
+
high_value = value
|
| 169 |
+
low_value = max([foo for foo in values if foo != high_value])
|
| 170 |
+
kicker = max([bar for bar in values if bar not in [high_value, low_value]])
|
| 171 |
+
multiple_hand = Hand(hand_type, high_value, low_value=low_value, kicker=kicker)
|
| 172 |
+
return multiple_hand
|
| 173 |
+
elif c[value] == 4 and n == 4:
|
| 174 |
+
multiple = True
|
| 175 |
+
hand_type = '4ok'
|
| 176 |
+
high_value = value
|
| 177 |
+
low_value = max([value for value in values if value != high_value])
|
| 178 |
+
multiple_hand = Hand(hand_type, high_value, low_value=low_value)
|
| 179 |
+
return multiple_hand
|
| 180 |
+
return multiple
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def evaluate_straight(values):
|
| 184 |
+
"""Evaluates a list of card values to determine whether there are 5 consecutive values"""
|
| 185 |
+
straight = False
|
| 186 |
+
count = 0
|
| 187 |
+
straight_hand_values = []
|
| 188 |
+
sranks = [bit for bit in reversed(range(2, 15))]
|
| 189 |
+
sranks.append(14)
|
| 190 |
+
for rank in sranks:
|
| 191 |
+
if rank in values:
|
| 192 |
+
count += 1
|
| 193 |
+
straight_hand_values.append(rank)
|
| 194 |
+
if count == 5:
|
| 195 |
+
straight = True
|
| 196 |
+
return straight, straight_hand_values
|
| 197 |
+
else:
|
| 198 |
+
count = 0
|
| 199 |
+
straight_hand_values = []
|
| 200 |
+
return straight, straight_hand_values
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
@register
|
| 204 |
+
def find_straight_flush(hand, board):
|
| 205 |
+
"""Find a straight flush in a given hand/board combination"""
|
| 206 |
+
hand = make_card(hand)
|
| 207 |
+
board = make_card(board)
|
| 208 |
+
straight_flush = False
|
| 209 |
+
flush = find_flush(hand, board)
|
| 210 |
+
if flush:
|
| 211 |
+
total_hand = hand + board
|
| 212 |
+
total_hand = [card for card in total_hand]
|
| 213 |
+
hand_suits = [card.suit for card in total_hand]
|
| 214 |
+
c = Counter(hand_suits)
|
| 215 |
+
flush_suit = c.most_common(1)[0][0]
|
| 216 |
+
flush_hand = [card.value for card in total_hand if card.suit == flush_suit]
|
| 217 |
+
straight_flush, straight_hand = evaluate_straight(flush_hand)
|
| 218 |
+
if straight_flush:
|
| 219 |
+
high_value = max(straight_hand)
|
| 220 |
+
hand_type = 'straight_flush'
|
| 221 |
+
straight_flush_hand = Hand(hand_type,high_value)
|
| 222 |
+
return straight_flush_hand
|
| 223 |
+
else:
|
| 224 |
+
return straight_flush
|
| 225 |
+
else:
|
| 226 |
+
return straight_flush
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
@register
|
| 230 |
+
def find_quads(hand, board):
|
| 231 |
+
quads = find_multiple(hand, board, n=4)
|
| 232 |
+
return quads
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
@register
|
| 236 |
+
def find_full_house(hand, board):
|
| 237 |
+
"""Is there a full house?"""
|
| 238 |
+
hand = make_card(hand)
|
| 239 |
+
board = make_card(board)
|
| 240 |
+
boat = False
|
| 241 |
+
boat_hand = None
|
| 242 |
+
total_hand = hand + board
|
| 243 |
+
values = [card.value for card in total_hand]
|
| 244 |
+
c = Counter(values)
|
| 245 |
+
for value in set(values):
|
| 246 |
+
if c[value] == 3:
|
| 247 |
+
high_value = value
|
| 248 |
+
c.pop(value)
|
| 249 |
+
for value in set(values):
|
| 250 |
+
if c[value] > 1:
|
| 251 |
+
low_value = value
|
| 252 |
+
kicker = max([value for value in values if value != high_value and value != low_value])
|
| 253 |
+
boat_hand = Hand('boat', high_value, low_value=low_value, kicker=kicker)
|
| 254 |
+
boat = True
|
| 255 |
+
return boat_hand
|
| 256 |
+
return boat
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
@register
|
| 260 |
+
def find_flush(hand, board):
|
| 261 |
+
"""Does any combination of 5 cards in hand or on board amount to 5 of the same suit"""
|
| 262 |
+
hand = make_card(hand)
|
| 263 |
+
board = make_card(board)
|
| 264 |
+
total_hand = hand + board
|
| 265 |
+
total_hand_suits = [card.suit for card in total_hand]
|
| 266 |
+
flush = False
|
| 267 |
+
c = Counter(total_hand_suits)
|
| 268 |
+
for suit in total_hand_suits:
|
| 269 |
+
if c[suit] >= 5:
|
| 270 |
+
flush = True
|
| 271 |
+
if flush:
|
| 272 |
+
flush_cards = [card for card in total_hand if card.suit == c.most_common(1)[0][0]]
|
| 273 |
+
high_value = max([card.value for card in flush_cards])
|
| 274 |
+
flush_hand = Hand('flush', high_value)
|
| 275 |
+
return flush_hand
|
| 276 |
+
else:
|
| 277 |
+
return flush
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
@register
|
| 281 |
+
def find_straight(hand, board):
|
| 282 |
+
"""Find a straight in a given hand/board combination"""
|
| 283 |
+
hand = make_card(hand)
|
| 284 |
+
board = make_card(board)
|
| 285 |
+
straight = False
|
| 286 |
+
straight_hand = None
|
| 287 |
+
high_value = 2
|
| 288 |
+
reqd_hand_size = 5 # required hand size gives us some flexibility at the cost of more lines. could be more efficient if we say 'if len(values)<5'
|
| 289 |
+
total_hand = hand + board
|
| 290 |
+
values = [*set(card.value for card in total_hand)]
|
| 291 |
+
slices = len(values) - reqd_hand_size
|
| 292 |
+
if slices < 0:
|
| 293 |
+
return straight
|
| 294 |
+
else:
|
| 295 |
+
straight, straight_hand_values = evaluate_straight(values)
|
| 296 |
+
if straight:
|
| 297 |
+
hand_type = 'straight'
|
| 298 |
+
if 14 in straight_hand_values: # all([5,14]) does not work here so using nested ifs.
|
| 299 |
+
if 5 in straight_hand_values:
|
| 300 |
+
high_value = 5
|
| 301 |
+
else:
|
| 302 |
+
high_value = max(straight_hand_values)
|
| 303 |
+
straight_hand = Hand(hand_type, high_value)
|
| 304 |
+
return straight_hand
|
| 305 |
+
else:
|
| 306 |
+
return straight
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
@register
|
| 310 |
+
def find_trips(hand, board):
|
| 311 |
+
trips = find_multiple(hand, board, n=3)
|
| 312 |
+
return trips
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
@register
|
| 316 |
+
def find_two_pair(hand, board):
|
| 317 |
+
"""Is there two-pair?"""
|
| 318 |
+
hand = make_card(hand)
|
| 319 |
+
board = make_card(board)
|
| 320 |
+
two_pair = False
|
| 321 |
+
# two_pair_hand = None
|
| 322 |
+
total_hand = hand + board
|
| 323 |
+
values = [card.value for card in total_hand]
|
| 324 |
+
c = Counter(values)
|
| 325 |
+
for value in values:
|
| 326 |
+
if c[value] > 1:
|
| 327 |
+
pair1 = Hand('pair', value)
|
| 328 |
+
c.pop(value)
|
| 329 |
+
for value in values:
|
| 330 |
+
if c[value] > 1:
|
| 331 |
+
pair2 = Hand('pair', value)
|
| 332 |
+
kicker = max([value for value in values if value != pair1.high_value and value != pair2.high_value])
|
| 333 |
+
two_pair_hand = Hand('2pair', max(pair1.high_value, pair2.high_value), low_value=min(pair1.high_value, pair2.high_value), kicker=kicker)
|
| 334 |
+
two_pair = True
|
| 335 |
+
return two_pair_hand
|
| 336 |
+
return two_pair
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
@register
|
| 340 |
+
def find_pair(hand, board):
|
| 341 |
+
pair = find_multiple(hand, board, n=2)
|
| 342 |
+
return pair
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
@register
|
| 346 |
+
def find_high_card(hand, board):
|
| 347 |
+
hand = make_card(hand)
|
| 348 |
+
board = make_card(board)
|
| 349 |
+
total_hand = hand + board
|
| 350 |
+
total_hand_values = [card.value for card in total_hand]
|
| 351 |
+
total_hand_values.sort()
|
| 352 |
+
high_value = total_hand_values[-1]
|
| 353 |
+
low_value = total_hand_values[-2]
|
| 354 |
+
kicker = total_hand_values[-3]
|
| 355 |
+
high_card_hand = Hand('hc', high_value,low_value=low_value, kicker=kicker)
|
| 356 |
+
return high_card_hand
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit
|
| 2 |
+
extra-streamlit-components
|
| 3 |
+
landingai
|
| 4 |
+
prettytable
|
simulation.py
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import poker_functions as p
|
| 2 |
+
from fractions import Fraction
|
| 3 |
+
from collections import Counter
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class Player:
|
| 7 |
+
def __init__(self, number, cards=[]):
|
| 8 |
+
if len(cards) > 0:
|
| 9 |
+
cards = p.make_card(cards)
|
| 10 |
+
else:
|
| 11 |
+
cards = []
|
| 12 |
+
self.number = number
|
| 13 |
+
self.cards = cards
|
| 14 |
+
self.hand = None
|
| 15 |
+
self.starting_cards = None
|
| 16 |
+
self.wins = 0
|
| 17 |
+
|
| 18 |
+
def __str__(self):
|
| 19 |
+
return "player_" + str(self.number)
|
| 20 |
+
|
| 21 |
+
def dedup(board):
|
| 22 |
+
duplicate = False
|
| 23 |
+
c = Counter(board)
|
| 24 |
+
for card in board:
|
| 25 |
+
if c[card] > 1:
|
| 26 |
+
duplicate = True
|
| 27 |
+
return duplicate
|
| 28 |
+
return duplicate
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def validate_card(check):
|
| 32 |
+
"""Detect invalid cards in a passed collection"""
|
| 33 |
+
valid = True
|
| 34 |
+
deck = p.generate_deck()
|
| 35 |
+
valid_cards = [card.name for card in deck]
|
| 36 |
+
for card in check:
|
| 37 |
+
if card not in valid_cards:
|
| 38 |
+
valid = False
|
| 39 |
+
return valid
|
| 40 |
+
return valid
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def convert_and_update(deck, cards):
|
| 44 |
+
if len(cards) == 0:
|
| 45 |
+
return deck, cards
|
| 46 |
+
else:
|
| 47 |
+
cards = p.make_card(cards)
|
| 48 |
+
for card in cards:
|
| 49 |
+
deck.update_deck(card)
|
| 50 |
+
return deck, cards
|
| 51 |
+
|
| 52 |
+
##### SIMULATIONS #####
|
| 53 |
+
def evaluate_hand(hole_cards, flop=[], turn=[], river=[]):
|
| 54 |
+
board = flop + turn + river
|
| 55 |
+
hand = None
|
| 56 |
+
if len(hole_cards + board) < 5:
|
| 57 |
+
return hand
|
| 58 |
+
else:
|
| 59 |
+
for func in p.HAND_REGISTRY:
|
| 60 |
+
func = func(hole_cards, board)
|
| 61 |
+
if not func:
|
| 62 |
+
continue
|
| 63 |
+
else:
|
| 64 |
+
return func
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def score_game(contestants):
|
| 68 |
+
# TODO make this more elegant by functionizing repeated code.
|
| 69 |
+
"""Application will determine the highest hand, including low and kicker for each player in player_list"""
|
| 70 |
+
high = ['flush', 'straight', 'straight_flush']
|
| 71 |
+
kick = ['4ok']
|
| 72 |
+
hi_lo = ['boat']
|
| 73 |
+
hi_lo_kick = ['2pair', 'hc', '3ok', 'pair']
|
| 74 |
+
high_hand = max(contestants, key=lambda x: x.hand.hand_value) # contestant with highest hand
|
| 75 |
+
same_high_hand = [player for player in contestants if player.hand.hand_value == high_hand.hand.hand_value]
|
| 76 |
+
if len(same_high_hand) == 1:
|
| 77 |
+
same_high_hand[0].wins += 1
|
| 78 |
+
return contestants
|
| 79 |
+
elif high_hand.hand.type in high:
|
| 80 |
+
high_card = max(same_high_hand, key=lambda x: x.hand.high_value)
|
| 81 |
+
same_high_card = [player for player in same_high_hand if player.hand.high_value == high_card.hand.high_value]
|
| 82 |
+
if len(same_high_card) == 1:
|
| 83 |
+
high_card.wins += 1
|
| 84 |
+
return contestants
|
| 85 |
+
else:
|
| 86 |
+
return contestants
|
| 87 |
+
elif high_hand.hand.type in hi_lo:
|
| 88 |
+
over = max(same_high_hand, key=lambda x: x.hand.high_value) # Highest pair in hand
|
| 89 |
+
same_over = [player for player in same_high_hand if player.hand.high_value == over.hand.high_value]
|
| 90 |
+
if len(same_over) == 1:
|
| 91 |
+
over.wins += 1
|
| 92 |
+
return contestants
|
| 93 |
+
else:
|
| 94 |
+
under = max(same_over, key=lambda x: x.hand.low_value) # lowest pair in hand
|
| 95 |
+
same_under = [player for player in same_over if player.hand.low_value == under.hand.low_value]
|
| 96 |
+
if len(same_under) == 1:
|
| 97 |
+
under.wins += 1
|
| 98 |
+
return contestants
|
| 99 |
+
else:
|
| 100 |
+
return contestants
|
| 101 |
+
elif high_hand.hand.type in hi_lo_kick:
|
| 102 |
+
over = max(same_high_hand, key=lambda x: x.hand.high_value) # Highest pair in hand
|
| 103 |
+
same_over = [player for player in same_high_hand if player.hand.high_value == over.hand.high_value]
|
| 104 |
+
if len(same_over) == 1:
|
| 105 |
+
over.wins += 1
|
| 106 |
+
return contestants
|
| 107 |
+
else:
|
| 108 |
+
under = max(same_over, key=lambda x: x.hand.low_value) # lowest pair in hand
|
| 109 |
+
same_under = [player for player in same_over if player.hand.low_value == under.hand.low_value]
|
| 110 |
+
if len(same_under) == 1:
|
| 111 |
+
under.wins += 1
|
| 112 |
+
return contestants
|
| 113 |
+
else:
|
| 114 |
+
kicker = max(same_under, key=lambda x: x.hand.kicker)
|
| 115 |
+
same_kicker = [player for player in same_under if player.hand.kicker == kicker.hand.kicker]
|
| 116 |
+
if len(same_kicker) == 1:
|
| 117 |
+
kicker.wins += 1
|
| 118 |
+
return contestants
|
| 119 |
+
else:
|
| 120 |
+
return contestants
|
| 121 |
+
elif high_hand.hand.type in kick:
|
| 122 |
+
low_val = max(same_high_hand, key=lambda x: x.hand.low_value)
|
| 123 |
+
same_low_val = [player for player in same_high_hand if player.hand.low_value == low_val.hand.low_value]
|
| 124 |
+
if len(same_low_val) == 1:
|
| 125 |
+
low_val.wins += 1
|
| 126 |
+
return contestants
|
| 127 |
+
else:
|
| 128 |
+
return contestants
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def simulation_one_player(hole, flop=None, turn=None, river=None, sims=100000):
|
| 132 |
+
if flop is None:
|
| 133 |
+
flop = []
|
| 134 |
+
if turn is None:
|
| 135 |
+
turn = []
|
| 136 |
+
if river is None:
|
| 137 |
+
river = []
|
| 138 |
+
full_board = 7 # number of cards required to run sim
|
| 139 |
+
passed_cards = len(hole) + len(flop) + len(turn) + len(river)
|
| 140 |
+
passed_flop = list(flop)
|
| 141 |
+
high_cards = 0
|
| 142 |
+
pairs = 0
|
| 143 |
+
two_pairs = 0
|
| 144 |
+
trips = 0
|
| 145 |
+
straights = 0
|
| 146 |
+
flushes = 0
|
| 147 |
+
boats = 0
|
| 148 |
+
quads = 0
|
| 149 |
+
straight_flushes = 0
|
| 150 |
+
invalid = 0
|
| 151 |
+
for i in range(sims):
|
| 152 |
+
deck = p.generate_deck()
|
| 153 |
+
deck, hole = convert_and_update(deck, hole)
|
| 154 |
+
deck, flop = convert_and_update(deck, flop)
|
| 155 |
+
deck, turn = convert_and_update(deck, turn)
|
| 156 |
+
deck, river = convert_and_update(deck, river)
|
| 157 |
+
j = full_board - passed_cards
|
| 158 |
+
for _ in range(j):
|
| 159 |
+
deal, deck = deck.deal_card()
|
| 160 |
+
flop.append(deal) # Adding to flop because it shouldn't matter, will revert flop back at end of loop
|
| 161 |
+
hand = evaluate_hand(hole, flop, turn, river)
|
| 162 |
+
if hand.type == '2pair':
|
| 163 |
+
two_pairs += 1
|
| 164 |
+
elif hand.type == '3ok':
|
| 165 |
+
trips += 1
|
| 166 |
+
elif hand.type == '4ok':
|
| 167 |
+
quads += 1
|
| 168 |
+
elif hand.type == 'boat':
|
| 169 |
+
boats += 1
|
| 170 |
+
elif hand.type == 'flush':
|
| 171 |
+
flushes += 1
|
| 172 |
+
elif hand.type == 'hc':
|
| 173 |
+
high_cards += 1
|
| 174 |
+
elif hand.type == 'pair':
|
| 175 |
+
pairs += 1
|
| 176 |
+
elif hand.type == 'straight':
|
| 177 |
+
straights += 1
|
| 178 |
+
elif hand.type == 'straight_flush':
|
| 179 |
+
straight_flushes += 1
|
| 180 |
+
else:
|
| 181 |
+
invalid += 1
|
| 182 |
+
i += 1
|
| 183 |
+
flop = list(passed_flop)
|
| 184 |
+
return sims, high_cards, pairs, two_pairs, trips, straights, flushes, boats, quads, straight_flushes
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def simulation_multiplayer(hole_one, hole_two=[], hole_three=[], hole_four=[], hole_five=[], hole_six=[],
|
| 188 |
+
flop = [], turn = [], river = [], opponents=2, sims=10000):
|
| 189 |
+
contestant_hands = [hole_one, hole_two, hole_three, hole_four, hole_five, hole_six]
|
| 190 |
+
contestants = []
|
| 191 |
+
flop = p.make_card(flop)
|
| 192 |
+
turn = p.make_card(turn)
|
| 193 |
+
river = p.make_card(river)
|
| 194 |
+
passed_flop_stable = [card for card in flop]
|
| 195 |
+
for n in range(opponents):
|
| 196 |
+
player_name = 'opponent' + str(n+1)
|
| 197 |
+
player_name = Player(n, contestant_hands[n])
|
| 198 |
+
contestants.append(player_name)
|
| 199 |
+
i = 0
|
| 200 |
+
passed_board = len(flop) + len(turn) + len(river)
|
| 201 |
+
full_board = 5
|
| 202 |
+
k = full_board - passed_board
|
| 203 |
+
for i in range(sims):
|
| 204 |
+
deck = p.generate_deck()
|
| 205 |
+
for contestant in contestants: # TODO move assigning Player.starting_cards to init
|
| 206 |
+
if len(contestant.cards) == 2:
|
| 207 |
+
contestant.starting_cards = True
|
| 208 |
+
for card in contestant.cards:
|
| 209 |
+
deck.update_deck(card) # remove known hole cards from deck
|
| 210 |
+
else:
|
| 211 |
+
contestant.starting_cards = False
|
| 212 |
+
hole_cards = []
|
| 213 |
+
for j in range(2):
|
| 214 |
+
deal, deck = deck.deal_card()
|
| 215 |
+
hole_cards.append(deal)
|
| 216 |
+
contestant.cards = hole_cards # assign new hole cards if not passed
|
| 217 |
+
for l in range(k): # complete the board as needed
|
| 218 |
+
deal, deck = deck.deal_card()
|
| 219 |
+
flop.append(deal)
|
| 220 |
+
for contestant in contestants:
|
| 221 |
+
hand = evaluate_hand(contestant.cards, flop, turn, river)
|
| 222 |
+
contestant.hand = hand
|
| 223 |
+
# Compare hand values in contestants
|
| 224 |
+
contestants = score_game(contestants)
|
| 225 |
+
i += 1
|
| 226 |
+
# Revert to starting state
|
| 227 |
+
flop = [card for card in passed_flop_stable]
|
| 228 |
+
for contestant in contestants:
|
| 229 |
+
if contestant.starting_cards is False:
|
| 230 |
+
contestant.cards = []
|
| 231 |
+
hole_cards = []
|
| 232 |
+
return contestants
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
# TODO for single and mult: find and return most likely hand. Return number of outs and odds.
|
| 237 |
+
|
| 238 |
+
##### MATH #####
|
| 239 |
+
def percent(hits, sims):
|
| 240 |
+
percent = round((hits / sims) * 100,0)
|
| 241 |
+
return percent
|
| 242 |
+
|
| 243 |
+
def ratio(hits, sims):
|
| 244 |
+
"""Return a ratio (e.g. 3:5) for two input numbers"""
|
| 245 |
+
percent = round((hits / sims),2)
|
| 246 |
+
fraction = str(Fraction(percent).limit_denominator())
|
| 247 |
+
fraction = fraction.replace('/', ':')
|
| 248 |
+
return fraction
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
##### REFERENCE #####
|
| 252 |
+
outs = {'1':('46:1','45:1',"22:1"),
|
| 253 |
+
'2':('22:1','22:1','11:1'),
|
| 254 |
+
'3':('15:1', '14:1', '7:1'),
|
| 255 |
+
'4':('11:1','10:1','5:1'),
|
| 256 |
+
'5':('8.5:1', '8:1','4:1'),
|
| 257 |
+
'6':('7:1','7:1','3:1'),
|
| 258 |
+
'7':('6:1','6:1','2.5:1'),
|
| 259 |
+
'8':('5:1','5:1','2.5:1'),
|
| 260 |
+
'9':('4:1','4:1','2:1'),
|
| 261 |
+
'10':('3.5:1','3.5:1','1.5:1'),
|
| 262 |
+
'11':('3.3:1','3.2:1','1.5:1'),
|
| 263 |
+
'12':('3:1','3:1','1.2:1'),
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
rank_value = p.rank_value
|