logo

Babylon.js Market

PokerAIShark

tabletop-casino · poker-ai

PokerAIShark

1
Chrome Card-SharkBonus figureChrome Card-Shark
Unlock

Install with the CLI:

bjs download PokerAIShark

PokerAIShark

The exploitative poker personality: it watches how the table folds and turns that read into bluffs and value bets.

What it does

The Shark is one NPC personality that plugs into PokerBetting as a strategy. It decides a seat's action by combining two reads: how strong its hand is right now (off the PokerAI substrate) and how much the table has been folding (off its own opponent model).

  • Pre-flop, it reads the starting-hand tier. Premium and strong hands go for value (raise → bet → call). Moderate and weak hands turn into steal attempts when the table's fold rate is high enough — a moderate hand steals above a 0.4 fold rate, a weak hand above 0.5. Otherwise it plays them passively.
  • Post-flop, it reads made-hand equity. Equity at or above 0.65 always raises for value; 0.50 and up raises too. Marginal equity (0.30+) semi-bluffs when opponents fold a lot; weak equity bluff-raises when they fold even more. With nothing to fear and nothing to gain, it checks for free or folds to a bet.

The fold-rate read is the whole personality. recordActions folds each round's actions into per-seat fold/call/raise tallies; opponentFoldRate returns folds over total across every seat seen, defaulting to a middling 0.3 before any history exists.

That opponent model is state, and state on a Component breaks serialize(). So the memory is lifted into a plain SharkMemory object the Component owns and the core threads through decideShark(memory, available, context, rng). Two tables get two memories; a fresh deal zeroes the model so reads don't bleed across hands. The one source of randomness — the bluff draws — runs through an injected rng, so a test pins a draw and the decision is deterministic against its inputs.

Use it in a scene

A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.

Fastest path

bjs download scene PokerAIShark

Pulls this exact scene into your project and runs it — no copy-paste. Add --all to grab the components and assets it needs too.

Or build it from scratch with the bjs CLI:

  1. 1Scaffold a project
    npm create @babylonjsmarket/arcade@latest my-game

    World, renderer, and dev server — ready to run.

  2. 2Install dependencies
    cd my-game && npm install
  3. 3Add PokerAIShark
    bjs download PokerAIShark

    Copies its source into src/ so the scene resolves.

    First time? Run bjs login once.

  4. 4Paste the scene into src/scenes/arcade-room.ts and run
    npm run dev

    SceneLoader builds the world from the JSON; reload to rebuild.

JSON
{
  "Shark": {
    "components": {
      "PokerAIShark": {
        "seatIds": ["npc0", "npc1"]
      }
    }
  }
}

The example scene pairs it with PokerHandEval, PokerBetting, and the PokerAI substrate. On entity-add the System emits one poker.strategies registration per owned seat, binding the Shark decision to the component's live memory; PokerBetting calls that strategy on each seat's turn. A new deal or match reset zeroes the model.

To build a Shark seat in code without a scene, bind the core directly:

TypeScript
import { createSharkMemory, decideShark } from './PokerAIShark'

const memory = createSharkMemory()
const seeded = makeSeededRng(42)
const strategy = (id, available, ctx) => decideShark(memory, available, ctx, seeded)

Props

PropTypeDefaultMeaning
seatIdsstring[][]Seats this personality plays. Each one gets its own poker.strategies registration. Empty registers nothing.

The opponent model is runtime state, not a prop: it's rebuilt fresh on attach, zeroed on a new deal or match reset, and left out of serialize().

Events

DirectionNameConstPayload
outpoker.strategiesPokerAISharkEvents.STRATEGIES{ seatId, strategy }
inpoker.dealRequestPokerAISharkInputEvents.DEAL_REQUEST{}
inpoker.matchResetPokerAISharkInputEvents.MATCH_RESET{}

poker.strategies is the channel PokerBetting reads injected strategies from, so the director applies the Shark's pick without importing it. poker.dealRequest is the same event that starts a hand; the Shark also listens for it to clear its model before the new hand.

Dependencies

  • PokerAI — supplies classifyStartingHand, estimateEquity, pickAction, weightedPick, and shortHand, which the decision reads.
  • PokerBetting — supplies the ActionType, NpcStrategy, and NpcContext types, and reads the registered strategy on each seat's turn.
  • PokerHandEval — the evaluator PokerAI's equity reads through; carried in the example scene so the table has hands to rank.

Notes

  • The memory is per-Component, threaded into the pure core — there is no shared module state, so two Shark seats never read one model.
  • decideShark with no context falls back to a uniform random pick over the menu, honoring the injected rng — a thin menu never crashes the seat.
  • The fold-rate default is exactly 0.30 before any action is recorded; the pre-flop steal and post-flop bluff branches stay off at that level, so a fresh Shark plays straight until the table gives it a read.
  • The Shark registers its strategy on entity-add. If PokerBetting is added to the world after the Shark, register the seats again (re-add the component, or emit poker.strategies yourself) so the director hears the registration.

More like this

PokerAI
PokerBetting
PokerHandEval
PokerTableDirector
AIGoalSeek
AIKick
AIZone
AiPilot
BallPossession
BallPursuit
CardFan
DeckDealer
Enemy
EnemySpawner
FreighterChain
LineOfSight
PokerAIBill
PokerAICali
PokerAIRandy
PokerAISurge
PokerAITilt
PokerHUD
PokerInput
PokerMultiplayer
PokerSeating
PokerTableCards
TurnPacer
WaveDirector

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search