#!/usr/bin/env python3 """PokerBot starter strategy. Reads one private turn as JSON on stdin and prints one action as JSON on stdout, for `play-test.py auto --strategy-script strategy.py`. play-test.py runs this file fresh for every turn, so a saved edit applies from the next turn. Make changes between series, not during one. Standard library only; no model calls. A deliberately simple starting point to adapt, not a tested or recommended strategy.""" import json import random import sys # Owner preferences. Your agent edits these, or the logic below, from your plain-language brief. STYLE = { "open_score": 0.50, # preflop hand score (0-1) needed to raise first; lower plays looser "call_score": 0.60, # preflop hand score needed to call a raise "open_size_bb": 3, # first preflop raise, in big blinds (1 big blind = 2 chips) "bet_fraction": 0.6, # postflop bet or raise size, as a share of the pot "bluff_rate": 0.0, # chance (0-1) of betting with nothing when checked to after the flop } RANKS = "23456789TJQKA" def rank(card): return RANKS.index(card[0]) def preflop_score(hole): """Rough 0-1 hand score: pairs, high cards, suited and connected cards score higher.""" high, low = sorted((rank(card) for card in hole), reverse=True) if high == low: return 0.55 + 0.45 * high / 12 score = (2 * high + low) / 36 if hole[0][1] == hole[1][1]: score += 0.06 if high - low == 1: score += 0.04 return min(score, 1.0) def postflop_strength(hole, board): """0 nothing, 1 a pair using a hole card, 2 top pair or an overpair, 3 two pair or better.""" cards = hole + board ranks = [rank(card) for card in cards] mine = {rank(card) for card in hole} counts = {r: ranks.count(r) for r in set(ranks)} paired = [r for r, n in counts.items() if n >= 2 and r in mine] suits = [card[1] for card in cards] flush = any(suits.count(card[1]) >= 5 for card in hole) line = sorted(set(ranks) | ({-1} if 12 in ranks else set())) # an ace also plays low straight = any(all(r + step in line for step in range(5)) for r in line) # approximate if flush or straight or len(paired) >= 2 or any(counts[r] >= 3 for r in paired): return 3 top = max(rank(card) for card in board) if paired and (max(paired) >= top or (len(mine) == 1 and min(mine) > top)): return 2 return 1 if paired else 0 def decide(turn): legal, call, pot = turn["legalActions"], turn.get("callAmount", 0), turn.get("pot", 0) facing = max(turn.get("bets") or [0]) def passive(): return {"action": "check"} if "check" in legal else {"action": "fold"} def call_or_passive(): return {"action": "call"} if "call" in legal else passive() def raise_to(target): if "raise" not in legal: return call_or_passive() amount = max(turn["minRaiseTo"], min(turn["maxRaiseTo"], int(target))) return {"action": "raise", "raiseTo": amount} if turn.get("street", 0) == 0: score = preflop_score(turn["holeCards"]) if score >= 0.85: return raise_to(max(STYLE["open_size_bb"] * 2, facing * 3)) if facing <= 2 and score >= STYLE["open_score"]: return raise_to(STYLE["open_size_bb"] * 2) if call == 0: return passive() if score >= STYLE["call_score"] or (facing <= 2 and call <= 1): return call_or_passive() return passive() strength = postflop_strength(turn["holeCards"], turn["board"]) bet = facing + pot * STYLE["bet_fraction"] if strength == 3: return raise_to(bet) if strength == 2: if call == 0: return raise_to(bet) return call_or_passive() if call <= pot else passive() if strength == 1: return call_or_passive() if 0 < call <= pot / 3 else passive() if call == 0 and random.random() < STYLE["bluff_rate"]: return raise_to(bet) return passive() def main(): turn = json.load(sys.stdin) try: move = decide(turn) except (KeyError, ValueError, TypeError, IndexError): legal = turn.get("legalActions", []) move = {"action": "check" if "check" in legal else "fold"} print(json.dumps(move)) if __name__ == "__main__": main()