diff options
author | Paul Harrison <paul@harrison.sh> | 2022-11-20 15:13:16 +0000 |
---|---|---|
committer | Paul Harrison <paul@harrison.sh> | 2022-12-15 16:02:14 +0000 |
commit | 9baafc80ed889c232587cf5d4cfaa2db44d1825a (patch) | |
tree | 13371c1220a8eabbc0932d30cc9475b98b984c3b /poker | |
parent | b4ba284dcc20563b9b7dde770b7c9f67c6b25e76 (diff) |
feat: Hand ranking API
Hand ranking API with a health check root endpoint and rank endpoint.
Diffstat (limited to 'poker')
-rw-r--r-- | poker/api.py | 56 | ||||
-rw-r--r-- | poker/constants.py | 7 | ||||
-rw-r--r-- | poker/rank/hands.py | 4 |
3 files changed, 63 insertions, 4 deletions
diff --git a/poker/api.py b/poker/api.py new file mode 100644 index 0000000..9faa89b --- /dev/null +++ b/poker/api.py @@ -0,0 +1,56 @@ +from fastapi import Body, FastAPI +from loguru import logger + +from poker.constants import Suit, Value +from poker.models import Card, Hand +from poker.rank import rank_hand + +app = FastAPI() + +_CARD_PATTERN = r"\s".join(5 * ["(2|3|4|5|6|7|8|9|10|J|K|Q|A)[CDHS]"]) +_SUIT_MAP = { + "C": Suit.CLUBS, + "D": Suit.DIAMONDS, + "H": Suit.HEARTS, + "S": Suit.SPADES, +} +_VALUE_MAP = { + "2": Value.TWO, + "3": Value.THREE, + "4": Value.FOUR, + "5": Value.FIVE, + "6": Value.SIX, + "7": Value.SEVEN, + "8": Value.EIGHT, + "9": Value.NINE, + "10": Value.TEN, + "J": Value.JACK, + "Q": Value.QUEEN, + "K": Value.KING, + "A": Value.ACE, +} + + +@app.get("/") +async def health_check() -> str: + """Health check endpoint.""" + return "OK" + + +@app.post("/rank") +def rank(body: str = Body(regex=_CARD_PATTERN, example="2H 3D 5S 10C KD")) -> str: + """Rank hand. + + TODO: Improve error response. + """ + logger.info(f"Input hand: {body}") + cards = [ + Card(suit=_SUIT_MAP[card[-1]], value=_VALUE_MAP[card[:-1]]) + for card in body.split() + ] + + ranked_hand = rank_hand(Hand(cards=cards)) + + logger.info(f"Hand rank: {ranked_hand.rank}") + + return ranked_hand.description diff --git a/poker/constants.py b/poker/constants.py index e0a7348..6402ef7 100644 --- a/poker/constants.py +++ b/poker/constants.py @@ -17,6 +17,11 @@ class Rank(IntEnum): PAIR = 9 HIGH_CARD = 10 + def __str__(self) -> str: + """Return string representation.""" + out: str = self.name.lower().replace("_", " ").title() + return out + class Suit(AutoName): """Card suit enum.""" @@ -46,7 +51,7 @@ class Value(IntEnum): def __str__(self) -> str: """Return string representation.""" - if self.value in [1, 11, 12, 13]: + if self.value in [1, 11, 12, 13, 14]: out: str = self.name.lower() else: out = str(self.value) diff --git a/poker/rank/hands.py b/poker/rank/hands.py index 6a81af5..a622f87 100644 --- a/poker/rank/hands.py +++ b/poker/rank/hands.py @@ -162,7 +162,7 @@ def rank_hand(hand: Hand) -> RankedHand: for rank, func in _FUNCTIONS.items(): result = func(hand) if result: - out = RankedHand( + return RankedHand( cards=hand.cards, rank=rank, description=DESCRIPTIONS[rank].format(**result), @@ -170,5 +170,3 @@ def rank_hand(hand: Hand) -> RankedHand: if out is None: raise ValueError("No rank found.") - - return out |