mager-bench

Claude Sonnet 4.6 × slots

0.0

Vegas slot machine — reels, pay table, betting, win animations

correctness 0.0quality 0.0documentation 0.081929ms
$ cat slots.prompt — what the model was asked
Build a fully playable Vegas-style slot machine in a single self-contained HTML file with no external libraries and no external assets.

## Reels
- 3 reels, each with these 7 symbols: 🍒 Cherry, 🍋 Lemon, 🍊 Orange, 🍇 Grape, 🔔 Bell, ⭐ Star, 7️⃣ Seven
- Spin animation: each reel spins independently (reel 1 stops ~800ms, reel 2 ~1200ms, reel 3 ~1600ms), showing symbols scrolling at ~12 symbols/sec before snapping to the result
- SPIN button disabled while spinning

## Pay table (multipliers × bet)
- 7️⃣ 7️⃣ 7️⃣ → 100×
- ⭐ ⭐ ⭐ → 50×
- 🔔 🔔 🔔 → 20×
- 🍇 🍇 🍇 → 15×
- 🍊 🍊 🍊 → 10×
- 🍋 🍋 🍋 → 5×
- 🍒 🍒 🍒 → 3×
- Any two 🍒 in first two positions → 2×
- All other → 0× (loss)

## Betting & credits
- Start with 100 credits
- Bet buttons: 1, 5, 10 credits (highlighted active; default: 1)
- Bet deducted immediately on spin; win adds bet × multiplier
- GAME OVER at 0 credits with 'Play Again' button that resets to 100

## Win feedback
- Win: flash matching symbols, show '+[amount] credits' in gold, display win name ('JACKPOT!', 'THREE BELLS', etc.)
- Loss: shake animation on reels
- Credit balance count-up animation over 400ms

## Pay table display
- Visible panel on page (not a modal) listing all combinations
- Highlight winning row when that combination hits

## Code quality
- Single HTML file; all JS in <script>, all CSS in <style>
- Game state: { credits, bet, spinning, reels, result }
- Named functions: initState(), spin(), checkWin(), animateReels(), updateUI()
- RNG: Math.random() only
- No inline onclick= handlers; use addEventListener

run 1 of 1

0.0
correctness 0.0quality 0.0documentation 0.081929ms

# judge: Response is truncated mid-CSS with no JavaScript at all, so the file is not valid HTML and none of the required game logic or functions exist.

