SLOPSHOPPER

roulette

Roulette band for Claude Code: a number per prompt, the wheel spins while Claude works, hit or miss when it lands.

newbandtoasttimer
v0.1.0GPL-3.0-onlyupdated 2026-10-22NedimKaravdic/claude-casino
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · roulette
› fix the failing auth test and add an audit log call ╭────────────────────────────╮ │ roulette │ ⏺ Read(src/auth.ts) │ 🎲 black hit, number │ ⎿ Read 6 lines │ missed │ ⏺ Update(src/auth.ts) ╰────────────────────────────╯ ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM 🎰 landed 29 black · COLOR HIT · your number 28 · 0J 1C 0M ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts

Draws

Band
🎰 landed 29 black · COLOR HIT · your number 28 · 0J 1C 0M
README

🎰 Roullete

Turn every Claude Code prompt into a spin of the wheel. 🎡

You get a number. Claude gets to work. The wheel spins above your prompt the whole time, and when Claude is done the ball drops. Did it land on your number? 🍀

🎰 [ 23 ]  · your number 17  · 0J 3C 5M            ← Claude is working, wheel spinning
🎰 landed 8 black · COLOR HIT  · your number 27  · 0J 4C 5M

🎲 How to win

ResultMeaningOdds
💎 JACKPOTThe ball landed on your number1 in 37
🔴⚫ COLOR HITSame color as your numberabout 1 in 2
💸 MISSThe house wins againthe rest

Zero is green 🟢, as on a real European wheel. Your score (J/C/M) is remembered between sessions, so the losing streak follows you around. 🃏

⚠️ Disclaimer: this is a toy. No money, no stakes, no real wagering. I do not support or encourage gambling, and neither should you. Gambling causes real harm; if it is affecting you or someone you know, please seek help, for example from your local gambling-support helpline. ❤️

🎟️ Install

git clone https://github.com/NedimKaravdic/claude-casino
claude --plugin-dir ./claude-casino

Or add the folder to CLAUDE_CODE_PLUGIN_DIRS. Requires Claude Code 2.1.287 or newer (function hooks, early access). 🚀

🛠️ Develop

claude plugin validate .   # 🔍 what the engine would refuse
claude plugin test .       # 🧪 spins the wheel on terminal and desktop

hooks/register.tsx is the whole casino 🏛️:

  • 🟢 turn.start deals your number and starts a 90 ms ticker
  • 🎡 ui.render on AbovePrompt draws the spinning band
  • 🛑 turn.complete stops the ticker, drops the ball and scores

💰 Payout

Unlike a real casino, this one costs nothing and pays nothing. The only thing you can lose here is your focus. 🎩

📜 License

GPL-3.0. Fork it, change it, share it, but keep it open. © 2026 Nedim Karavdic

Source 2 files
hooks/register.tsx 76 lines
1import { atom, read, update } from 'claude-code'
2import type { Register, Timer } from 'claude-code'
3
4import type { Game, Outcome } from '../types'
5
6const game = atom({ plugin: 'roulette', key: 'game' } as const, null)
7
8const RED = new Set([1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36])
9const spin = () => Math.floor(Math.random() * 37)
10const colorOf = (n: number) => (n === 0 ? 'green' : RED.has(n) ? 'red' : 'black')
11const judge = (bet: number, landed: number): Outcome =>
12  bet === landed ? 'jackpot' : colorOf(bet) === colorOf(landed) ? 'color' : 'miss'
13
14export const register: Register = on => {
15  let ticker: Timer | null = null
16
17  on('session.start', async ($, e, next) => {
18    const score = ((await $.store.get('score')) as Game['score'] | undefined) ?? { jackpot: 0, color: 0, miss: 0 }
19    await update($, game, () => ({ bet: spin(), wheel: 0, phase: 'idle', outcome: null, score }))
20    return next(e)
21  })
22
23  on('turn.start', async ($, e, next) => {
24    ticker?.cancel()
25    await update($, game, g => g && { ...g, phase: 'spinning', outcome: null })
26    ticker = $.clock.every(90, () => update($, game, g => g && { ...g, wheel: spin() }))
27    return next(e)
28  })
29
30  on('turn.complete', async ($, e, next) => {
31    ticker?.cancel()
32    ticker = null
33    const landed = spin()
34    const g = await read($, game)
35    if (g) {
36      const outcome = judge(g.bet, landed)
37      const score = { ...g.score, [outcome]: g.score[outcome] + 1 }
38      await update($, game, () => ({ bet: spin(), wheel: landed, phase: 'landed', outcome, score }))
39      await $.store.set('score', score)
40      $.ui.toast(
41        outcome === 'jackpot' ? `🎰 JACKPOT! ${landed} hit your number`
42        : outcome === 'color' ? `🎲 ${colorOf(landed)} hit, number missed`
43        : `💸 ${landed} ${colorOf(landed)}, miss`,
44      )
45    }
46    return next(e)
47  })
48
49  on('ui.render', { component: 'AbovePrompt' }, async ($, e, next) => {
50    const g = await read($, game)
51    if (!g || e.props.hasSurvey) return next(e)
52
53    const { Box, Text } = $.ui.resolve(e)
54    const tint = (n: number) => (colorOf(n) === 'red' ? 'red' : colorOf(n) === 'green' ? 'green' : 'white')
55    const label: Record<Outcome, string> = { jackpot: 'JACKPOT', color: 'COLOR HIT', miss: 'MISS' }
56
57    return (
58      <Box>
59        <Text>🎰 </Text>
60        {g.phase === 'spinning' ? (
61          <Text color={tint(g.wheel)} bold>[ {String(g.wheel).padStart(2, ' ')} ]</Text>
62        ) : g.phase === 'landed' && g.outcome ? (
63          <Text color={g.outcome === 'miss' ? 'red' : 'green'} bold>
64            landed {g.wheel} {colorOf(g.wheel)} · {label[g.outcome]}
65          </Text>
66        ) : (
67          <Text dimColor>place your prompt</Text>
68        )}
69        <Text dimColor>  · your number </Text>
70        <Text color={tint(g.bet)} bold>{g.bet}</Text>
71        <Text dimColor>  · {g.score.jackpot}J {g.score.color}C {g.score.miss}M</Text>
72      </Box>
73    )
74  })
75}
76
types/index.d.ts 15 lines
1export type Outcome = 'jackpot' | 'color' | 'miss'
2export type Game = {
3  bet: number
4  wheel: number
5  phase: 'idle' | 'spinning' | 'landed'
6  outcome: Outcome | null
7  score: Record<Outcome, number>
8}
9
10declare module 'claude-code' {
11  interface PluginState {
12    'roulette': { game: Game | null }
13  }
14}
15