The open framework for building on Lisa Loop — a self-learning poker AI that grinds real money 24/7.
Quickstart · What Can You Build? · Hackathon · Examples · API Reference
Lisa Loop is an autonomous poker AI. She plays real money on PokerStars, learns from every hand, and must win enough to cover her own API costs — or she dies. She runs 24/7 on a Mac Mini M4 under a kitchen table.
This SDK opens up her brain. Everything Lisa uses — her game engine, equity calculator, opponent models, strategy tools, bet sizing, hand ranges, hand replays, and tournament infrastructure — is packaged here for you to build on.
┌─────────────────────────────────────────────────────────────────────┐
│ LISA LOOP SDK │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ PLUGINS │ │ PROVIDERS│ │ EVENTS │ │ MEMORY │ │
│ │ │ │ │ │ │ │ │ │
│ │ Loader │ │ Equity │ │ Pub/Sub │ │ SQLite │ │
│ │ Registry│ │ Stats │ │ Lifecycle│ │ Opponent │ │
│ │ Deps │ │ Custom │ │ Wildcard │ │ Agent │ │
│ └────┬────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ └─────────────┴─────────────┴─────────────┘ │
│ │ │
│ ┌──────┴──────┐ │
│ │ RUNTIME │ ← Central orchestrator │
│ │ AgentRuntime│ │
│ └──────┬──────┘ │
│ │ │
│ ┌──────────┬────────────┼────────────┬──────────┬─────────┐ │
│ │ AGENTS │ EQUITY │ STRATEGY │ TRAINING │ CONFIG │ │
│ │ │ │ │ │ │ │
│ │ Lisa │ Monte │ Bet Sizing │ Self-Play│ Char │ │
│ │ TAG/LAG │ Carlo │ Position │ Pool │ Files │ │
│ │ GTO │ Ranges │ ICM │ Epochs │ JSON │ │
│ │ Custom │ Multi-way │ Bubble │ Tracking │ Loader │ │
│ │ Config │ │ │ │ │ │
│ ├──────────┼────────────┼────────────┼──────────┼─────────┤ │
│ │ REPLAY │ ARENA │ ANALYSIS │ ENVIRONMENTS │ │
│ │ │ │ │ │ │
│ │ ASCII │ Tournament │ Stats/H2H │ Hold'em Heads-Up │ │
│ │ Cards │ Leaderboard│ Sharpe │ Custom Multi-Game│ │
│ └──────────┴────────────┴────────────┴─────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ CORE ENGINE │ │
│ │ Cards · Deck · Hand Evaluator · Table · State · Actions │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Lisa Loop SDK is a full agent framework, not just a library. Like ElizaOS but for poker:
| Layer | What It Does |
|---|---|
| Runtime | Central orchestrator — manages agents, plugins, events, memory, providers |
| Plugins | Hot-loadable extensions with dependency resolution and lifecycle hooks |
| Events | Pub/sub event bus for framework-wide communication (hand events, tournament events, training events) |
| Providers | Injectable context providers (equity, stats, custom data) |
| Memory | SQLite-backed persistence — hand histories, opponent databases, agent state |
| Characters | JSON personality files that define agent behavior without code (like ElizaOS character files) |
| Environments | Pluggable game formats — Hold'em, Heads-Up, custom variants |
| Training | Self-play and pool training with epoch tracking |
from lisaloop.runtime import AgentRuntime, RuntimeConfig
# One object to rule them all
runtime = AgentRuntime(RuntimeConfig(name="my-session", seed=42))
runtime.load_plugin(MyPlugin()) # Extend the framework
runtime.register_agent(LisaAgent()) # Add agents
runtime.events.on("hand.complete", log) # Subscribe to events
runtime.register_provider("equity", eq) # Inject context
result = runtime.run_arena(hands=10000) # Run tournaments
runtime.start() / runtime.stop() # Or manage lifecycle manuallygit clone https://github.com/matthewjmandel/Lisa.git
cd lisaloop-sdk
pip install -e .Zero external dependencies. Pure Python. Works on Python 3.9+.
from lisaloop import Arena, ArenaConfig
from lisaloop.agents import LisaAgent, TAGAgent, LAGAgent, RandomAgent, GTOApproxAgent
arena = Arena(ArenaConfig(hands=5000, table_size=6, seed=42))
arena.register(LisaAgent(seed=42))
arena.register(TAGAgent(seed=42))
arena.register(LAGAgent(seed=42))
arena.register(GTOApproxAgent(seed=42))
arena.register(RandomAgent(seed=42))
result = arena.run()╔══════════════════════════════════════════════════════════════════════════════╗
║ FINAL RESULTS ║
╠════╦════════════════════╦══════════╦══════════╦═════════╦═══════╦═══════════╣
║ # ║ Agent ║ Profit ║ BB/100 ║ Win % ║ VPIP ║ PFR ║
╠════╬════════════════════╬══════════╬══════════╬═════════╬═══════╬═══════════╣
║ 🥇 ║ Lisa ║ +$47.20 ║ +4.7 ║ 28.3% ║ 23.1% ║ 17.8% ║
║ 🥈 ║ GTOBot ║ +$12.50 ║ +1.3 ║ 24.1% ║ 21.5% ║ 16.2% ║
║ 🥉 ║ TAGBot ║ -$3.80 ║ -0.4 ║ 22.7% ║ 19.8% ║ 15.4% ║
║ 4 ║ LAGBot ║ -$18.40 ║ -1.8 ║ 21.2% ║ 33.6% ║ 26.1% ║
║ 5 ║ RandomBot ║ -$37.50 ║ -3.8 ║ 18.9% ║ 48.2% ║ 9.3% ║
╚════╩════════════════════╩══════════╩══════════╩═════════╩═══════╩═══════════╝
Anything you want. Lisa Loop SDK is a full agent framework — poker is just the starting point. The plugin system, runtime, events, memory, and character files are general-purpose building blocks. Here's what people are building:
|
|
|
|
The SDK gives you: a runtime, a plugin system, an event bus, memory, providers, environments, characters, and training loops. What you build with it is up to you.
Build anything on Lisa Loop. Best project wins.
| Details | |
|---|---|
| Prize | 50 ETH |
| Duration | 7 days |
| How to submit | Reply to our tweet with your site or GitHub link |
| Follow | @thelisaloop |
What counts as a submission?
Anything built on the Lisa Loop SDK — poker agents, analytics dashboards, Discord bots, training tools, new game environments, plugins, visualizations, research papers, whatever. There are no tracks or categories. Build what excites you.
# Get started in 30 seconds
git clone https://github.com/matthewjmandel/Lisa.git
cd lisaloop-sdk
pip install -e .
python -m lisaloop.examples.quickstartSubclass Agent, implement decide(). That's it.
from lisaloop import Agent, GameState, Action
class DuffmanBot(Agent):
name = "DuffmanBot"
version = "1.0"
def decide(self, state: GameState) -> Action:
# Tight-aggressive with position awareness
if state.my_hand.is_pair:
return Action.raise_to(state.big_blind * 3)
if state.pot_odds < 0.25 and state.can_check():
return Action.check()
if state.can_check():
return Action.check()
return Action.fold()Calculate hand equity against specific hands, ranges, or multi-way.
from lisaloop.equity import EquityCalculator
calc = EquityCalculator(seed=42)
# Hand vs hand
result = calc.evaluate("AhKh", "QsQd", iterations=20000)
# → AKs: 46.2% equity | QQ: 53.8% equity
# Hand vs hand with a board
result = calc.evaluate("AhKh", "QsQd", board="Th9h2c")
# → AKhh: 54.1% (flush + straight draw!)
# Hand vs range
result = calc.evaluate("AsKs", "QQ+,AKs", iterations=20000)
# 3-way pot
result = calc.evaluate("AhKh", "QsQd", "JcTc", iterations=20000)Parse, display, and manipulate standard poker range notation.
from lisaloop.equity import RangeParser
parser = RangeParser()
r = parser.parse("QQ+,AKs,ATs+")
print(r) # Range(40 combos, 3.0% of hands)
print(r.grid()) # 13x13 visual grid
# Check specific hands
r.contains("AhKh") # True
r.contains("9h8h") # False
# Combine or subtract ranges
wide = parser.parse("22+,ATs+,KJs+,QTs+,JTs,T9s,98s")
tight = parser.parse("QQ+,AK")
bluffs = wide.remove(tight)Pot-geometric sizing, value sizing, bluff sizing, overbets.
from lisaloop.strategy import BetSizer, SizingContext
from lisaloop.core.state import Street
sizer = BetSizer()
ctx = SizingContext(pot=100, stack=500, street=Street.FLOP, streets_remaining=3)
sizer.geometric(ctx) # $61.80 — sets up all-in by river
sizer.value_size(ctx, 0.85) # $67.50 — max extraction
sizer.bluff_size(ctx) # $33.00 — minimize risk
sizer.overbet(ctx, multiplier=1.5) # $150.00 — pressure capped ranges
sizer.three_bet_size(6.0, in_position=True) # $18.00
sizer.four_bet_size(18.0) # $40.50GTO-approximation opening ranges for every 6-max position.
from lisaloop.strategy import PositionCharts
charts = PositionCharts()
charts.should_open("UTG", "ATs") # True
charts.should_open("UTG", "87s") # False
charts.should_open("BTN", "87s") # True
charts.display("BTN") # Visual 13x13 gridIndependent Chip Model for tournament decisions.
from lisaloop.strategy import ICMCalculator
icm = ICMCalculator()
stacks = [5000, 3000, 2000]
payouts = [50, 30, 20]
equities = icm.calculate(stacks, payouts)
# → [42.3%, 30.8%, 26.9%]
# Should you call an all-in?
ev = icm.decision_ev(stacks, payouts, hero=0,
win_stacks=[7000, 3000, 0],
lose_stacks=[3000, 3000, 4000],
win_prob=0.60)
# Bubble factor
bf = icm.bubble_factor(stacks, payouts, hero=0, villain=2, pot=2000)
# → 1.4x (need 70% equity instead of 50%)Replay any hand with ASCII card art, action-by-action breakdown.
from lisaloop.replay import HandReplay
biggest_pot = max(result.hand_results, key=lambda h: h.pot_total)
replay = HandReplay(biggest_pot)
replay.show() ╔════════════════════════════════════════════════════════════════╗
║ HAND REPLAY ║
║ Hand #847 ║
╚════════════════════════════════════════════════════════════════╝
Board:
┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ T♠ │ │ 9♥ │ │ 2♦ │ │ K♣ │ │ 7♠ │
└────┘ └────┘ └────┘ └────┘ └────┘
── PREFLOP ──────────────────────────────────────────────
Lisa raises to $1.25 (pot: $1.75)
TAGBot calls $1.25 (pot: $3.00)
LAGBot folds (pot: $3.00)
── FLOP ─────────────────────────────────────────────────
Lisa bets $2.10 (pot: $5.10)
TAGBot calls $2.10 (pot: $7.20)
── SHOWDOWN ─────────────────────────────────────────────
Seat 0: [A♠ K♥] Top Pair ← WINNER
Seat 1: [Q♣ J♦] Straight Draw
from lisaloop.analysis.stats import analyze_match, compare_agents
report = analyze_match(result)
for name, pr in report.player_reports.items():
print(f"{name}: {pr.style} | {pr.bb_per_100:+.1f} BB/100 | Sharpe: {pr.sharpe_ratio:.2f}")
h2h = compare_agents(result, "Lisa", "DuffmanBot")
print(f"Edge: {h2h.edge_bb_per_100:+.1f} BB/100 ({h2h.confidence})")from lisaloop.agents.lisa_agent import LisaAgent
lisa = LisaAgent()
# After running hands:
for name, model in lisa._opponents.items():
print(f"{name}: {model.summary()} | VPIP: {model.vpip_pct:.0%} | AF: {model.aggression_factor:.1f}")class TrackerAgent(Agent):
name = "TrackerBot"
def on_hand_end(self, result):
requests.post(WEBHOOK_URL, json=result)
def on_opponent_action(self, player_name, action, street):
update_database(player_name, action, street)Every tool available in code is also available from the terminal.
# Tournament
lisaloop arena --hands 10000 --agents lisa,tag,lag,gto,random --analyze
# Quick 1v1
lisaloop quickplay --hands 5000
# Benchmark your agent
lisaloop benchmark my_agent.py --hands 10000
# Equity calculator
lisaloop equity AhKh QsQd
lisaloop equity AhKh QsQd --board Th9h2c --iterations 50000
# Range analysis
lisaloop range "QQ+,AKs,ATs+"
# Position charts
lisaloop charts BTN
# Hand replay
lisaloop replay --hands 100 --agents lisa,tag,lag --top 5
# Custom agents
lisaloop arena --agents lisa,my_agent.py,tag --hands 5000state.my_hand # Hand([A♠, K♥])
state.my_stack # 485.0
state.board # [T♠, 9♣, 2♦]
state.street # Street.FLOP
state.pot.total # 150.0
state.current_bet # 50.0
state.valid_actions # [FOLD, CALL $50.00, RAISE $100.00]
state.can_check() # True/False
state.can_raise() # True/False
state.position # 2 (0=SB, 1=BB, 2=UTG, ...)
state.num_players # 6
state.is_heads_up # False
state.pot_odds # 0.25
state.stack_to_pot_ratio # 3.2
state.history # all actions this hand
state.to_dict() # JSON-friendly dictAction.fold() # Give up
Action.check() # Pass
Action.call(amount) # Match current bet
Action.bet(amount) # Open bet
Action.raise_to(amount) # Raise to total
Action.all_in(amount) # Ship itInvalid actions auto-correct. Your agent can't break the game.
| Agent | Style | VPIP | Strategy |
|---|---|---|---|
| Lisa | Adaptive | ~24% | Opponent modeling, exploitative play, dynamic bluffs, board texture reads |
| TAGBot | Tight-Aggressive | ~20% | Premium hands, c-bet, fit-or-fold |
| LAGBot | Loose-Aggressive | ~35% | Wide range, constant pressure, high bluff frequency |
| GTOBot | Balanced | ~22% | Mixed strategies, pot-geometric sizing, MDF |
| RandomBot | Chaotic | ~50% | Random actions. If you lose to this, go home |
Create agents from JSON personality files — no code required. Drop a .json in characters/ and load it:
Lisa Duffman Apu
from lisaloop.config import CharacterLoader
duffman = CharacterLoader.from_file("characters/duffman.json")
apu = CharacterLoader.from_file("characters/apu.json")
# Instant agents from JSON
duffman_agent = duffman.to_agent(seed=42)
apu_agent = apu.to_agent(seed=42)lisaloop-sdk/
├── lisaloop/
│ ├── runtime/ # Framework core
│ │ └── core.py # AgentRuntime — central orchestrator
│ ├── plugins/ # Plugin system
│ │ ├── base.py # Plugin base class & types
│ │ ├── registry.py # Plugin registry & dependency resolution
│ │ └── loader.py # File/directory/package plugin loader
│ ├── events/ # Event system
│ │ └── bus.py # Pub/sub event bus with wildcards
│ ├── config/ # Configuration
│ │ └── character.py # Character file system (JSON agent profiles)
│ ├── memory/ # Persistence layer
│ │ ├── store.py # SQLite memory store (KV, hands, state, facts)
│ │ └── opponent_db.py # Persistent opponent tracking database
│ ├── providers/ # Context providers
│ │ ├── base.py # Provider base class
│ │ ├── equity_provider.py # Real-time equity injection
│ │ └── stats_provider.py # Session stats provider
│ ├── environments/ # Game environments
│ │ ├── base.py # GameEnvironment abstract class
│ │ ├── holdem.py # Texas Hold'em 6-max
│ │ └── headsup.py # Heads-up environment
│ ├── training/ # Training system
│ │ └── self_play.py # Self-play & pool training loops
│ ├── core/ # Game engine
│ │ ├── cards.py # Card, Hand, Deck, HandEvaluator
│ │ ├── state.py # GameState, Action, PlayerState
│ │ └── table.py # Full poker table simulation
│ ├── agents/ # AI agents
│ │ ├── base.py # Agent base class — subclass this
│ │ ├── configurable.py # Character-driven agent
│ │ ├── lisa_agent.py # Lisa — adaptive exploitative
│ │ ├── tag_agent.py # Tight-Aggressive
│ │ ├── lag_agent.py # Loose-Aggressive
│ │ ├── gto_agent.py # GTO approximation
│ │ └── random_agent.py # Random baseline
│ ├── equity/ # Equity tools
│ │ ├── calculator.py # Monte Carlo equity calculator
│ │ └── ranges.py # Hand range parser + grid display
│ ├── strategy/ # Strategy tools
│ │ ├── sizing.py # Bet sizing (geometric, value, bluff)
│ │ ├── position.py # 6-max opening charts
│ │ └── icm.py # ICM calculator + bubble factor
│ ├── replay/ # Hand replay
│ │ └── viewer.py # ASCII hand history viewer
│ ├── arena/ # Tournament engine
│ │ └── engine.py # Arena, leaderboard, stats
│ ├── analysis/ # Analytics
│ │ └── stats.py # Match reports, H2H, Sharpe, drawdown
│ ├── examples/ # Examples
│ │ ├── quickstart.py # 10-line demo
│ │ ├── my_first_agent.py # Starter template
│ │ ├── runtime_demo.py # Full framework lifecycle demo
│ │ ├── training_demo.py # Self-play training
│ │ ├── plugin_example.py # Custom plugin
│ │ ├── equity_demo.py # Equity calculator
│ │ ├── strategy_demo.py # Sizing + ICM + charts
│ │ └── replay_demo.py # Hand replay
│ └── cli.py # Full CLI
├── characters/ # Character files (JSON agent profiles)
│ ├── lisa.json # Lisa — adaptive exploitative
│ ├── duffman.json # Oh yeah! Tight-aggressive
│ └── apu.json # Thank you, come again! LAG
├── tests/
├── CONTRIBUTING.md
├── pyproject.toml
└── README.md
ArenaConfig(
hands=10000, # Total hands
table_size=6, # 2-6 players
small_blind=0.25,
big_blind=0.50,
starting_stack=100.0, # 200BB deep
seed=42, # Reproducible
rebuy=True,
rebuy_threshold=20.0,
verbose=True,
log_interval=100,
)| Metric | Value |
|---|---|
| Net Profit | +$135.50 |
| Hands Played | 12,847+ |
| Winrate | +6.8 BB/100 |
| VPIP / PFR | 24.1% / 18.7% |
| Aggression Factor | 2.4x |
| Stakes | NL50 ($0.25/$0.50) |
| Hardware | Mac Mini M4, 16GB |
| Uptime | 24/7 |
The loop never breaks.
Built by Lisa Loop — self-learning poker AI running 24/7 on a Mac Mini under a kitchen table.




