109 lines
3.2 KiB
Python
109 lines
3.2 KiB
Python
"""
|
|
core/checker.py
|
|
---------------
|
|
Ticket checker — compare a user-supplied ticket against all historical draws.
|
|
Returns every draw where at least one number (main or bonus) matched,
|
|
sorted by match quality descending.
|
|
|
|
Prize tiers follow Powerball / Mega Millions conventions:
|
|
Jackpot 5 main + bonus
|
|
Match 5 5 main, no bonus
|
|
Match 4 + Bonus 4 main + bonus
|
|
Match 4 4 main
|
|
Match 3 + Bonus 3 main + bonus
|
|
Match 3 3 main
|
|
Match 2 + Bonus 2 main + bonus
|
|
Match 1 + Bonus 1 main + bonus
|
|
Bonus Only 0 main + bonus
|
|
"""
|
|
|
|
from db.models import get_all_draws_numbers, get_game_by_id
|
|
|
|
# (required_main_matches, requires_bonus_match, tier_label)
|
|
_TIERS = [
|
|
(5, True, "Jackpot"),
|
|
(5, False, "Match 5"),
|
|
(4, True, "Match 4 + Bonus"),
|
|
(4, False, "Match 4"),
|
|
(3, True, "Match 3 + Bonus"),
|
|
(3, False, "Match 3"),
|
|
(2, True, "Match 2 + Bonus"),
|
|
(1, True, "Match 1 + Bonus"),
|
|
(0, True, "Bonus Only"),
|
|
]
|
|
|
|
|
|
def prize_tier(main_matches: int, bonus_match: bool) -> str:
|
|
"""Return the prize tier label for a given match result."""
|
|
for req_main, req_bonus, label in _TIERS:
|
|
if main_matches == req_main:
|
|
if req_bonus and not bonus_match:
|
|
continue
|
|
return label
|
|
return "No Prize"
|
|
|
|
|
|
def check_ticket(game_id: int, numbers: list[int],
|
|
bonus: int | None = None) -> list[dict]:
|
|
"""
|
|
Compare *numbers* (and optional *bonus*) against all draws for *game_id*.
|
|
|
|
Returns a list of dicts for draws with ≥ 1 matched number (main or bonus),
|
|
sorted by (main_matches DESC, bonus_match DESC, draw_date DESC).
|
|
|
|
Each dict contains:
|
|
draw_date str
|
|
draw_numbers list[int]
|
|
draw_bonus int | None
|
|
main_matches int
|
|
bonus_match bool
|
|
prize_tier str
|
|
"""
|
|
draws = get_all_draws_numbers(game_id)
|
|
ticket_set = set(numbers)
|
|
results = []
|
|
|
|
for draw in draws:
|
|
draw_set = set(draw["numbers"])
|
|
main_matches = len(ticket_set & draw_set)
|
|
bonus_match = (
|
|
bonus is not None
|
|
and draw["bonus"] is not None
|
|
and bonus == draw["bonus"]
|
|
)
|
|
|
|
if main_matches == 0 and not bonus_match:
|
|
continue
|
|
|
|
results.append({
|
|
"draw_date": draw["draw_date"],
|
|
"draw_numbers": draw["numbers"],
|
|
"draw_bonus": draw["bonus"],
|
|
"main_matches": main_matches,
|
|
"bonus_match": bonus_match,
|
|
"prize_tier": prize_tier(main_matches, bonus_match),
|
|
})
|
|
|
|
results.sort(
|
|
key=lambda x: (x["main_matches"], x["bonus_match"], x["draw_date"]),
|
|
reverse=True,
|
|
)
|
|
return results
|
|
|
|
|
|
def parse_numbers(raw: str) -> list[int]:
|
|
"""
|
|
Parse a user-typed string of lottery numbers into a sorted list of ints.
|
|
Accepts space- or comma-separated input: '7 14 32 56 68' or '7,14,32,56,68'.
|
|
Raises ValueError if any token is not a positive integer.
|
|
"""
|
|
tokens = raw.replace(",", " ").split()
|
|
if not tokens:
|
|
raise ValueError("No numbers entered.")
|
|
nums = []
|
|
for t in tokens:
|
|
if not t.isdigit():
|
|
raise ValueError(f"'{t}' is not a valid number.")
|
|
nums.append(int(t))
|
|
return sorted(nums)
|