#!/usr/bin/env python3 """ Standalone CLI script for Teen Patti pattern analysis. Usage: python analyze.py --host localhost --port 8123 """ import argparse import sys def fmt_pct(n, total): return f"{n/total*100:.1f}%" if total else "0.0%" def print_table(headers, rows, col_widths=None): """Print a simple formatted table.""" if not col_widths: col_widths = [max(len(str(h)), *(len(str(r[i])) for r in rows)) for i, h in enumerate(headers)] # Header hdr = " ".join(str(h).ljust(w) for h, w in zip(headers, col_widths)) print(hdr) print("-" * len(hdr)) for row in rows: print(" ".join(str(c).ljust(w) for c, w in zip(row, col_widths))) def main(): parser = argparse.ArgumentParser(description="Teen Patti Pattern Analysis CLI") parser.add_argument("--host", default="localhost", help="ClickHouse host") parser.add_argument("--port", type=int, default=8123, help="ClickHouse HTTP port") args = parser.parse_args() # Set config before importing db from app import config config.CLICKHOUSE_HOST = args.host config.CLICKHOUSE_PORT = args.port from app import db print(f"Connecting to ClickHouse at {args.host}:{args.port}...") try: data = db.get_pattern_analysis() except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) total = data["chair_bias"]["total_games"] print(f"\n{'='*60}") print(f" TEEN PATTI PATTERN ANALYSIS ({total:,} games)") print(f"{'='*60}\n") # 1. Chair Win Bias print("1. CHAIR WIN BIAS (expected 33.3%)") cb = data["chair_bias"] rows = [] for ch in ("A", "B", "C"): d = cb[ch] diff = d["pct"] - 33.3 sign = "+" if diff >= 0 else "" rows.append([ch, f"{d['wins']:,}", f"{d['pct']:.1f}%", f"{sign}{diff:.1f}%"]) print_table(["Chair", "Wins", "Win %", "vs Expected"], rows) # 2. Bet Rank Analysis print("\n2. BET RANK ANALYSIS") br = data["bet_rank"] br_total = br["high"] + br["mid"] + br["low"] rows = [] for rank in ("high", "mid", "low"): rows.append([rank.capitalize(), f"{br[rank]:,}", fmt_pct(br[rank], br_total)]) print_table(["Rank", "Wins", "Win %"], rows) # 3. Per-Chair Bet Rank print("\n3. PER-CHAIR: HIGHEST BET WIN RATE") print(" When chair X has the highest bet, how often does X win?") pcr = data["per_chair_rank"] rows = [] for ch in ("A", "B", "C"): d = pcr.get(ch, {}) rows.append([ch, f"{d.get('has_highest', 0):,}", f"{d.get('wins', 0):,}", f"{d.get('win_pct', 0):.1f}%"]) print_table(["Chair", "Times Highest", "Wins", "Win %"], rows) # 4. Hand Type Distribution by Chair print("\n4. HAND TYPE DISTRIBUTION BY CHAIR") htbc = data["hand_types_by_chair"] hand_order = ["Trail", "Straight Flush", "Straight", "Flush", "Pair", "High Card"] rows = [] for ht in hand_order: a = htbc["A"].get(ht, 0) b = htbc["B"].get(ht, 0) c = htbc["C"].get(ht, 0) if a + b + c == 0: continue rows.append([ht, f"{a:,}", f"{b:,}", f"{c:,}"]) print_table(["Hand Type", "Chair A", "Chair B", "Chair C"], rows) # 5. Hand Type Win Rates print("\n5. HAND TYPE WIN RATES") htw = data["hand_type_wins"] htw_total = sum(htw.values()) rows = [] for ht in hand_order: v = htw.get(ht, 0) if v == 0: continue rows.append([ht, f"{v:,}", fmt_pct(v, htw_total)]) print_table(["Hand Type", "Wins", "Win %"], rows) # 6. Pot Size Buckets print("\n6. WIN RATES BY POT SIZE") pb = data["pot_buckets"] ranges = pb.get("_ranges", {}) rows = [] for bucket in ("small", "medium", "large", "whale"): d = pb.get(bucket) if not d: continue t = d["total"] or 1 rows.append([ bucket.capitalize(), ranges.get(bucket, ""), f"{d['total']:,}", f"{d['A']/t*100:.1f}%", f"{d['B']/t*100:.1f}%", f"{d['C']/t*100:.1f}%", ]) print_table(["Bucket", "Range", "Games", "A %", "B %", "C %"], rows) # 7. Streaks print("\n7. STREAK ANALYSIS") streaks = data["streaks"] rows = [] for ch in ("A", "B", "C"): s = streaks[ch] rows.append([ch, str(s["max_streak"]), str(s["current_streak"])]) print_table(["Chair", "Max Streak", "Current Streak"], rows) # 8. Hourly Patterns print("\n8. HOURLY PATTERNS (win % by hour)") hourly = data["hourly"] hours = sorted(hourly.keys(), key=lambda h: int(h)) rows = [] for h in hours: d = hourly[h] t = d["total"] or 1 rows.append([ f"{int(h):02d}:00", str(d["total"]), f"{d['A']/t*100:.1f}%", f"{d['B']/t*100:.1f}%", f"{d['C']/t*100:.1f}%", ]) print_table(["Hour", "Games", "A %", "B %", "C %"], rows) # 9. Recent vs Overall print("\n9. RECENT (LAST 100) vs ALL-TIME") rva = data["recent_vs_all"] for label, section in [("All-Time", rva["all"]), ("Last 100", rva["recent"])]: t = section["total"] or 1 d = section["dist"] parts = " | ".join(f"{ch}: {d[ch]:>4} ({d[ch]/t*100:.1f}%)" for ch in ("A", "B", "C")) print(f" {label:>10} [{t:>5} games] {parts}") print(f"\n{'='*60}") print(" Done.") if __name__ == "__main__": main()