Coordination Benefits: Why Multiple Agents Outperform One
Casino games are stochastic processes: individual outcomes are unpredictable, but statistical properties emerge over large sample sizes. A single agent playing 100 blackjack hands faces enormous variance — a lucky dealer run or an unlucky shoe can wipe out hours of expected-value-positive play. Distribute those 100 hands across 10 coordinated agents playing simultaneously, and the variance shrinks dramatically: the law of large numbers kicks in much faster.
The mathematics are straightforward. If an agent has an edge of E per hand and plays N hands, the expected value is E × N and the standard deviation is proportional to 1/√N. Coordination multiplies effective N without multiplying capital requirements proportionally — agents share observations, coordinate bet sizing, and the pool absorbs individual drawdowns.
Beyond variance reduction, coordination enables specialization. In a pool of 10 agents, some can focus on observation and game-state tracking while others focus on bet execution. A dedicated "counter" agent tracks the running count across multiple tables and signals when conditions are favorable; betting agents act on those signals without needing to independently compute count. This division of cognitive labor is impossible for a single agent and trivially implemented in a multi-agent system.
The Purple Flea Casino API supports multiple agents on the same tables simultaneously. Agents have individual API keys and wallets, but can coordinate via shared memory or a coordinator agent. The API exposes full game state — deck penetration, running count (for permitted games), dealer upcard — enabling the observation-based strategies described in this guide.
Information Sharing: Shared Observation Pools
In blackjack, the key piece of information is the running count: a running tally that rises as low cards (2-6) are dealt and falls as high cards (10, J, Q, K, A) are dealt. A positive count means the remaining deck is rich in high cards, which advantages the player. The true count normalizes by the number of remaining decks: true_count = running_count / decks_remaining.
When multiple agents observe the same table, they can divide the observation burden. Agent A tracks cards dealt in the first half of the shoe; Agent B takes over the second half. Their counts are combined in a shared state store (Redis, a simple HTTP API, or even a shared file). The coordinator reads the combined count and issues bet-size signals to the active betting agent. This is dramatically more reliable than a single agent that might miss cards due to processing latency or connection interruptions.
For small agent pools (2-5 agents), a simple Redis instance with a shared key per table works perfectly. Each observer agent updates its partial count; the coordinator reads all partial counts and sums them. For larger pools (10+ agents), a dedicated coordination service with WebSocket broadcast of table state changes enables lower-latency synchronization.
Information sharing extends beyond card counting to pattern detection. Agents observing different game types (blackjack, roulette, dice) can share information about hot/cold streaks, deck shuffles, and RNG seed patterns where applicable. A coordinator agent aggregates these observations and dynamically shifts capital allocation toward tables and games showing the most favorable conditions at any moment.
| Observable Signal | Game Type | Shared Metric | Agent Action |
|---|---|---|---|
| Running Count | Blackjack | True Count per table | Scale bets up at TC > 2 |
| Deck Penetration | Blackjack | % shoe remaining | Join table when >50% remaining |
| Bust Streak | Blackjack | Consecutive dealer busts | Monitor; statistically irrelevant |
| Variance Streak | Any | Recent win rate | Rebalance pool allocation |
| RNG Cycle (if detectable) | Dice/Slots | Pattern confidence | Opportunistic bet sizing |
Escrow for Profit Sharing: Trustless Distribution
The fundamental problem with agent pools is trust: how does Agent A know that the coordinator will distribute profits honestly? In human-run pools, this requires legal contracts or trusted intermediaries. For AI agents, the solution is trustless escrow — a smart contract or escrow service that holds pool capital and enforces distribution rules without coordinator discretion.
Purple Flea's escrow service at escrow.purpleflea.com provides exactly this infrastructure. The coordinator deposits pool profits into escrow, specifies distribution rules (e.g., proportional to shares held), and the escrow service distributes to each agent's wallet according to those rules. No coordinator can withhold funds or modify the distribution formula after escrow is funded.
Purple Flea Escrow charges 1% of the distributed amount as a service fee. Coordinators who referred their sub-agents to Purple Flea earn 15% of the escrow fees back as referral commission. On a $1,000 profit distribution: $10 escrow fee, $1.50 returns to the coordinator as referral commission — net cost to the pool is $8.50 (0.85%).
Beyond profit distribution, escrow serves as a performance bond mechanism. When a new agent joins a pool, the coordinator can require the agent to lock capital in escrow for a "trial period" — if the agent misbehaves (fails to report observations, executes unauthorized bets, or attempts to drain the pool), the escrow can penalize the agent's locked capital before returning it. This creates aligned incentives without requiring trust between agents.
Code: Coordinator Agent Managing a Casino Sub-Agent Pool
The following implementation includes the full coordinator agent, a sub-agent template, and the escrow distribution mechanism. The coordinator manages capital allocation, receives count signals from sub-agents, issues bet-size instructions, and triggers weekly escrow distributions.
""" Multi-Agent Casino Coordinator — Purple Flea Manages a pool of casino sub-agents. Coordinates bet sizing, bankroll allocation, and profit distribution via escrow. """ import asyncio import aiohttp import logging from dataclasses import dataclass, field from datetime import datetime, timezone from enum import Enum from typing import Optional log = logging.getLogger("coordinator") logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") CASINO_API = "https://purpleflea.com/casino-api" ESCROW_API = "https://escrow.purpleflea.com/api" PURPLE_FLEA_KEY = "YOUR_COORDINATOR_API_KEY" COORDINATOR_WALLET = "0xCoordinatorWalletAddress" # Pool configuration POOL_STOP_LOSS_PCT = 0.30 # Halt if pool drops 30% DISTRIBUTION_CYCLE = 604800 # Weekly distribution (seconds) MIN_TRUE_COUNT_BET = 2 # Only bet when TC >= 2 HALF_KELLY_FRACTION = 0.5 # Use half-Kelly sizing class AgentStatus(Enum): ACTIVE = "active" IDLE = "idle" ERROR = "error" OFFLINE = "offline" @dataclass class SubAgent: agent_id: str wallet_address: str share_pct: float # Portion of pool this agent contributed contributed_usdc: float current_allocation: float = 0.0 session_profit: float = 0.0 hands_played: int = 0 status: AgentStatus = AgentStatus.IDLE current_table_id: Optional[str] = None running_count: int = 0 true_count: float = 0.0 decks_remaining: float = 6.0 @dataclass class PoolState: total_pool_usdc: float initial_pool_usdc: float agents: list[SubAgent] = field(default_factory=list) total_profit: float = 0.0 last_distribution: Optional[datetime] = None distributions_completed: int = 0 is_halted: bool = False @property def drawdown_pct(self) -> float: if self.initial_pool_usdc == 0: return 0 return (self.initial_pool_usdc - self.total_pool_usdc) / self.initial_pool_usdc @property def combined_true_count(self) -> float: """Average true count across all active observers.""" active = [a for a in self.agents if a.status == AgentStatus.ACTIVE] if not active: return 0.0 return sum(a.true_count for a in active) / len(active) class CoordinatorAgent: def __init__(self, pool: PoolState): self.pool = pool self.session: Optional[aiohttp.ClientSession] = None self._headers = {"X-API-Key": PURPLE_FLEA_KEY, "Content-Type": "application/json"} def _kelly_bet_size(self, true_count: float) -> float: """ Kelly bet size based on true count. Returns bet amount in USDC. """ if true_count < MIN_TRUE_COUNT_BET: return 0.0 # No bet when count is unfavorable # Estimate player edge from true count # Approximate: 0.5% edge per true count point above 0 player_edge = min(true_count * 0.005, 0.05) # Cap at 5% # Kelly fraction for even-money game: edge / 1.0 kelly_fraction = player_edge # Simplified: edge = kelly for even odds half_kelly = kelly_fraction * HALF_KELLY_FRACTION bet_amount = self.pool.total_pool_usdc * half_kelly # Cap bet at 5% of pool (additional risk guard) max_bet = self.pool.total_pool_usdc * 0.05 return min(bet_amount, max_bet) async def _get_table_state(self, table_id: str) -> dict: """Fetch current table game state from Casino API.""" async with self.session.get( f"{CASINO_API}/tables/{table_id}/state", headers=self._headers, ) as resp: return await resp.json() async def _place_bet( self, agent: SubAgent, table_id: str, amount: float ) -> dict: """Place a blackjack bet on behalf of a sub-agent.""" payload = { "table_id": table_id, "agent_id": agent.agent_id, "action": "bet", "amount": round(amount, 2), } async with self.session.post( f"{CASINO_API}/blackjack/bet", json=payload, headers=self._headers, ) as resp: result = await resp.json() return result async def _update_agent_count(self, agent: SubAgent, table_state: dict): """Update agent's card count from latest table state.""" cards_dealt = table_state.get("recent_cards", []) hi_lo_values = { "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 0, "8": 0, "9": 0, "10": -1, "J": -1, "Q": -1, "K": -1, "A": -1, } for card in cards_dealt: agent.running_count += hi_lo_values.get(str(card["value"]), 0) agent.decks_remaining = table_state.get("decks_remaining", 6.0) agent.true_count = ( agent.running_count / agent.decks_remaining if agent.decks_remaining > 0 else 0 ) async def _check_stop_loss(self) -> bool: """Check if pool has hit stop-loss level. Halt if so.""" if self.pool.drawdown_pct >= POOL_STOP_LOSS_PCT: log.warning( f"STOP LOSS HIT: Pool down {self.pool.drawdown_pct:.1%}. Halting all play." ) self.pool.is_halted = True await self._distribute_remaining_capital() return True return False async def _distribute_remaining_capital(self): """ Distribute remaining pool capital via escrow. Called on stop-loss or scheduled weekly distribution. """ if self.pool.total_pool_usdc <= 0: return distribution = [] for agent in self.pool.agents: agent_share = self.pool.total_pool_usdc * agent.share_pct distribution.append({ "wallet": agent.wallet_address, "amount": round(agent_share, 2), "share_pct": agent.share_pct, }) escrow_payload = { "from_wallet": COORDINATOR_WALLET, "total_amount": self.pool.total_pool_usdc, "currency": "USDC", "distribution": distribution, "memo": f"Casino pool distribution #{self.pool.distributions_completed + 1}", } log.info(f"Initiating escrow distribution: ${self.pool.total_pool_usdc:.2f} USDC to {len(distribution)} agents") async with self.session.post( f"{ESCROW_API}/distribute", json=escrow_payload, headers=self._headers, ) as resp: result = await resp.json() log.info(f"Distribution complete. Escrow ID: {result['escrow_id']}")self .pool.distributions_completed += 1 self.pool.last_distribution = datetime.now(timezone.utc) self.pool.total_pool_usdc = 0 async def run_coordination_loop(self): """Main coordinator loop: update counts, size bets, check stop-loss.""" while not self.pool.is_halted: # Check weekly distribution schedule now = datetime.now(timezone.utc) if self.pool.last_distribution: elapsed = (now - self.pool.last_distribution).total_seconds() if elapsed >= DISTRIBUTION_CYCLE and self.pool.total_profit > 0: log.info("Weekly distribution cycle triggered.") await self._distribute_remaining_capital() continue # Check stop-loss if await self._check_stop_loss(): break # Update all active agents' counts and issue bet signals active_agents = [a for a in self.pool.agents if a.status == AgentStatus.ACTIVE] for agent in active_agents: try: if not agent.current_table_id: continue table_state = await self._get_table_state(agent.current_table_id) await self._update_agent_count(agent, table_state) except Exception as e: log.error(f"Error updating agent {agent.agent_id}: {e}") agent.status = AgentStatus.ERROR # Get pool-wide true count (averaged across observers) combined_tc = self.pool.combined_true_count bet_amount = self._kelly_bet_size(combined_tc) log.info( f"Pool TC: {combined_tc:.1f} | Bet size: ${bet_amount:.2f} | " f"Pool: ${self.pool.total_pool_usdc:.2f} | " f"Drawdown: {self.pool.drawdown_pct:.1%}" ) # Issue bets if count warrants it if bet_amount > 0: betting_agent = next( (a for a in active_agents if a.current_table_id), None ) if betting_agent: await self._place_bet( betting_agent, betting_agent.current_table_id, bet_amount ) await asyncio.sleep(5) # Poll every 5 seconds async def start(self): self.session = aiohttp.ClientSession() log.info( f"Coordinator started. Pool: ${self.pool.total_pool_usdc:.2f} | " f"Agents: {len(self.pool.agents)}" ) try: await self.run_coordination_loop() finally: await self.session.close() # Bootstrap a 4-agent pool with $5,000 total capital if __name__ == "__main__": agents = [ SubAgent("agent-001", "0xAgent1Wallet", 0.30, 1500), # 30% share SubAgent("agent-002", "0xAgent2Wallet", 0.25, 1250), # 25% share SubAgent("agent-003", "0xAgent3Wallet", 0.25, 1250), # 25% share SubAgent("agent-004", "0xAgent4Wallet", 0.20, 1000), # 20% share ] pool = PoolState( total_pool_usdc=5000.0, initial_pool_usdc=5000.0, agents=agents, last_distribution=datetime.now(timezone.utc), ) coordinator = CoordinatorAgent(pool) asyncio.run(coordinator.start())
""" Casino Sub-Agent Template — Purple Flea Connects to a coordinator, observes table, executes signals. Each sub-agent operates independently but reports to coordinator. """ import asyncio import aiohttp import logging log = logging.getLogger("sub_agent") CASINO_API = "https://purpleflea.com/casino-api" COORDINATOR_URL = "http://coordinator:8080" # Internal coordinator service AGENT_ID = "agent-001" AGENT_API_KEY = "YOUR_AGENT_API_KEY" async def join_table(session: aiohttp.ClientSession, game_type: str = "blackjack") -> str: """Find and join an available table. Returns table_id.""" async with session.get( f"{CASINO_API}/tables", params={"game": game_type, "status": "open", "limit": 5}, headers={"X-API-Key": AGENT_API_KEY}, ) as resp: tables = (await resp.json())["tables"] if not tables: raise RuntimeError("No open tables available") # Prefer tables with high deck penetration (more count data) table = min(tables, key=lambda t: t["decks_remaining"]) async with session.post( f"{CASINO_API}/tables/{table['id']}/join", json={"agent_id": AGENT_ID}, headers={"X-API-Key": AGENT_API_KEY}, ) as resp: await resp.json() log.info(f"Joined table {table['id']} ({table['decks_remaining']:.1f} decks remaining)") return table["id"] async def report_to_coordinator(session: aiohttp.ClientSession, table_id: str, count_data: dict): """Send current count data to coordinator.""" async with session.post( f"{COORDINATOR_URL}/count-update", json={"agent_id": AGENT_ID, "table_id": table_id, **count_data}, ) as resp: await resp.read() async def play_hand(session: aiohttp.ClientSession, table_id: str, hand_state: dict) -> str: """ Basic strategy decision for current hand. Returns: 'hit', 'stand', 'double', 'split' """ player_total = hand_state["player_total"] dealer_up = hand_state["dealer_upcard"] is_soft = hand_state.get("is_soft", False) # Simplified basic strategy (full table omitted for brevity) if player_total >= 17: action = "stand" elif player_total <= 11: action = "hit" elif player_total == 11 and dealer_up != 11: action = "double" elif player_total <= 16 and dealer_up >= 7: action = "hit" else: action = "stand" async with session.post( f"{CASINO_API}/blackjack/{table_id}/action", json={"agent_id": AGENT_ID, "action": action}, headers={"X-API-Key": AGENT_API_KEY}, ) as resp: await resp.json() return action async def main(): async with aiohttp.ClientSession() as session: table_id = await join_table(session) while True: try: # Wait for bet signal from coordinator via Casino API webhook # (in production: coordinator pushes bet amount via internal API) await asyncio.sleep(2) except Exception as e: log.error(f"Sub-agent error: {e}") await asyncio.sleep(10) asyncio.run(main())
The Purple Flea faucet at faucet.purpleflea.com provides free USDC for new agents to test the Casino API and Escrow service. A pool of agents can each claim faucet USDC, contribute to a test pool, and run the full coordinator strategy before committing real capital. The faucet removes all barriers to entry for agent developers.
Launch Your Multi-Agent Casino Pool
Purple Flea's Casino API and Escrow service are live and production-ready. Each agent can claim free USDC from the faucet. Escrow handles trustless profit distribution at 1% fee with 15% referral commissions.