$ cat claude-sonnet-4-6.slots.r1.response
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vegas Slots</title>
<style>
  :root {
    --gold: #ffd700;
    --dark-gold: #b8860b;
    --red: #c0392b;
    --green: #27ae60;
    --bg: #1a0a2e;
    --panel: #2d1b4e;
    --reel-bg: #0d0d0d;
    --border: #6a0dad;
    --text: #f0e6ff;
    --highlight: #ff6b6b;
  }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    background: var(--bg);
    color: var(--text);
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 20px;
    background-image:
      radial-gradient(ellipse at top, #3a0a5e 0%, #1a0a2e 60%),
      repeating-linear-gradient(
        45deg,
        transparent,
        transparent 10px,
        rgba(106,13,173,0.05) 10px,
        rgba(106,13,173,0.05) 20px
      );
  }

  h1 {
    font-size: 2.8rem;
    color: var(--gold);
    text-shadow: 0 0 20px rgba(255,215,0,0.7), 0 0 40px rgba(255,215,0,0.4);
    letter-spacing: 4px;
    margin-bottom: 20px;
    text-transform: uppercase;
    font-weight: 900;
  }

  .main-layout {
    display: flex;
    gap: 24px;
    align-items: flex-start;
    width: 100%;
    max-width: 900px;
  }

  /* ── MACHINE ── */
  .machine {
    flex: 1;
    background: linear-gradient(160deg, #3a1060 0%, #1e0840 100%);
    border: 3px solid var(--border);
    border-radius: 20px;
    padding: 24px;
    box-shadow: 0 0 40px rgba(106,13,173,0.6), inset 0 0 20px rgba(0,0,0,0.4);
    min-width: 320px;
  }

  /* Win announcement */
  #win-announcement {
    text-align: center;
    font-size: 1.5rem;
    font-weight: 900;
    letter-spacing: 2px;
    min-height: 2rem;
    margin-bottom: 12px;
    color: var(--gold);
    text-shadow: 0 0 10px rgba(255,215,0,0.8);
    transition: opacity 0.3s;
  }

  #win-announcement.hidden { opacity: 0; }

  /* Reels container */
  .reels-wrapper {
    background: #000;
    border: 3px solid #444;
    border-radius: 12px;
    padding: 8px;
    box-shadow: inset 0 0 20px rgba(0,0,0,0.8), 0 0 15px rgba(0,0,0,0.5);
    position: relative;
    overflow: hidden;
  }

  .reels-wrapper::before,
  .reels-wrapper::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    height: 60px;
    z-index: 10;
    pointer-events: none;
  }
  .reels-wrapper::before {
    top: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,0.85), transparent);
  }
  .reels-wrapper::after {
    bottom: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.85), transparent);
  }

  .reels-row {
    display: flex;
    gap: 8px;
  }

  /* Payline indicator */
  .payline {
    position: absolute;
    left: 8px; right: 8px;
    top: 50%;
    transform: translateY(-50%);
    height: 3px;
    background: rgba(255,215,0,0.5);
    z-index: 11;
    pointer-events: none;
    box-shadow: 0 0 8px rgba(255,215,0,0.7);
  }

  /* Individual reel */
  .reel {
    flex: 1;
    height: 160px;
    overflow: hidden;
    background: var(--reel-bg);
    border-radius: 8px;
    border: 2px solid #333;
    position: relative;
  }

  .reel-strip {
    display: flex;
    flex-direction: column;
    position: absolute;
    width: 100%;
    top: 0;
    transition: none;
  }

  .reel-symbol {
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2.6rem;
    flex-shrink: 0;
    user-select: none;
    position: relative;
  }

  /* The visible center cell */
  .reel-symbol.result-cell {
    background: rgba(255,255,255,0.03);
  }

  /* Flash animation for winners */
  @keyframes flash-win {
    0%, 100% { background: rgba(255,215,0,0); transform: scale(1); }
    25% { background: rgba(255,215,0,0.3); transform: scale(1.15); }
    75% { background: rgba(255,215,0,0.2); transform: scale(1.1); }
  }

  .reel-symbol.flashing {
    animation: flash-win 0.4s ease-in-out 4;
  }

  /* Shake for loss */
  @keyframes shake {
    0%, 100% { transform: translateX(0); }
    15% { transform: translateX(-6px); }
    30% { transform: translateX(6px); }
    45% { transform: translateX(-5px); }
    60% { transform: translateX(5px); }
    75% { transform: translateX(-3px); }
    90% { transform: translateX(3px); }
  }

  .reels-row.shaking .reel {
    animation: shake 0.5s ease-in-out;
  }

  /* Controls */
  .controls {
    margin-top: 20px;
    display: flex;
    flex-direction: column;
    gap: 14px;
  }

  .bet-section {
    display: flex;
    align-items: center;
    gap: 10px;
    justify-content: center;
  }

  .bet-label {
    font-size: 0.9rem;
    color: #aaa;
    text-transform: uppercase;
    letter-spacing: 1px;
  }

  .bet-btn {
    background: #2a0a4e;
    color: var(--text);
    border: 2px solid #6a0dad;
    border-radius: 8px;
    padding: 8px 18px;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.2s;
    font-weight: 700;
  }

  .bet-btn:hover:not(:disabled) {
    background: #4a1a7e;
    border-color: var(--gold);
  }

  .bet-btn.active {
    background: linear-gradient(135deg, #8b0000, #c0392b);
    border-color: var(--gold);
    color: var(--gold);
    box-shadow: 0 0 10px rgba(255,215,0,0.4);
  }

  .bet-btn:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }

  .spin-btn {
    background: linear-gradient(135deg, #8b0000 0%, #c0392b 50%, #8b0000 100%);
    color: var(--gold);
    border: 3px solid var(--gold);
    border-radius: 50px;
    padding: 16px 40px;
    font-size: 1.5rem;
    font-weight: 900;
    cursor: pointer;
    letter-spacing: 3px;
    text-transform: uppercase;
    transition: all 0.2s;
    box-shadow: 0 4px 15px rgba(192,57,43,0.5), 0 0 20px rgba(255,215,0,0.2);
    width: 100%;
  }

  .spin-btn:hover:not(:disabled) {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(192,57,43,0.7), 0 0 30px rgba(255,215,0,0.4);
    background: linear-gradient(135deg, #a00000 0%, #e74c3c 50%, #a00000 100%);
  }

  .spin-btn:active:not(:disabled) {
    transform: translateY(1px);
  }

  .spin-btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
    transform: none;
  }

  /* Credits bar */
  .credits-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: rgba(0,0,0,0.4);
    border: 2px solid #6a0dad;
    border-radius: 10px;
    padding: 10px 16px;
  }

  .credits-label, .bet-display-label {
    font-size: 0.8rem;
    color: #aaa;
    text-transform: uppercase;
    letter-spacing: 1px;
  }

  .credits-value {
    font-size: 1.6rem;
    font-weight: 900;
    color: var(--gold);
    text-shadow: 0 0 10px rgba(255,215,0,0.5);
    min-width: 60px;
    text-align: right;
  }

  .bet-display {
    text-align: right;
  }

  .bet-value {
    font-size: 1.4rem;
    font-weight: 700;
    color: #ff6b6b;
  }

  /* Win amount */
  #win-amount {
    text-align: center;
    font-size: 1.3rem;
    font-weight: 700;
    min-height: 1.5rem;
    margin-top: 4px;
    color: var(--gold);
    text-shadow: 0 0 10px rgba(255,215,0,0.8);
    opacity: 0;
    transition: opacity 0.3s;
  }

  #win-amount.visible {
    opacity: 1;
  }

  /* GAME OVER overlay */
  #game-over {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.85);
    z-index: 100;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    gap: 24px;
  }

  #game-over.active {
    display: flex;
  }

  .game-over-box {
    background: linear-gradient(135deg, #2d1b4e, #1a0a2e);
    border: 3px solid var(--red);
    border-radius: 20px;
    padding: 50px 60px;
    text-align: center;
    box-shadow: 0 0 60px rgba(192,57,43,0.5);
  }

  .game-over-title {
    font-size: 3rem;
    color: var(--red);
    text-shadow: 0 0 20px rgba(192,57,43,0.8);
    font-weight: 900;
    letter-spacing: 4px;
    margin-bottom: 16px;
  }

  .game-over-text {
    color: #aaa;
    margin-bottom: 30px;
    font-size: 1.1rem;
  }

  .play-again-btn {
    background: linear-gradient(135deg, #27ae60, #2ecc71);
    color: white;
    border: none;
    border-radius: 50px;
    padding: 14px 40px;
    font-size: 1.3rem;
    font-weight: 700;
    cursor: pointer;
    letter-spacing: 2px;
    transition: all 0.2s;
    box-shadow: 0 4px 15px rgba(39,174,96,0.4);
  }

  .play-again-btn:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(39,174,96,0.6);
  }

  /* ── PAY TABLE ── */
  .paytable {
    background: linear-gradient(160deg, #2d1b4e 0%, #1e0840 100%);
    border: 2px solid var(--border);
    border-radius: 16px;
    padding: 20px;
    min-width: 220px;
    box-shadow: 0 0 30px rgba(106,13,173,0.4);
  }

  .paytable h2 {
    color: var(--gold);
    text-align: center;
    font-size: 1.1rem;
    letter-spacing: 2px;
    margin-bottom: 16px;
    text-transform: uppercase;
    text-shadow: 0 0 10px rgba(255,215,0,0.5);
    padding-bottom: 10px;
    border-bottom: 1px solid #6a0dad;
  }

  .paytable-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 7px 10px;
    border-radius: 8px;
    transition: background 0.2s, box-shadow 0.2s;
    gap: 10px;
    margin-bottom: 3px;
  }

  .paytable-row.highlighted {
    background: linear-gradient(135deg, rgba(255,215,0,0.2), rgba(255,215,0,0.1));
    box-shadow: 0 0 12px rgba(255,215,0,0.4);
    animation: pulse-row 0.5s ease-in-out 3;
  }

  @keyframes pulse-row {
    0%, 100% { box-shadow: 0 0 12px rgba(255,215,0,0.4); }
    50% { box-shadow: 0 0 25px rgba(255,215,0,0.8); }
  }

  .paytable-symbols {
    font-size: 1.1rem;
    letter-spacing: 2px;
    flex: 1;
  }

  .paytable-mult {
    font-weight: 700;
    font-size: 0.95rem;
    white-space: nowrap;
  }

  .paytable-mult.jackpot { color: #ff4444; font-size: 1.1rem; }
  .paytable-mult.high { color: var(--gold); }
  .paytable-mult.mid { color: #ff9944; }
  .paytable-mult.low { color: #aaffaa; }

  .paytable-name {
    font-size: 0.7rem;
    color: #888;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    text-align: right;
    min-width: 70px;
  }

  /* Lights */
  .lights {
    display: flex;
    justify-content: center;
    gap: 10px;
    margin-bottom: 12px;
  }

  .light {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #ff0;
    box-shadow: 0 0 6px #ff0;
    animation: blink-lights 1s infinite;
  }

  .light:nth-child(2) { animation-delay: 0.15s; background: #f80; box-shadow: 0 0 6px #f80; }
  .light:nth-child(3) { animation-delay: 0.3s; background: #0f0; box-shadow: 0 0 6px #0f0; }
  .light:nth-child(4) { animation-delay: 0.45s; background: #0ff; box-shadow: 0 0 6px #0ff; }
  .light:nth-child(5) { animation-delay: 0.6s; background: #f0f; box-shadow: 0 0 6px #f0f; }
  .light:nth-child(6) { animation-delay: 0.75s; }
  .light:nth-child(7) { animation-delay: 0.9s; background: #f80; box-shadow: 0 0 6px #f80; }

  @keyframes blink-lights {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
  }

  @media (max-width: 650px) {
    .main-layout { flex-direction: column; align-items: center; }
    .paytable { width: 100%; max-width: 400px; }
    h1 { font-size: 2rem; }
  }
</style>
</head>
<body>

<h1>🎰 Vegas Slots 🎰</h1>

<div class="main-layout">

  <!-- SLOT MACHINE -->
  <div class="machine">
    <div class="lights">
      <div class="light"></div>
      <div class="light"></div>
      <div class="light"></div>
      <div class="light"></div>
      <div class="light"></div>
      <div class="light"></div>
      <div class="light"></div>
    </div>

    <div id="win-announcement" class="hidden"></div>

    <div class="reels-wrapper">
      <div class="payline"></div>
      <div class="reels-row" id="reels-row">
        <div class="reel" id="reel-0"><div class="reel-strip" id="strip-0"></div></div>
        <div class="reel" id="reel-1"><div class="reel-strip" id="strip-1"></div></div>
        <div class="reel" id="reel-2"><div class="reel-strip" id="strip-2"></div></div>
      </div>
    </div>

    <div id="win-amount"></div>

    <div class="controls">
      <div class="credits-bar">
        <div>
          <div class="credits-label">Credits</div>
          <div class="credits-value" id="credits-display">100</div>
        </div>
        <div class="bet-display">
          <div class="bet-display-label">Current Bet</div>
          <div class="bet-value" id="bet-display">1</div>
        </div>
      </div>

      <div class="bet-section">
        <span class="bet-label">Bet:</span>
        <button class="bet-btn active" id="bet-1" data-bet="1">1</button>
        <button class="bet-btn" id="bet-5" data-bet="5">5</button>
        <button class="bet-btn" id="bet-10" data-bet="10">10</button>
      </div>

      <button class="spin-btn" id="spin-btn">SPIN</button>
    </div>
  </div>

  <!-- PAY TABLE -->
  <div class="paytable">
    <h2>Pay Table</h2>
    <div id="paytable-rows"></div>
  </div>

</div>

<!-- GAME OVER -->
<div id="game-over">
  <div class="game-over-box">
    <div class="game-over-title">GAME OVER</div>
    <div class="game-over-text">You've run out of credits!<br>Better luck next time.</div>
    <button class="play-again-btn" id="play-again-btn">Play Again</button>
  </div>
</div>

<script>
'use strict';

// ── Constants ──────────────────────────────────────────────────────────────────

const SYMBOLS = ['🍒','🍋','🍊','🍇','🔔','⭐','7️⃣'];

const PAYTABLE = [
  { key: 'three-sevens',  symbols: ['7️⃣','7️⃣','7️⃣'], mult: 100, label: 'JACKPOT!!!',     tier: 'jackpot' },
  { key: 'three-stars',   symbols: ['⭐','⭐','⭐'],   mult: 50,  label: 'TRIPLE STARS',   tier: 'high' },
  { key: 'three-bells',   symbols: ['🔔','🔔','🔔'],   mult: 20,  label: 'THREE BELLS',    tier: 'high' },
  { key: 'three-grapes',  symbols: ['🍇','🍇','🍇'],   mult: 15,  label: 'TRIPLE GRAPES',  tier: 'mid' },
  { key: 'three-oranges', symbols: ['🍊','🍊','🍊'],   mult: 10,  label: 'THREE ORANGES',  tier: 'mid' },
  { key: 'three-lemons',  symbols: ['🍋','🍋','🍋'],   mult: 5,   label: 'THREE LEMONS',   tier: 'low' },
  { key: 'three-cherries',symbols: ['🍒','🍒','🍒'],   mult: 3,   label: 'THREE CHERRIES', tier: 'low' },
  { key: 'two-cherries',  symbols: ['🍒','🍒','❓'],   mult: 2,   label: 'DOUBLE CHERRY',  tier: 'low' },
];

const REEL_STOP_TIMES = [800, 1200, 1600];
const SYMBOLS_PER_SEC = 12;
const SYMBOL_HEIGHT = 80; // px, must match CSS .reel-symbol height
const REEL_HEIGHT    = 160; // px, visible reel height (2 symbols)
const VISIBLE_ROWS   = 2;
const STRIP_COUNT    = 30; // symbols in a spinning strip

// ── State ──────────────────────────────────────────────────────────────────────

let state = {};

function initState() {
  state = {
    credits: 100,
    bet: 1,
    spinning: false,
    reels: ['🍒', '🍒', '🍒'],   // current result symbols
    result: null,                  // matching PAYTABLE entry or null
  };
}

// ── DOM refs ───────────────────────────────────────────────────────────────────

const spinBtn        = document.getElementById('spin-btn');
const creditsDisplay = document.getElementById('credits-display');
const betDisplay     = document.getElementById('bet-display');
const winAnnounce    = document.getElementById('win-announcement');
const winAmount      = document.getElementById('win-amount');
const reelsRow       = document.getElementById('reels-row');
const gameOverEl     = document.getElementById('game-over');
const playAgainBtn   = document.getElementById('play-again-btn');
const paytableRows   = document.getElementById('paytable-rows');

// ── Paytable render ────────────────────────────────────────────────────────────

function renderPaytable() {
  paytableRows.innerHTML = '';
  PAYTABLE.forEach(entry => {
    const row = document.createElement('div');
    row.className = 'paytable-row';
    row.id = 'pt-' + entry.key;

    const symsDisplay = entry.key === 'two-cherries'
      ? '🍒 🍒 ❓'
      : entry.symbols.join(' ');

    row.innerHTML = `
      <span class="paytable-symbols">${symsDisplay}</span>
      <span class="paytable-mult ${entry.tier}">${entry.mult}×</span>
      <span class="paytable-name">${entry.label}</span>
    `;
    paytableRows.appendChild(row);
  });
}

function highlightPaytableRow(key) {
  // Remove all highlights first
  document.querySelectorAll('.paytable-row').forEach(r => r.classList.remove('highlighted'));
  if (key) {
    const row = document.getElementById('pt-' + key);
    if (row) row.classList.add('highlighted');
  }
}

// ── RNG helpers ────────────────────────────────────────────────────────────────

function randomSymbol() {
  return SYMBOLS[Math.floor(Math.random() * SYMBOLS.length)];
}

function randomStripSymbols(count) {
  const arr = [];
  for (let i = 0; i < count; i++) arr.push(randomSymbol());
  return arr;
}

// ── Win check ─────────────────────────────────────────────────────────────────

function checkWin(reels) {
  const [r0, r1, r2] = reels;

  for (const entry of PAYTABLE) {
    if (entry.key === 'two-cherries') {
      if (r0 === '🍒' && r1 === '🍒') return entry;
    } else {
      if (r0 === entry.symbols[0] && r1 === entry.symbols[1] && r2 === entry.symbols[2]) return entry;
    }
  }
  return null;
}

// ── Credit count-up animation ─────────────────────────────────────────────────

function animateCredits(fromVal, toVal) {
  const duration = 400;
  const start = performance.now();
  function step(now) {
    const elapsed = now