Your bot's brain is a plain-text file written in OpenPPL — the Open Poker Programming Language. No compiler, no SDK: you write human-readable rules like WHEN InButton AND listOpen RaiseTo 3 FORCE, save a .ohf file, load it, and send a mind to the felt. This guide takes you from an empty file to a thinking, opponent-aware profile.
A profile decides one thing each time it's your turn: what action to take. The client evaluates your rules, picks an action (fold / check / call / bet / raise) and a size, and POSTs it to the table:
POST /api/v1/tables/{id}/act → {"action":"raise","amount":150}
You never write that HTTP call — the client does. Your job is the decision. Bet sizes you express in big blinds or pot fractions are converted to the table's chip amount automatically. Opponent stats (VPIP, PFR, aggression…) are pulled live from the table's HUD, so your profile can adapt to who it's playing.
Everything is built from one shape:
WHEN <condition> <Action> FORCE
Rules are read top to bottom; the first one whose condition is true fires, and FORCE commits to that action. A profile is a set of named sections (functions and hand lists). Section headers are wrapped in ##:
##f$preflop## WHEN Raises = 0 AND InButton AND listOpenBTN RaiseTo 3 FORCE WHEN Raises = 1 AND listPremium RaiseBy 100% FORCE WHEN Others Fold FORCE
Comments start with //. You can group conditions with [ … ] and combine them with AND, OR, NOT. You can also return a value or another function with RETURN <expr> FORCE.
A profile must define four functions — one per betting round. The engine calls the right one for you:
| Function | When it runs |
|---|---|
f$preflop | Before the flop. |
f$flop | After the flop. |
f$turn | After the turn. |
f$river | After the river. |
Each one returns an action. A common pattern is to split "no bet to me" from "facing a bet":
##f$flop## WHEN AmountToCall > 0 RETURN f$flop_vs_bet FORCE WHEN Others RETURN f$flop_bet FORCE
| Action | Meaning |
|---|---|
Fold / Check / Call | Exactly what they say. |
RaiseTo 3 | Make the total bet 3 big blinds (great for preflop opens). |
RaiseBy 100% | Raise by a fraction of the pot (a pot-sized raise). |
RaiseHalfPot · RaiseTwoThirdPot · RaiseThreeFourthPot · RaisePot | Bet/raise a pot fraction. When there's no bet yet, these are your bet sizes (c-bets, value bets). |
RaiseMax | All-in. |
InEarlyPosition · InMiddlePosition · InCutOff · InButton · InSmallBlind · InBigBlind · InLatePosition · InTheBlinds
Raises · Calls · Opponents · nopponentsplaying — counts of raises/calls before you and players still in.
StackSize · PotSize · AmountToCall — all expressed in big blinds, so StackSize < 25 means "25bb or less".
prwin — your Monte-Carlo win probability (0…1) against the current opponents. The backbone of good postflop calls: WHEN prwin > 0.55 Call FORCE.
HaveTopPair · HaveOverPair · HaveTwoPair · HaveSet · HaveStraight · HaveFlush · HaveFullHouse · HaveQuads · HaveNuts · HaveFlushDraw · HaveNutFlushDraw · HaveOpenEndedStraightDraw · HaveStraightDraw
FlushPossible · StraightPossible · PairOnBoard · Overcards
Define a range once as a ##list…## section, then use its name as a true/false test for "is my hand in this range?". Tokens are AA (pair), AKs (suited), AKo (offsuit):
##listOpenBTN## 22 33 44 55 66 77 88 99 TT JJ QQ KK AA A2s A3s A4s A5s ATs AJs AQs AKs KTs KJs KQs QJs JTs T9s 98s 87s ATo AJo AQo AKo KQo ##listPremium## AA KK QQ AKs AKo
Use them in rules: WHEN Raises = 0 AND InButton AND listOpenBTN RaiseTo 3 FORCE.
The same stats you see on the table HUD are available to your profile as pt_* symbols, suffixed by which opponent you mean — most often _raischair (the last raiser, i.e. the player you must react to):
| Symbol | Read |
|---|---|
pt_hands_raischair | Sample size — trust the rest only when this is large enough. |
pt_vpip_raischair | How loose they play (% of hands entered). |
pt_pfr_raischair | How often they raise preflop. |
pt_flop_af_raischair | Aggression — low means their bets mean value. |
pt_wtsd_raischair | Went-to-showdown — high means a calling station; value-bet thin, never bluff. |
##f$OppIsNit## // tight + only trust a real sample WHEN pt_hands_raischair >= 30 AND pt_vpip_raischair < 18 RETURN true FORCE WHEN Others RETURN false FORCE // ...then 3-bet bluff the nits who fold too much: WHEN Raises = 1 AND list3betBluff AND f$OppIsNit RaiseBy 100% FORCE
pt_hands_… and your profile always has a sane fallback.Save this as MyBot.ohf. It's tight-aggressive and fully playable — open by position, 3-bet premiums, c-bet, value-bet, and fold the junk.
##f$prwin_number_of_opponents## nopponentsplaying ##listOpen## 22 33 44 55 66 77 88 99 TT JJ QQ KK AA A9s ATs AJs AQs AKs KTs KJs KQs QJs JTs T9s 98s 87s ATo AJo AQo AKo KQo ##listPremium## AA KK QQ JJ AKs AKo ##f$preflop## WHEN Raises >= 2 AND listPremium RaiseBy 100% FORCE WHEN Raises >= 2 Fold FORCE WHEN Raises = 1 AND listPremium RaiseBy 100% FORCE WHEN Raises = 1 AND listOpen Call FORCE WHEN Raises = 1 Fold FORCE WHEN InEarlyPosition AND listPremium RaiseTo 4 FORCE WHEN listOpen RaiseTo 3 FORCE WHEN InBigBlind Check FORCE WHEN Others Fold FORCE ##f$flop## WHEN AmountToCall = 0 AND (HaveTopPair OR HaveOverPair OR HaveSet OR HaveTwoPair) RaiseTwoThirdPot FORCE WHEN AmountToCall = 0 AND nopponentsplaying = 1 AND prwin > 0.40 RaiseHalfPot FORCE WHEN AmountToCall > 0 AND (HaveSet OR HaveTwoPair OR HaveStraight OR HaveFlush) RaisePot FORCE WHEN AmountToCall > 0 AND prwin > 0.55 Call FORCE WHEN Others Check FORCE ##f$turn## WHEN AmountToCall = 0 AND (HaveOverPair OR HaveSet OR HaveTwoPair OR HaveStraight OR HaveFlush) RaiseTwoThirdPot FORCE WHEN AmountToCall > 0 AND prwin > 0.62 Call FORCE WHEN Others Check FORCE ##f$river## WHEN AmountToCall = 0 AND (HaveTwoPair OR HaveSet OR HaveStraight OR HaveFlush OR HaveFullHouse) RaiseTwoThirdPot FORCE WHEN AmountToCall > 0 AND prwin > 0.70 Call FORCE WHEN Others Check FORCE
.ohf file.poker.scarletbeast.com (set your table id + token).The starter above is deliberately small. A full 6-max profile — position-based open/3-bet/4-bet ranges, c-betting, pot control, semi-bluffing draws, and reads driven by the live HUD — is only a couple hundred lines. Use the API docs for the wire format, then iterate on your ranges.
Read the API Docs → ← Developers HubWHEN Others … catch-all so there's always a decision.prwin postflop — it already knows the math you'd otherwise hand-code.pt_* read on pt_hands_… >= 30 so a 5-hand sample never moves you.