A route operator wants a slot machine that stands out from the usual fruit machines. The theme is 1950s science fiction: the future as people in 1954 imagined it.
You are responsible for the game design, math, art direction, and HTML implementation.
Create a complete slot machine as one self-contained HTML page.
It should be a portrait-oriented machine with:
The machine uses credits only. There is no cash, purchasing, accounts, or real-money gambling.
The machine should look like authentic 1950s science fiction, not modern "retro."
Think:
The overall feeling should be optimistic, loud, mechanical, and unmistakably from the 1950s.
Choose the 16 objects that appear on the reels.
This is an important design decision. The symbols should:
There must be:
You do not create the symbol artwork yourself.
Instead, provide prompts that will later be used to generate it.
The symbols will be delivered as one PNG:
art/symbols.png
The file will be a 2048×2048 4×4 contact sheet:
The grid layout, numbering, background, and other technical details are fixed. Your job is to specify what belongs in each cell.
The path art/symbols.png must appear exactly once in your HTML.
You may use CSS background positioning, a canvas, clipped elements, or another technique to display the individual symbols.
Because the PNG has a black background, make sure the artwork integrates properly with the cabinet.
The machine must still work before art/symbols.png exists.
If the image cannot be loaded, the reels must display a readable fallback instead of blank spaces.
Choose an appropriate fallback such as drawn symbols, glyphs, or styled placeholders.
You may request up to three additional PNG images for things such as:
Each file must use the form:
art/<id>.png
These images are optional decoration. The machine must still look finished without them.
The machine has:
Use 10–25 paylines.
Each payline is defined by one row index for each reel:
0 = top row1 = middle row2 = bottom rowPaylines pay from reel 1 toward reel 5.
Each reel has its own symbol strip containing 20–60 symbols.
Every one of the 16 symbols must appear on at least one reel strip.
There is exactly one wild symbol.
The wild substitutes for any symbol except the scatter.
There is exactly one scatter symbol.
Three or more scatters anywhere on the visible 5×3 grid start a feature.
The feature is up to you. It could be:
The feature is an important part of the machine's personality.
Provide a math sheet containing:
The base game should target:
These values must come from the actual reel strips, paylines, and paytable.
Do not simply claim a target percentage. Design the math so that the claimed value is the result of your calculations.
The checker runs 200,000 spins using a fixed seed.
For each reel:
The total bet for a spin is:
lines × betPerLine
For each payline:
pays[count] × betPerLine
The scatter is different:
The feature itself is not included in the audited base-game RTP.
The machine has exactly one control for playing.
Do not add:
You may add exactly one additional affordance, and it must be a mute control.
Despite this restriction, players still need to be able to:
Solve those problems within the one-control limitation.
All sound must be generated with the Web Audio API.
Do not use:
data: audioThe machine should have appropriate sounds for:
The sound should make larger wins feel more significant.
Make sure the audio works when the player presses the Play button. Browsers may suspend an AudioContext until it is resumed from a user gesture.
The page must support two URL modes.
#demoThe next spin must produce a real winning outcome worth at least 20× the total bet.
This must not be a fake animation.
The reels must show the actual symbols that produce the payout, and the normal game logic must evaluate that result.
#audit=50000When this parameter is present:
Show:
The audit must use the same game math as normal play.
The machine must be a single HTML file.
It must:
art/ filesDo not use:
localStoragesessionStorageindexedDBThe page must have no scrollbars at any window size.
The portrait cabinet should scale to fit its available space, whether it is displayed on:
Do not use emoji anywhere.
Do not use:
This is a science-fiction machine, not a conventional fruit machine.
Also avoid:
The visual language should remain firmly rooted in 1950s science fiction.
Your response must contain exactly these four sections.
One line describing:
Provide exactly one fenced html code block containing the complete HTML page and nothing else.
Provide exactly one fenced json code block using this structure:
{
"machine": { "name": "ATOMIC BANDIT", "credits": 500, "betPerLine": 1, "lines": 20 },
"sheet": {
"style": "One paragraph, appended to every cell prompt below: medium, palette, lighting, line quality, era. This is what makes sixteen separate objects look like one set.",
"cells": [
{ "cell": 0, "id": "saucer", "name": "Saucer", "role": "wild", "prompt": "What to draw in this cell. One object.", "pays": { "3": 25, "4": 100, "5": 500 } },
{ "cell": 1, "id": "beacon", "name": "Beacon", "role": "scatter", "prompt": "...", "pays": { "3": 2, "4": 10, "5": 50 } },
{ "cell": 2, "id": "helmet", "name": "Bubble helmet", "role": "pay", "prompt": "...", "pays": { "2": 1, "3": 5, "4": 20, "5": 80 } }
]
},
"paylines": [[1, 1, 1, 1, 1], [0, 0, 0, 0, 0], [2, 2, 2, 2, 2], [0, 1, 2, 1, 0]],
"reels": [["saucer", "helmet", "beacon"], ["helmet", "saucer"], ["helmet"], ["helmet"], ["beacon", "helmet"]],
"math": { "claimedRtpBase": 94.1, "claimedHitFrequency": 27.4, "claimedFeatureRtp": 2.0, "volatility": "medium" },
"cabinet": [{ "id": "backdrop", "aspect": "9:16", "prompt": "..." }]
}
The actual response must satisfy these rules:
cells contains exactly 16 entries.cell values are 0–15 with no gaps or duplicates.id is a single lowercase token.wild, one scatter, and fourteen pay symbols.pays contains credit values for each matching count."2" is optional."3" is required.paylines contain one row index per reel.reels contains five strips.cabinet may contain up to three entries.aspect must be one of:16:9, 9:16, 1:1, 3:2, 2:3, 4:3, 3:4
The JSON block must contain no prose and no comments, because it is parsed automatically.
Provide:
art/symbols.png is unavailable.Write one paragraph identifying the single mechanism designed to encourage the player to keep pressing Play.
Name it plainly and explain how it works.
Do not use euphemisms such as "player engagement." Explain the actual mechanism.
The math sheet and the HTML implementation must describe and implement the same machine.
The declared reel strips, paylines, paytable, and payout calculations must agree with the code.
The #demo result must be a real game outcome.
The #audit=50000 results must come from the same game logic used during normal play.
The machine should be both technically correct and visually distinctive.
THE ATOMIC BANDIT — a five-reel space-carnival cabinet in chrome and bakelite: pull up a stool, hear the relays chitter, and watch chrome rockets, ray-guns and flying saucers march across a deep-space window under a marquee of warm amber bulbs.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>THE ATOMIC BANDIT</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{width:100%;height:100%;overflow:hidden}
body{
background:
radial-gradient(120% 70% at 50% -10%, rgba(120,160,210,.16), transparent 60%),
repeating-radial-gradient(circle at 50% 130%, rgba(90,130,180,.05) 0 2px, transparent 2px 90px),
repeating-linear-gradient(45deg, rgba(255,255,255,.012) 0 2px, transparent 2px 6px),
radial-gradient(ellipse at 50% 45%, #0d1b33 0%, #060d1c 62%, #02050c 100%);
display:flex;align-items:center;justify-content:center;
font-family:"Trebuchet MS","Lucida Grande",Verdana,sans-serif;
user-select:none;-webkit-user-select:none;
}
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:99;
background:radial-gradient(circle at 50% 46%, transparent 55%, rgba(0,0,0,.5) 100%);}
.stars{position:fixed;inset:0;pointer-events:none;z-index:0}
.stars i{position:absolute;border-radius:50%;background:#cfe0f2;animation:tw 3.4s ease-in-out infinite}
@keyframes tw{0%,100%{opacity:.15}50%{opacity:.9}}
#bgart{position:fixed;inset:0;background-size:cover;background-position:center;opacity:.4;z-index:0;
-webkit-mask-image:radial-gradient(circle at 50% 40%, #000 30%, transparent 78%);
mask-image:radial-gradient(circle at 50% 40%, #000 30%, transparent 78%);}
#stage{position:relative;z-index:2}
#cab{
width:1000px;height:1600px;position:relative;
background:linear-gradient(180deg,#152741,#0d1a2e 40%,#0a1424);
border-radius:44px 44px 26px 26px;
box-shadow:0 40px 90px rgba(0,0,0,.7), inset 0 0 0 3px #050a14, inset 0 0 0 10px rgba(214,226,238,.12);
padding:22px 30px 26px;
}
#cab::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
background:radial-gradient(90% 30% at 50% 0%, rgba(255,255,255,.07), transparent 70%);}
#cab::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
background:repeating-linear-gradient(45deg, rgba(255,255,255,.014) 0 3px, transparent 3px 9px);}
.rvt{position:absolute;width:13px;height:13px;border-radius:50%;z-index:5;
background:radial-gradient(circle at 34% 30%, #f2f7fb, #9fb0c0 45%, #3c4a5a 100%);
box-shadow:0 2px 3px rgba(0,0,0,.65);}
/* ---------- marquee ---------- */
#marquee{position:relative;height:172px;border-radius:26px 26px 12px 12px;
background:
radial-gradient(140% 160% at 50% 118%, rgba(255,122,26,.34), transparent 62%),
repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 3px, transparent 3px 8px),
linear-gradient(180deg,#233a5c,#101f38 70%,#0c182c);
border:7px solid;border-image:linear-gradient(180deg,#f8fbfe,#b9c6d4 30%,#5c6c80 62%,#e8eff7 82%,#77879a) 1;
box-shadow:inset 0 0 0 3px #0a1220, inset 0 -14px 30px rgba(0,0,0,.5), 0 6px 0 rgba(0,0,0,.35);
display:flex;align-items:center;justify-content:center;overflow:hidden;}
#marquee h1{font-family:Impact,"Arial Black","Franklin Gothic Bold",sans-serif;
font-size:88px;line-height:.92;letter-spacing:.04em;text-align:center;color:#ff8a1e;
text-shadow:0 2px 0 #7a2c00, 0 5px 0 rgba(0,0,0,.4), 0 0 26px rgba(255,138,30,.45);
transform:scaleY(1.06);}
#marquee h1 b{display:block;color:#ffe9c4;font-size:64px;letter-spacing:.34em;text-indent:.34em;
text-shadow:0 2px 0 #4a3011, 0 0 18px rgba(255,200,120,.5);}
#marquee .atom{position:absolute;width:120px;height:120px;opacity:.9}
#marquee .atom.a1{left:26px;top:20px}#marquee .atom.a2{right:26px;top:20px}
.bulbs{position:absolute;left:26px;right:26px;display:flex;justify-content:space-between;z-index:3}
.bulbs.top{top:9px}.bulbs.bot{bottom:9px}
.bulbs i{width:13px;height:13px;border-radius:50%;
background:radial-gradient(circle at 35% 30%, #fff6d8, #ffb43a 55%, #8a4a00);
box-shadow:0 0 9px 2px rgba(255,170,60,.75);animation:chase 1.15s steps(1) infinite;}
@keyframes chase{0%,100%{opacity:1}50%{opacity:.22;box-shadow:none}}
/* ---------- chrome bars ---------- */
.bar{height:17px;margin:12px 4px;border-radius:9px;position:relative;
background:linear-gradient(180deg,#fdfefe 0%,#c9d6e2 28%,#67788c 50%,#dfe9f2 56%,#f6fafd 78%,#8b9cae 100%);
box-shadow:0 3px 5px rgba(0,0,0,.5), inset 0 -2px 3px rgba(0,0,0,.25);}
.bar::before,.bar::after{content:"";position:absolute;top:2px;width:13px;height:13px;border-radius:50%;
background:radial-gradient(circle at 34% 30%, #fff, #93a3b3 50%, #38465a);}
.bar::before{left:8px}.bar::after{right:8px}
/* ---------- belly glass ---------- */
#belly{position:relative;height:118px;margin:0 6px;border-radius:14px;overflow:hidden;
background:
radial-gradient(60% 130% at 50% 120%, rgba(255,122,26,.5), transparent 70%),
repeating-linear-gradient(45deg, rgba(255,255,255,.03) 0 2px, transparent 2px 7px),
linear-gradient(180deg,#1b2f4f,#0e1c33);
border:5px solid;border-image:linear-gradient(180deg,#eef4fa,#8b9cae 45%,#dfe9f2) 1;
box-shadow:inset 0 0 0 2px #0a1220, inset 0 10px 24px rgba(0,0,0,.5);
display:flex;align-items:center;justify-content:center;gap:26px;}
#bellyart{position:absolute;inset:0;background-size:cover;background-position:center;opacity:.55}
#belly .tag{position:relative;font-family:Impact,"Arial Black",sans-serif;font-size:30px;letter-spacing:.14em;
color:#ffe9c4;text-shadow:0 2px 0 #40230a, 0 0 16px rgba(255,150,50,.55);}
#belly .tag em{font-style:normal;color:#ff8a1e}
#belly .rocket{position:relative;width:64px;height:64px}
/* ---------- meter deck ---------- */
#deck{display:flex;gap:14px;align-items:stretch;margin:14px 2px 12px}
.tube{width:26px;min-width:26px;border-radius:13px;position:relative;
background:linear-gradient(90deg,rgba(255,150,40,.05),rgba(255,150,40,.45),rgba(255,150,40,.05));
border:2px solid #3a2a14;box-shadow:inset 0 0 10px rgba(255,140,30,.35);}
.tube i{position:absolute;left:3px;right:3px;bottom:3px;height:55%;border-radius:9px;
background:linear-gradient(180deg,#ffd98c,#ff8a1e);box-shadow:0 0 14px rgba(255,150,40,.8);
animation:tubeGlow 2.6s ease-in-out infinite;}
@keyframes tubeGlow{0%,100%{opacity:.75}50%{opacity:1}}
.meter{flex:1;background:linear-gradient(180deg,#0b0f16,#05080e);border-radius:10px;
border:3px solid;border-image:linear-gradient(180deg,#dfe9f2,#67788c 55%,#c9d6e2) 1;
box-shadow:inset 0 4px 14px rgba(0,0,0,.85);padding:7px 12px 8px;text-align:center;}
.meter label{display:block;font-size:15px;letter-spacing:.3em;color:#8fa3bd;font-weight:bold}
.meter output{font-family:"Courier New",Courier,monospace;font-weight:bold;font-size:46px;line-height:1.05;
color:#ffb43a;text-shadow:0 0 12px rgba(255,150,40,.75),0 0 2px rgba(255,180,70,.9);}
.meter.win output{color:#ffd98c}
.meter.bump output{animation:bump .4s ease}
@keyframes bump{0%{transform:scale(1)}35%{transform:scale(1.16)}100%{transform:scale(1)}}
/* ---------- reel window ---------- */
#windowFrame{position:relative;margin:0 2px;padding:16px 18px;border-radius:18px;
background:linear-gradient(180deg,#f4f8fb 0%,#b9c6d4 22%,#5f7085 50%,#dbe5ee 58%,#f2f6fa 80%,#7c8da0 100%);
box-shadow:0 8px 18px rgba(0,0,0,.55), inset 0 0 0 2px rgba(20,30,45,.5);}
#windowFrame .screw{position:absolute;width:16px;height:16px;border-radius:50%;
background:radial-gradient(circle at 34% 30%, #fff, #93a3b3 55%, #2e3c50);box-shadow:0 2px 3px rgba(0,0,0,.6);}
#windowFrame .screw::after{content:"";position:absolute;left:2px;right:2px;top:7px;height:2px;background:#26313f;transform:rotate(38deg)}
#windowFrame .screw:nth-child(1){left:8px;top:8px}#windowFrame .screw:nth-child(2){right:8px;top:8px}
#windowFrame .screw:nth-child(3){left:8px;bottom:8px}#windowFrame .screw:nth-child(4){right:8px;bottom:8px}
#reelWindow{position:relative;height:570px;border-radius:10px;overflow:hidden;background:#081120;
box-shadow:inset 0 0 0 3px #04070d, inset 0 0 40px rgba(0,0,0,.8);}
#reels{position:absolute;inset:0;display:flex;gap:8px;padding:0 10px;
background:
radial-gradient(120% 90% at 50% 50%, rgba(90,130,190,.16), transparent 70%),
repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 2px, transparent 2px 6px),
linear-gradient(180deg,#0d1a30,#081222 60%,#060d1a);}
.reel{flex:1;position:relative;overflow:hidden;border-radius:6px;background:rgba(4,8,16,.4)}
.strip{position:absolute;left:0;right:0;top:0;will-change:transform}
.cell{position:relative;width:100%;height:190px}
.cell::after{content:"";position:absolute;left:6%;right:6%;bottom:0;height:2px;
background:linear-gradient(90deg,transparent,rgba(160,190,220,.22),transparent);}
.sym{position:absolute;inset:5px}
.sym.img{background-image:url(art/symbols.png);background-size:400% 400%;background-repeat:no-repeat}
.reel::before,.reel::after{content:"";position:absolute;left:0;right:0;z-index:4;pointer-events:none}
.reel::before{top:0;height:26%;background:linear-gradient(180deg,rgba(2,5,10,.85),transparent)}
.reel::after{bottom:0;height:26%;background:linear-gradient(0deg,rgba(2,5,10,.85),transparent)}
.reel.spinblur .strip{filter:blur(1.1px) brightness(1.12)}
#rowBars{position:absolute;left:0;right:0;top:0;bottom:0;z-index:6;pointer-events:none}
#rowBars i{position:absolute;left:0;right:0;height:33.334%;opacity:0;transition:opacity .25s;
border-top:3px solid rgba(255,150,40,.9);border-bottom:3px solid rgba(255,150,40,.9);
box-shadow:inset 0 0 34px rgba(255,130,30,.28), 0 0 18px rgba(255,130,30,.35);
background:rgba(255,140,40,.05);}
#rowBars i.on{opacity:1}
#rowBars i:nth-child(1){top:0}#rowBars i:nth-child(2){top:33.333%}#rowBars i:nth-child(3){top:66.666%}
#payOverlay{position:absolute;inset:0;z-index:7}
#payOverlay svg{width:100%;height:100%;display:block}
#payOverlay polyline{fill:none;stroke:#ffb43a;stroke-width:6;stroke-linejoin:round;stroke-linecap:round;
filter:drop-shadow(0 0 7px rgba(255,160,50,.9));animation:wire .8s steps(2) infinite}
@keyframes wire{0%,100%{opacity:1}50%{opacity:.45}}
#winGlow{position:absolute;inset:0;z-index:5;pointer-events:none;opacity:0;
background:radial-gradient(70% 70% at 50% 50%, rgba(255,170,60,.34), transparent 75%);}
#winGlow.go{animation:glowPulse 1s ease-in-out infinite}
@keyframes glowPulse{0%,100%{opacity:.25}50%{opacity:.9}}
.cell.hit .sym{animation:symPop .55s ease infinite alternate}
@keyframes symPop{from{transform:scale(1);filter:brightness(1)}to{transform:scale(1.12);filter:brightness(1.5) drop-shadow(0 0 10px rgba(255,170,60,.9))}}
/* ---------- plaque ---------- */
#plaque{margin:13px 30px 4px;padding:9px 14px;text-align:center;border-radius:8px;cursor:pointer;
background:linear-gradient(180deg,#2a1c0c,#1a1006);border:3px solid;border-image:linear-gradient(180deg,#e8c66a,#8a6a20,#e8c66a) 1;
box-shadow:inset 0 0 12px rgba(0,0,0,.7);}
#plaque span{font-family:Impact,"Arial Black",sans-serif;font-size:21px;letter-spacing:.16em;color:#ffd98c;
text-shadow:0 0 10px rgba(255,180,80,.6);}
#plaque em{font-style:normal;color:#ff8a1e}
/* ---------- console ---------- */
#console{position:relative;margin-top:12px;padding:20px 26px 24px;border-radius:20px;
background:linear-gradient(180deg,#3a4b63,#22304a 30%,#17233a 75%,#101b2e);
border:5px solid;border-image:linear-gradient(180deg,#f4f8fb,#67788c 50%,#dbe5ee) 1;
box-shadow:0 10px 22px rgba(0,0,0,.55), inset 0 2px 0 rgba(255,255,255,.25);}
#console .grille{position:absolute;top:22px;width:120px;height:96px;border-radius:10px;
background:repeating-linear-gradient(90deg,#0a1220 0 6px,#33415a 6px 10px);
box-shadow:inset 0 0 0 3px #0a1220, inset 0 6px 14px rgba(0,0,0,.7);}
#console .grille.gl{left:26px}#console .grille.gr{right:26px}
#bigwin{position:absolute;left:50%;top:14px;transform:translateX(-50%);min-width:300px;text-align:center;
padding:6px 22px;border-radius:10px;background:#0a0e15;border:3px solid #3a2a14;
box-shadow:inset 0 0 18px rgba(255,140,30,.25);}
#bigwin span{display:block;font-size:14px;letter-spacing:.42em;color:#8fa3bd;font-weight:bold}
#bigwin output{font-family:"Courier New",Courier,monospace;font-weight:bold;font-size:44px;color:#5c4a2a;transition:color .2s}
#bigwin.lit output{color:#ffd98c;text-shadow:0 0 16px rgba(255,180,70,.9),0 0 3px #ffdf9e}
#bigwin.lit{box-shadow:inset 0 0 26px rgba(255,150,40,.5),0 0 22px rgba(255,150,40,.35)}
#playBtn{position:relative;display:block;margin:26px auto 0;width:330px;height:112px;border:none;cursor:pointer;
border-radius:56px;font-family:Impact,"Arial Black",sans-serif;font-size:47px;letter-spacing:.1em;color:#5a1c00;
background:radial-gradient(120% 160% at 50% 8%, #ffc26e 0%, #ff8a1e 38%, #e5540a 72%, #a33200 100%);
box-shadow:0 0 0 8px #0a1220, 0 0 0 13px #b9c6d4, 0 0 0 16px #4a5a6e, 0 10px 0 #7a2c00, 0 16px 26px rgba(0,0,0,.6),
inset 0 4px 8px rgba(255,255,255,.5), inset 0 -10px 16px rgba(120,30,0,.55);
text-shadow:0 2px 0 rgba(255,230,180,.6);transition:transform .06s, box-shadow .06s, filter .2s;}
#playBtn::after{content:"";position:absolute;left:14%;right:14%;top:9px;height:26px;border-radius:26px;
background:linear-gradient(180deg,rgba(255,255,255,.65),transparent);}
#playBtn:active{transform:translateY(8px);
box-shadow:0 0 0 8px #0a1220,0 0 0 13px #b9c6d4,0 0 0 16px #4a5a6e,0 2px 0 #7a2c00,0 6px 14px rgba(0,0,0,.6),
inset 0 4px 8px rgba(255,255,255,.4), inset 0 -8px 14px rgba(120,30,0,.55);}
#playBtn:disabled{filter:saturate(.35) brightness(.7);cursor:default}
#muteBtn{position:absolute;right:24px;bottom:22px;width:62px;height:62px;border-radius:50%;cursor:pointer;
border:4px solid #0a1220;box-shadow:0 0 0 3px #8b9cae, 0 5px 10px rgba(0,0,0,.55), inset 0 3px 6px rgba(255,255,255,.35);
background:radial-gradient(circle at 40% 30%, #e8eef5, #9fb0c0 55%, #4a5a6e);display:flex;align-items:center;justify-content:center;}
#muteBtn svg{width:34px;height:34px}
#muteBtn .slash{opacity:0;stroke:#a33200;stroke-width:5;stroke-linecap:round}
#muteBtn.muted .slash{opacity:1}
#muteBtn.muted .wave{opacity:.2}
/* ---------- paytable overlay ---------- */
#paytable{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;
background:rgba(3,7,14,.82);}
#paytable.open{display:flex}
#ptCard{width:640px;max-width:94vw;max-height:92vh;overflow:hidden;border-radius:20px;display:flex;flex-direction:column;
background:
repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 3px, transparent 3px 8px),
linear-gradient(180deg,#16273f,#0c1728);
border:6px solid;border-image:linear-gradient(180deg,#f4f8fb,#67788c 50%,#dbe5ee) 1;
box-shadow:0 30px 70px rgba(0,0,0,.8), inset 0 0 0 3px #050a14;}
#ptCard header{padding:14px 18px 10px;text-align:center;border-bottom:3px solid #0a1220;
background:radial-gradient(120% 180% at 50% 130%, rgba(255,122,26,.3), transparent 70%);}
#ptCard h2{font-family:Impact,"Arial Black",sans-serif;font-size:34px;letter-spacing:.18em;color:#ff8a1e;
text-shadow:0 2px 0 #5a1c00,0 0 18px rgba(255,138,30,.5);}
#ptCard header small{display:block;color:#8fa3bd;font-size:14px;letter-spacing:.22em;margin-top:2px}
#ptGrid{overflow-y:auto;padding:14px 16px;display:grid;grid-template-columns:1fr 1fr;gap:10px;scrollbar-width:thin}
.ptRow{display:flex;align-items:center;gap:10px;padding:8px 10px;border-radius:10px;
background:rgba(255,255,255,.035);border:2px solid rgba(160,185,215,.14);}
.ptRow .ic{position:relative;width:56px;height:56px;flex:none}
.ptRow .nm{font-size:14px;font-weight:bold;letter-spacing:.06em;color:#cfe0f2;line-height:1.1}
.ptRow .nm i{display:block;font-style:normal;font-size:11px;letter-spacing:.2em;color:#ff9a3e}
.ptRow .pv{margin-left:auto;text-align:right;font-family:"Courier New",monospace;font-weight:bold;
font-size:14px;color:#ffb43a;line-height:1.25;white-space:nowrap}
.ptRow.wild{border-color:rgba(255,150,40,.55)}.ptRow.sct{border-color:rgba(140,190,255,.45)}
#ptRules{padding:12px 20px 16px;border-top:3px solid #0a1220;color:#9fb3cc;font-size:14px;line-height:1.5}
#ptRules b{color:#ffd98c}
#ptClose{display:block;margin:0 auto 18px;padding:10px 46px;border:none;cursor:pointer;border-radius:30px;
font-family:Impact,"Arial Black",sans-serif;font-size:22px;letter-spacing:.14em;color:#5a1c00;
background:linear-gradient(180deg,#ffc26e,#ff8a1e 55%,#e5540a);
box-shadow:0 4px 0 #7a2c00, 0 0 0 4px #0a1220, 0 0 0 6px #67788c;}
/* ---------- audit ---------- */
#audit{position:fixed;inset:0;z-index:60;display:none;overflow-y:auto;padding:5vh 6vw;
background:radial-gradient(100% 80% at 50% 20%, #14253f, #060c18);color:#cfe0f2;
font-family:"Trebuchet MS",Verdana,sans-serif;}
#audit.open{display:block}
#audit h2{font-family:Impact,"Arial Black",sans-serif;font-size:40px;letter-spacing:.14em;color:#ff8a1e;margin-bottom:4px}
#audit .sub{color:#8fa3bd;letter-spacing:.2em;font-size:14px;margin-bottom:22px}
#audit table{border-collapse:collapse;width:100%;max-width:760px}
#audit td{padding:9px 12px;border-bottom:2px dotted rgba(160,185,215,.25);font-size:18px}
#audit td:first-child{letter-spacing:.12em;color:#9fb3cc;font-size:14px}
#audit td:last-child{font-family:"Courier New",monospace;font-weight:bold;color:#ffd98c;text-align:right}
#audit .note{margin-top:20px;color:#7d90aa;font-size:13px;max-width:760px;line-height:1.5}
/* ---------- out of credits ---------- */
#refill{position:absolute;left:50%;bottom:190px;transform:translateX(-50%);z-index:20;display:none;
padding:14px 30px;border-radius:12px;text-align:center;
background:linear-gradient(180deg,#2a1c0c,#160d04);border:4px solid #e8c66a;
box-shadow:0 0 30px rgba(255,170,60,.35), inset 0 0 14px rgba(0,0,0,.7);animation:refillPulse 1.4s ease-in-out infinite}
#refill.on{display:block}
#refill b{display:block;font-family:Impact,"Arial Black",sans-serif;font-size:26px;letter-spacing:.12em;color:#ffd98c}
#refill span{font-size:14px;letter-spacing:.2em;color:#c9a24a}
@keyframes refillPulse{0%,100%{box-shadow:0 0 18px rgba(255,170,60,.25)}50%{box-shadow:0 0 40px rgba(255,170,60,.6)}}
/* ---------- feature ---------- */
#feature{position:fixed;inset:0;z-index:40;display:none;align-items:center;justify-content:center;
background:rgba(3,7,14,.86);}
#feature.open{display:flex}
#featCard{position:relative;width:680px;max-width:94vw;padding:26px 30px 30px;border-radius:24px;text-align:center;
background:repeating-linear-gradient(45deg, rgba(255,255,255,.02) 0 3px, transparent 3px 8px),
linear-gradient(180deg,#1a2c49,#0d1a2e 60%,#0a1424);
border:7px solid;border-image:linear-gradient(180deg,#f4f8fb,#67788c 50%,#dbe5ee) 1;
box-shadow:0 34px 80px rgba(0,0,0,.85), inset 0 0 0 3px #050a14;}
#featCard h2{font-family:Impact,"Arial Black",sans-serif;font-size:44px;letter-spacing:.1em;color:#ff8a1e;
text-shadow:0 2px 0 #5a1c00,0 0 22px rgba(255,138,30,.55);}
#featCard .sub{color:#8fa3bd;letter-spacing:.26em;font-size:15px;margin:4px 0 14px}
#featStatus{min-height:34px;font-size:19px;color:#ffd98c;letter-spacing:.08em;margin-bottom:8px}
#featTotal{font-family:"Courier New",monospace;font-weight:bold;font-size:30px;color:#ffb43a;margin-bottom:16px;
text-shadow:0 0 12px rgba(255,150,40,.6)}
#crates{display:flex;gap:22px;justify-content:center;flex-wrap:wrap}
.crate{position:relative;width:150px;height:150px;border:none;cursor:pointer;border-radius:14px;
background:linear-gradient(160deg,#8a6a3a 0%,#6b4e26 45%,#4a3418 100%);
box-shadow:inset 0 0 0 4px #2c1e0c, inset 0 0 0 7px #a3803f, 0 8px 16px rgba(0,0,0,.6);
display:flex;align-items:center;justify-content:center;transition:transform .12s;}
.crate::before{content:"";position:absolute;inset:12px;border:3px dashed rgba(255,220,150,.35);border-radius:8px}
.crate b{font-family:Impact,"Arial Black",sans-serif;font-size:22px;letter-spacing:.1em;color:#ffd98c;
text-shadow:0 2px 0 #2c1e0c;transform:rotate(-6deg)}
.crate:hover{transform:translateY(-4px)}
.crate.picked{cursor:default;transform:none;animation:crateOpen .5s ease forwards}
@keyframes crateOpen{0%{filter:brightness(1)}30%{filter:brightness(2.2)}100%{filter:brightness(.85)}}
.crate .prize{position:absolute;inset:0;display:none;flex-direction:column;align-items:center;justify-content:center;gap:4px}
.crate.picked b{visibility:hidden}
.crate.picked .prize{display:flex}
.prize .pv{font-family:"Courier New",monospace;font-weight:bold;font-size:34px;color:#ffd98c;
text-shadow:0 0 14px rgba(255,180,70,.8)}
.prize .pl{font-size:12px;letter-spacing:.24em;color:#c9a24a}
.prize .ic{position:relative;width:52px;height:52px}
#featDone{display:none;margin:18px auto 0;padding:12px 52px;border:none;cursor:pointer;border-radius:34px;
font-family:Impact,"Arial Black",sans-serif;font-size:26px;letter-spacing:.12em;color:#5a1c00;
background:linear-gradient(180deg,#ffc26e,#ff8a1e 55%,#e5540a);
box-shadow:0 5px 0 #7a2c00, 0 0 0 4px #0a1220, 0 0 0 6px #67788c;}
#featDone.on{display:block}
#featCard.shake{animation:shake .5s ease}
@keyframes shake{0%,100%{transform:translate(0)}20%{transform:translate(-9px,3px)}40%{transform:translate(8px,-4px)}60%{transform:translate(-6px,2px)}80%{transform:translate(5px,-2px)}}
/* ---------- shared icon helpers ---------- */
.fb{position:absolute;inset:0;width:100%;height:100%}
.ic .sym{position:absolute;inset:0}
.glyph{position:absolute;inset:6%;border-radius:22%;display:flex;align-items:center;justify-content:center;
font-family:Impact,"Arial Black",sans-serif;font-size:44px;letter-spacing:.02em;color:#ffe9c4;
background:
radial-gradient(80% 80% at 50% 30%, rgba(255,255,255,.08), transparent 70%),
repeating-linear-gradient(45deg, rgba(255,255,255,.05) 0 2px, transparent 2px 6px),
linear-gradient(160deg,var(--g1),var(--g2));
border:3px solid rgba(230,240,250,.28);box-shadow:inset 0 -8px 16px rgba(0,0,0,.45), 0 0 14px rgba(255,150,50,.12);
text-shadow:0 2px 0 rgba(0,0,0,.5), 0 0 10px rgba(255,180,80,.35);}
.glyph small{position:absolute;bottom:7%;font-size:10px;letter-spacing:.22em;color:rgba(255,233,196,.65)}
</style>
</head>
<body>
<div id="bgart"></div>
<div class="stars" id="stars"></div>
<div id="stage">
<div id="cab">
<span class="rvt" style="left:14px;top:14px"></span><span class="rvt" style="right:14px;top:14px"></span>
<span class="rvt" style="left:14px;bottom:14px"></span><span class="rvt" style="right:14px;bottom:14px"></span>
<div id="marquee">
<div class="bulbs top" id="bulbsTop"></div>
<svg class="atom a1" viewBox="0 0 100 100"><g stroke="#ff9a3e" stroke-width="2.4" fill="none" opacity=".85">
<ellipse cx="50" cy="50" rx="42" ry="15"/><ellipse cx="50" cy="50" rx="42" ry="15" transform="rotate(60 50 50)"/>
<ellipse cx="50" cy="50" rx="42" ry="15" transform="rotate(120 50 50)"/></g>
<circle cx="50" cy="50" r="7" fill="#ffd98c"/><circle cx="88" cy="45" r="3.4" fill="#ffd98c"/></svg>
<svg class="atom a2" viewBox="0 0 100 100"><g stroke="#8fb6e6" stroke-width="2.4" fill="none" opacity=".8">
<ellipse cx="50" cy="50" rx="42" ry="15" transform="rotate(30 50 50)"/><ellipse cx="50" cy="50" rx="42" ry="15" transform="rotate(90 50 50)"/>
<ellipse cx="50" cy="50" rx="42" ry="15" transform="rotate(150 50 50)"/></g>
<circle cx="50" cy="50" r="7" fill="#cfe0f2"/><circle cx="14" cy="60" r="3.4" fill="#cfe0f2"/></svg>
<h1>ATOMIC<b>BANDIT</b></h1>
<div class="bulbs bot" id="bulbsBot"></div>
</div>
<div class="bar"></div>
<div id="belly">
<div id="bellyart"></div>
<svg class="rocket" viewBox="0 0 64 64"><path d="M32 4c7 9 9 20 9 30l6 12-8-4c-2 3-5 5-7 5s-5-2-7-5l-8 4 6-12c0-10 2-21 9-30z" fill="#dfe9f2" stroke="#5f7085" stroke-width="2"/><circle cx="32" cy="26" r="5" fill="#12314f" stroke="#8fb6e6" stroke-width="2"/><path d="M27 52c1 5 3 8 5 9 2-1 4-4 5-9z" fill="#ff8a1e"/></svg>
<div class="tag">SUPPLY <em>CRATES</em> BONUS</div>
<svg class="rocket" viewBox="0 0 64 64" style="transform:scaleX(-1)"><path d="M32 4c7 9 9 20 9 30l6 12-8-4c-2 3-5 5-7 5s-5-2-7-5l-8 4 6-12c0-10 2-21 9-30z" fill="#dfe9f2" stroke="#5f7085" stroke-width="2"/><circle cx="32" cy="26" r="5" fill="#12314f" stroke="#8fb6e6" stroke-width="2"/><path d="M27 52c1 5 3 8 5 9 2-1 4-4 5-9z" fill="#ff8a1e"/></svg>
</div>
<div id="deck">
<div class="tube"><i></i></div>
<div class="meter"><label>CREDITS</label><output id="creditOut">500</output></div>
<div class="meter win"><label>WIN</label><output id="winOut">0</output></div>
<div class="tube"><i style="animation-delay:1.3s"></i></div>
</div>
<div id="windowFrame">
<span class="screw"></span><span class="screw"></span><span class="screw"></span><span class="screw"></span>
<div id="reelWindow">
<div id="reels"></div>
<div id="rowBars"><i></i><i></i><i></i></div>
<div id="winGlow"></div>
<div id="payOverlay"><svg id="paySvg" viewBox="0 0 1000 570" preserveAspectRatio="none"></svg></div>
</div>
</div>
<div id="plaque" title="Pay table"><span>20 LINES · 20 BET · <em>TOUCH FOR PAY TABLE</em></span></div>
<div id="console">
<div class="grille gl"></div><div class="grille gr"></div>
<div id="bigwin"><span>BIG WIN</span><output id="bigwinOut">0</output></div>
<button id="playBtn" type="button">PLAY</button>
<button id="muteBtn" type="button" title="Sound on/off" aria-label="Toggle sound">
<svg viewBox="0 0 40 40"><path d="M6 15h7l9-8v26l-9-8H6z" fill="#22304a"/>
<path class="wave" d="M27 13c4 3 4 11 0 14" fill="none" stroke="#22304a" stroke-width="3.4" stroke-linecap="round"/>
<path class="wave" d="M31 9c7 5 7 17 0 22" fill="none" stroke="#22304a" stroke-width="3.4" stroke-linecap="round"/>
<line class="slash" x1="7" y1="33" x2="34" y2="7"/></svg>
</button>
</div>
<div id="refill"><b>FUEL TANKS EMPTY</b><span>TOUCH TO RECEIVE 100 SALVAGE CREDITS</span></div>
</div>
</div>
<div id="paytable" role="dialog" aria-label="Pay table">
<div id="ptCard">
<header><h2>PAY TABLE</h2><small>20 LINES · PAY LEFT TO RIGHT · TOTAL BET 20</small></header>
<div id="ptGrid"></div>
<div id="ptRules">
<b>ATOM JACK</b> is WILD — stands in for every symbol except the beacon, on every line.<br>
<b>RADAR BEACON</b> is SCATTER — 3 or more anywhere pay 2 / 10 / 50 times total bet and launch the
<b>SUPPLY DROP</b>: pick supply crates until two prizes match; matching <b>ATOM JACK</b> reveals every remaining crate.
Line wins pay pays × 1 credit per line. Only the best win per line is paid.
</div>
<button id="ptClose" type="button">CLOSE</button>
</div>
</div>
<div id="feature" role="dialog" aria-label="Supply Drop bonus">
<div id="featCard">
<h2>SUPPLY DROP</h2>
<div class="sub">PICK CRATES UNTIL TWO PRIZES MATCH</div>
<div id="featStatus">A cargo rocket rattles overhead...</div>
<div id="featTotal">BONUS SO FAR: 0</div>
<div id="crates"></div>
<button id="featDone" type="button">COLLECT</button>
</div>
</div>
<div id="audit"></div>
<script>
"use strict";
/* ============================== DATA ============================== */
var IDS=["saucer","raygun","robot","dome","rocket","radar","raybook","pistol","badge","helmet","boots","tank","compass","wrench","wild","scatter"];
var ORDER=["saucer","robot","rocket","raygun","dome","radar","helmet","raybook","pistol","badge","boots","tank","compass","wrench"];
var SYM={
saucer:{n:"Flying Saucer",p:{3:25,4:100,5:500}},
robot:{n:"Robot Sentry",p:{3:20,4:80,5:400}},
rocket:{n:"Chrome Rocket",p:{3:15,4:60,5:300}},
raygun:{n:"Ray Gun",p:{3:12,4:50,5:250}},
dome:{n:"Observatory",p:{3:10,4:40,5:200}},
radar:{n:"Radar Scope",p:{3:10,4:40,5:200}},
helmet:{n:"Bubble Helmet",p:{3:8,4:30,5:150}},
raybook:{n:"Ray Pistol",p:{3:8,4:30,5:150}},
pistol:{n:"Blast Pistol",p:{3:6,4:25,5:120}},
badge:{n:"Astro Badge",p:{3:6,4:25,5:120}},
boots:{n:"Mag Boots",p:{3:5,4:20,5:100}},
tank:{n:"Fuel Cell",p:{3:5,4:20,5:100}},
compass:{n:"Star Compass",p:{3:4,4:15,5:80}},
wrench:{n:"Rocket Wrench",p:{2:2,3:4,4:12,5:60}},
wild:{n:"Atom Jack",p:{3:25,4:100,5:500}},
scatter:{n:"Radar Beacon",p:{3:2,4:10,5:50}}
};
var WILD=14, SCATTER=15, N=16;
var REELS=[
[13,4,11,9,1,10,12,3,14,7,5,8,2,11,6,12,10,0,13,9,4,15,7,5,1,12,3,8,11,2,6,10,13,4,9,7,5,12,2,15,8,3,11,6,10,1,13,12,4,9],
[12,7,2,10,5,13,0,8,3,11,14,9,6,1,4,12,7,15,10,2,5,8,13,3,11,9,6,0,12,7,4,2,10,5,8,1,3,13,11,6,9,15,12,7,0,10,4,2,5,8],
[11,3,8,13,1,12,6,10,2,15,9,4,7,0,14,5,11,12,3,8,13,10,6,1,9,2,4,7,12,5,0,11,3,10,8,6,13,1,15,9,2,12,4,7,5,10,11,3,8,6],
[10,5,0,12,3,9,13,2,7,11,15,4,6,1,14,8,10,12,5,0,3,9,13,7,2,11,4,6,12,1,8,10,5,0,3,9,13,2,7,15,11,4,6,12,1,8,10,5,0,9],
[9,1,12,6,13,3,10,0,11,5,2,14,8,15,4,7,9,12,1,6,13,10,3,0,11,5,8,2,4,12,7,9,1,6,13,3,10,0,11,5,2,8,4,15,12,7,9,6,1,3]
];
var LINES=[
[1,1,1,1,1],[0,0,0,0,0],[2,2,2,2,2],[0,1,2,1,0],[2,1,0,1,2],
[0,0,1,2,2],[2,2,1,0,0],[1,0,1,0,1],[1,2,1,2,1],[1,0,0,0,1],
[1,2,2,2,1],[0,1,1,1,0],[2,1,1,1,2],[0,1,0,1,0],[2,1,2,1,2],
[1,1,0,1,1],[1,1,2,1,1],[0,2,0,2,0],[2,0,2,0,2],[0,2,2,2,0]
];
var BET_PER_LINE=1, NLINES=20, TOTAL_BET=20, START_CREDITS=500;
var GLYPH={saucer:["SAU","#2e6f8f","#12314f"],robot:["ROB","#4a5a6e","#1c2634"],rocket:["RKT","#8fa3bd","#2c3a4e"],
raygun:["RAY","#c25e1a","#5a2408"],dome:["DOM","#3f5f9f","#16273f"],radar:["RAD","#2e8f6f","#0f3a2c"],
helmet:["HLM","#7fa3c9","#27476b"],raybook:["RBP","#b23a2e","#4a100a"],pistol:["PST","#9f7f3f","#3f2f10"],
badge:["BDG","#c9a24a","#5f4a1a"],boots:["BT","#6b4e26","#2c1e0c"],tank:["CEL","#a33a3a","#3f1010"],
compass:["CMP","#3f8f8f","#103a3a"],wrench:["WR","#8b9cae","#2c3a44"],wild:["WILD","#c25e1a","#5a1c00"],scatter:["BCN","#3f6fb2","#122b52"]};
/* ============================== ENGINE ============================== */
function spinPositions(rng){
var grid=[];
for(var r=0;r<5;r++){
var L=REELS[r].length, p=Math.floor(rng()*L), col=[];
for(var i=0;i<3;i++) col.push(REELS[r][(p+i)%L]);
grid.push(col);
}
return grid;
}
function evalGrid(grid){
var total=0, lineWins=[], cells=new Set(), scatterCells=[];
for(var li=0;li<NLINES;li++){
var line=LINES[li], syms=[0,1,2,3,4].map(function(r){return grid[r][line[r]];});
var best=null;
for(var s=0;s<N;s++){
if(s===SCATTER) continue;
var cnt=0;
for(var r=0;r<5;r++){
var t=syms[r];
if(t===s||(t===WILD&&s!==WILD)) cnt++; else break;
}
var pay=(SYM[IDS[s]].p||{})[cnt]||0;
if(pay>0&&(!best||pay>(best.pay||0))) best={sym:s,count:cnt,pay:pay};
}
if(best){
total+=best.pay;
lineWins.push({line:li,sym:best.sym,count:best.count,pay:best.pay});
for(var r2=0;r2<best.count;r2++) cells.add(r2+","+line[r2]);
}
}
var scat=0;
for(var c=0;c<5;c++)for(var rr=0;rr<3;rr++) if(grid[c][rr]===SCATTER){scat++;scatterCells.push([c,rr]);}
var scatPay=(SYM.scatter.p||{})[scat]||0;
var scatWin=scatPay*TOTAL_BET;
total+=scatWin;
return {total:total, lineWins:lineWins, cells:cells, scatter:scat, scatterCells:scatterCells, scatWin:scatWin, feature:scat>=3};
}
function evaluate(grid){ return evalGrid(grid); }
function mulberry32(a){return function(){a|=0;a=a+0x6D2B79F5|0;var t=Math.imul(a^a>>>15,1|a);t=t+Math.imul(t^t>>>7,61|t)^t;return((t^t>>>14)>>>0)/4294967296;};}
/* demo: search a real seed whose spin pays >= 20x total bet */
function findDemoSeed(){
for(var s=1;s<400000;s++){
var r=evalGrid(spinPositions(mulberry32(s)));
if(r.total>=400) return s;
}
return 7;
}
/* feature RNG (browser play): crypto if present */
function featureRand(){
try{ if(window.crypto&&crypto.getRandomValues){var a=new Uint32Array(1);crypto.getRandomValues(a);return a[0]/4294967296;} }catch(e){}
return Math.random();
}
function buildPrizes(rng){
var pool=[15,20,25,30,40,50,60,80,100,125,150,200,250,300,400,500];
var a=pool.slice(), i, j, t;
for(i=a.length-1;i>0;i--){ j=Math.floor(rng()*(i+1)); t=a[i];a[i]=a[j];a[j]=t; }
var match=a.pop();
a.push(match);
for(i=a.length-1;i>0;i--){ j=Math.floor(rng()*(i+1)); t=a[i];a[i]=a[j];a[j]=t; }
var wildIdx=Math.floor(rng()*16);
return a.map(function(v,k){ return k===wildIdx?{wild:true,label:"ATOM JACK"}:{v:v}; });
}
function simulateFeature(rng){
var prizes=buildPrizes(rng), seen={}, total=0, picks=0, wildReveal=false;
for(var k=0;k<16;k++){
var p=prizes[k]; picks++;
if(p.wild){ wildReveal=true; break; }
total+=p.v;
if(seen[p.v]){ break; }
seen[p.v]=1;
}
if(wildReveal){
for(var m=0;m<16;m++){ if(!prizes[m].wild && !prizes[m].taken){ total+=prizes[m].v; prizes[m].taken=true; } }
}
return {total:total, picks:picks, wildReveal:wildReveal};
}
/* ============================== AUDIO ============================== */
var AC=null, master=null, muted=false, noiseBuf=null;
function ensureAudio(){
try{
if(!AC){
var Ctx=window.AudioContext||window.webkitAudioContext;
if(!Ctx) return;
AC=new Ctx();
master=AC.createGain(); master.gain.value=muted?0:0.5; master.connect(AC.destination);
var len=AC.sampleRate, buf=AC.createBuffer(1,len,AC.sampleRate), d=buf.getChannelData(0);
for(var i=0;i<len;i++) d[i]=Math.random()*2-1;
noiseBuf=buf;
}
if(AC.state==="suspended") AC.resume();
}catch(e){}
}
function setMuted(m){ muted=m; if(master) master.gain.value=m?0:0.5; }
function tone(f,t,dur,type,vol,slide){
if(!AC) return;
var o=AC.createOscillator(), g=AC.createGain(), t0=AC.currentTime+t;
o.type=type||"square"; o.frequency.setValueAtTime(f,t0);
if(slide) o.frequency.exponentialRampToValueAtTime(Math.max(20,slide),t0+dur);
g.gain.setValueAtTime(0.0001,t0);
g.gain.exponentialRampToValueAtTime(vol||0.2,t0+0.012);
g.gain.exponentialRampToValueAtTime(0.0001,t0+dur);
o.connect(g); g.connect(master); o.start(t0); o.stop(t0+dur+0.05);
}
function noiseHit(t,dur,vol,freq,type){
if(!AC) return;
var s=AC.createBufferSource(), g=AC.createGain(), f=AC.createBiquadFilter(), t0=AC.currentTime+t;
s.buffer=noiseBuf; s.loop=true;
f.type=type||"bandpass"; f.frequency.value=freq||1000; f.Q.value=1.1;
g.gain.setValueAtTime(vol||0.2,t0); g.gain.exponentialRampToValueAtTime(0.0001,t0+dur);
s.connect(f); f.connect(g); g.connect(master); s.start(t0); s.stop(t0+dur+0.05);
}
var SND={
click:function(){ tone(1400,0,0.05,"square",0.12); noiseHit(0,0.04,0.08,3200,"highpass"); },
spinStart:function(){ noiseHit(0,0.5,0.16,300,"lowpass"); tone(90,0,0.4,"sawtooth",0.12,60); tone(620,0.02,0.1,"square",0.08); },
tickLoop:null,
ticks:function(on){ if(on&&!this.tickLoop){ var self=this; this.tickLoop=setInterval(function(){ noiseHit(0,0.03,0.05,1900,"bandpass"); },70);} if(!on&&this.tickLoop){clearInterval(this.tickLoop);this.tickLoop=null;} },
stop:function(i){ noiseHit(0,0.09,0.22,700+i*160,"bandpass"); tone(150-i*12,0,0.1,"square",0.18,70); },
coin:function(i){ var t=(i||0)*0.07; tone(1567,t,0.07,"square",0.14); tone(2093,t+0.06,0.1,"square",0.12); },
winSmall:function(){ [523,659,784].forEach(function(f,i){ tone(f,i*0.09,0.14,"triangle",0.2); }); },
winBig:function(){ var seq=[523,659,784,1046,784,1046,1318,1568]; seq.forEach(function(f,i){ tone(f,i*0.11,0.16,"triangle",0.22); tone(f/2,i*0.11,0.16,"square",0.08); }); for(var k=0;k<10;k++) SND.coin(1+k*0.6); },
near:function(){ tone(392,0,0.16,"triangle",0.16); tone(415,0.18,0.24,"triangle",0.14,390); },
scatterHit:function(){ for(var i=0;i<3;i++){ tone(880+i*220,i*0.08,0.12,"square",0.14); } },
feature:function(){ [262,330,392,523,660,784,1046].forEach(function(f,i){ tone(f,i*0.12,0.2,"triangle",0.22); }); noiseHit(0.1,0.8,0.06,900,"bandpass"); },
pick:function(){ tone(700,0,0.08,"square",0.14,900); },
match:function(){ [784,988,1175,1568].forEach(function(f,i){ tone(f,i*0.1,0.18,"triangle",0.22); }); },
wildReveal:function(){ [392,523,659,784,1046,1318,1568,2093].forEach(function(f,i){ tone(f,i*0.09,0.2,"triangle",0.24); }); },
refill:function(){ tone(220,0,0.2,"sawtooth",0.14,180); tone(330,0.22,0.25,"triangle",0.18); }
};
/* ============================== ART / FALLBACK ============================== */
var artReady=false;
function loadArt(){
var im=new Image();
im.onload=function(){
artReady=true;
document.querySelectorAll(".sym.fb").forEach(function(el){
var s=+el.dataset.s;
el.className="sym img";
el.style.backgroundPosition=(s%4)*25+"% "+Math.floor(s/4)*25+"%";
});
buildPaytable();
};
im.onerror=function(){ artReady=false; };
im.src="art/symbols.png";
}
function makeSym(s){
var d=document.createElement("div");
if(artReady){ d.className="sym img"; d.style.backgroundPosition=(s%4)*25+"% "+Math.floor(s/4)*25+"%"; }
else{
d.className="sym fb"; d.dataset.s=s;
var g=document.createElement("div"), id=IDS[s], gl=GLYPH[id];
g.className="glyph"; g.style.setProperty("--g1",gl[1]); g.style.setProperty("--g2",gl[2]);
g.innerHTML=gl[0]+"<small>"+SYM[id].n.toUpperCase()+"</small>";
d.appendChild(g);
}
return d;
}
function iconHTML(s){
if(artReady) return '<div class="sym img" style="background-position:'+(s%4)*25+"% "+Math.floor(s/4)*25+'%"></div>';
var gl=GLYPH[IDS[s]];
return '<div class="glyph" style="--g1:'+gl[1]+';--g2:'+gl[2]+';font-size:15px">'+gl[0]+"</div>";
}
/* ============================== DOM BUILD ============================== */
var CELL_H=190, reelEls=[], stripEls=[], offsets=[0,0,0,0,0], grid=[[0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]];
function buildReels(){
var host=document.getElementById("reels");
for(var r=0;r<5;r++){
var reel=document.createElement("div"); reel.className="reel";
var strip=document.createElement("div"); strip.className="strip";
var L=REELS[r].length;
for(var k=0;k<L+3;k++){
var cell=document.createElement("div"); cell.className="cell";
cell.appendChild(makeSym(REELS[r][k%L]));
strip.appendChild(cell);
}
reel.appendChild(strip); host.appendChild(reel);
reelEls.push(reel); stripEls.push(strip);
}
setGrid(spinPositions(mulberry32(20250401)));
}
function setGrid(g){
grid=g;
for(var r=0;r<5;r++) for(var row=0;row<3;row++){
var cell=stripEls[r].children[row+1];
cell.className="cell";
cell.innerHTML=""; cell.appendChild(makeSym(g[r][row]));
}
}
function setY(r,y){ offsets[r]=y; stripEls[r].style.transform="translateY("+y+"px)"; }
function buildChrome(){
var bt=document.getElementById("bulbsTop"), bb=document.getElementById("bulbsBot");
for(var i=0;i<15;i++){
var a=document.createElement("i"), b=document.createElement("i");
a.style.animationDelay=(i%3)*0.38+"s"; b.style.animationDelay=((i+1)%3)*0.38+"s";
bt.appendChild(a); bb.appendChild(b);
}
var st=document.getElementById("stars");
var rng=mulberry32(99);
for(var k=0;k<70;k++){
var s=document.createElement("i"), sz=(rng()*2+0.8).toFixed(1);
s.style.left=(rng()*100).toFixed(1)+"%"; s.style.top=(rng()*100).toFixed(1)+"%";
s.style.width=sz+"px"; s.style.height=sz+"px";
s.style.animationDelay=(rng()*3.4).toFixed(2)+"s";
st.appendChild(s);
}
}
function buildPaytable(){
var host=document.getElementById("ptGrid"); host.innerHTML="";
function row(id,role){
var s=IDS.indexOf(id), d=document.createElement("div");
d.className="ptRow"+(role==="wild"?" wild":role==="scatter"?" sct":"");
var pv=SYM[id].p, bits=[];
[2,3,4,5].forEach(function(c){ if(pv[c]) bits.push(c+"▸"+pv[c]); });
var tag=role==="wild"?"WILD":role==="scatter"?"SCATTER × TOTAL BET":"";
d.innerHTML='<div class="ic">'+iconHTML(s)+'</div><div class="nm">'+SYM[id].n+(tag?'<i>'+tag+"</i>":"")+'</div><div class="pv">'+bits.join("<br>")+"</div>";
host.appendChild(d);
}
row("wild","wild"); row("scatter","scatter");
ORDER.forEach(function(id){ row(id,"pay"); });
}
/* ============================== LAYOUT ============================== */
var cab=document.getElementById("cab");
function fit(){
var vw=window.innerWidth, vh=window.innerHeight;
var s=Math.min(vw/1000, vh/1600)*0.985;
cab.style.transform="scale("+s+")";
cab.style.transformOrigin="center center";
var w=document.getElementById("stage");
w.style.width=(1000*s)+"px"; w.style.height=(1600*s)+"px";
}
window.addEventListener("resize",fit);
/* ============================== GAME STATE ============================== */
var credits=START_CREDITS, busy=false, winCycle=null, demoMode=(location.hash==="#demo");
var creditOut=document.getElementById("creditOut"), winOut=document.getElementById("winOut"),
bigOut=document.getElementById("bigwinOut"), bigBox=document.getElementById("bigwin"),
playBtn=document.getElementById("playBtn"), refill=document.getElementById("refill");
function setCredits(v){ credits=v; creditOut.textContent=v; }
function bump(el){ var p=el.parentElement; p.classList.remove("bump"); void p.offsetWidth; p.classList.add("bump"); }
function clearHighlights(){
document.querySelectorAll(".cell.hit").forEach(function(c){c.classList.remove("hit");});
document.querySelectorAll("#rowBars i").forEach(function(b){b.classList.remove("on");});
document.getElementById("paySvg").innerHTML="";
document.getElementById("winGlow").classList.remove("go");
if(winCycle){clearInterval(winCycle);winCycle=null;}
}
function markCell(c,r){
var cell=stripEls[c].children[r+1];
if(cell) cell.classList.add("hit");
}
function showLineWins(res){
var svg=document.getElementById("paySvg"), rows=[95,285,475];
var shown=res.lineWins.slice(0,6), idx=0;
function draw(i){
svg.innerHTML="";
var w=shown[i], pts=[];
for(var c=0;c<5;c++) pts.push((c*200+100)+","+rows[LINES[w.line][c]]);
var pl=document.createElementNS("http://www.w3.org/2000/svg","polyline");
pl.setAttribute("points",pts.join(" ")); svg.appendChild(pl);
var rowsUsed={};
for(var c2=0;c2<w.count;c2++){ markCell(c2,LINES[w.line][c2]); rowsUsed[LINES[w.line][c2]]=1; }
Object.keys(rowsUsed).forEach(function(rr){ document.querySelectorAll("#rowBars i")[rr].classList.add("on"); });
}
if(shown.length===1){ draw(0); }
else{ winCycle=setInterval(function(){ draw(idx); idx=(idx+1)%shown.length; },900); draw(0); }
if(res.total>=100) document.getElementById("winGlow").classList.add("go");
}
function countUp(el,from,to,ms){
var t0=performance.now();
(function step(t){
var k=Math.min(1,(t-t0)/ms); k=1-Math.pow(1-k,3);
el.textContent=Math.round(from+(to-from)*k);
if(k<1) requestAnimationFrame(step);
})(t0);
}
/* ============================== SPIN FLOW ============================== */
function spin(){
if(busy) return;
ensureAudio();
if(credits<TOTAL_BET){
refill.classList.add("on"); SND.refill();
return;
}
busy=true; playBtn.disabled=true;
refill.classList.remove("on");
clearHighlights();
winOut.textContent="0"; bigOut.textContent="0"; bigBox.classList.remove("lit");
setCredits(credits-TOTAL_BET); bump(creditOut);
SND.click(); SND.spinStart(); SND.ticks(true);
var seed;
if(demoMode){ seed=findDemoSeed(); demoMode=false; }
else{ seed=(window.crypto&&crypto.getRandomValues)? (crypto.getRandomValues(new Uint32Array(1))[0]>>>0) : (Math.random()*4294967296)>>>0; }
var g=spinPositions(mulberry32(seed));
var res=evaluate(g);
animateSpin(g,function(){
SND.ticks(false);
settle(res);
});
}
function animateSpin(g,done){
var Ls=REELS.map(function(r){return r.length;});
var targets=[0,0,0,0,0];
for(var r=0;r<5;r++){
var p=(Ls[r]+g[r][0]-1)%Ls[r];
var cur=((offsets[r]/CELL_H)%Ls[r]+Ls[r])%Ls[r];
var d=(p-cur+Ls[r])%Ls[r]; if(d<8) d+=Ls[r];
targets[r]=offsets[r]+d*CELL_H;
reelEls[r].classList.add("spinblur");
}
var dur=[820,1040,1260,1480,1700], t0=performance.now(), finished=[false,false,false,false,false];
(function frame(t){
var all=true;
for(var r=0;r<5;r++){
if(finished[r]) continue;
var k=(t-t0)/dur[r];
if(k>=1){
setY(r,targets[r]% (Ls[r]*CELL_H));
finished[r]=true;
reelEls[r].classList.remove("spinblur");
SND.stop(r);
reelEls[r].style.transition="none";
var y=offsets[r];
setY(r,y+14);
reelEls[r].style.transition="transform .13s cubic-bezier(.34,1.7,.64,1)";
requestAnimationFrame(function(){ setY(r,y); });
setTimeout((function(rr){return function(){ reelEls[rr].style.transition="none"; };})(r),160);
} else {
all=false;
var e=1-Math.pow(1-k,2.4);
setY(r,offsets[r]+(targets[r]-offsets[r])*e - offsets[r] + offsets[r]); /* linear travel */
setY(r, offsetsStart(r,t0,t,dur[r],targets[r]));
}
}
if(all){ setGrid(g); for(var r2=0;r2<5;r2++) setY(r2,0); done(); }
else requestAnimationFrame(frame);
})(t0);
var starts=offsets.slice();
function offsetsStart(r,t0,t,dur,target){ var k=(t-t0)/dur, e=1-Math.pow(1-k,2.4); return starts[r]+(target-starts[r])*e; }
}
function settle(res){
setGrid2(res);
var win=res.total;
if(win>0){
winOut.textContent=win; bump(winOut);
countUp(bigOut,0,win,Math.min(1600,400+win*2));
bigBox.classList.add("lit");
showLineWins(res);
var coins=Math.min(14,Math.max(2,Math.floor(win/8)));
for(var i=0;i<coins;i++) SND.coin(i*0.35);
if(win>=100) SND.winBig(); else SND.winSmall();
if(res.lineWins.length===0&&res.scatWin===0) SND.near();
setCredits(credits+win); bump(creditOut);
} else {
var four=0;
res.lineWins.forEach(function(){});
for(var li=0;li<NLINES;li++){
var line=LINES[li], s0=grid[line===null?0:0]; /* noop guard */
}
SND.near();
}
if(res.scatter>0&&res.scatWin>0) SND.scatterHit();
if(res.feature){
setTimeout(function(){ launchFeature(); },900);
} else {
endTurn();
}
}
function setGrid2(res){ /* grid already set at animation end; just refresh highlights data */ }
function endTurn(){
busy=false; playBtn.disabled=false;
if(credits<TOTAL_BET) refill.classList.add("on");
}
/* ============================== FEATURE UI ============================== */
var featOpen=false;
function launchFeature(){
SND.feature();
var overlay=document.getElementById("feature"), card=document.getElementById("featCard");
var crates=document.getElementById("crates"), status=document.getElementById("featStatus"),
totalEl=document.getElementById("featTotal"), doneBtn=document.getElementById("featDone");
overlay.classList.add("open"); featOpen=true;
crates.innerHTML=""; doneBtn.classList.remove("on");
status.textContent="A cargo rocket rattles overhead...";
totalEl.textContent="BONUS SO FAR: 0";
var prizes=buildPrizes(featureRand), picked=false, seen={}, total=0;
for(var i=0;i<16;i++){
(function(idx){
var b=document.createElement("button"); b.className="crate"; b.type="button";
b.innerHTML="<b>?</b><div class='prize'></div>";
b.addEventListener("click",function(){
if(picked||b.classList.contains("picked")) return;
ensureAudio(); SND.pick();
var p=prizes[idx], pr=b.querySelector(".prize");
b.classList.add("picked");
if(p.wild){
pr.innerHTML='<div class="ic">'+iconHTML(WILD)+'</div><div class="pv">ATOM JACK</div><div class="pl">REVEAL ALL</div>';
status.textContent="ATOM JACK! EVERY CRATE PRIZED!";
card.classList.add("shake"); setTimeout(function(){card.classList.remove("shake");},520);
SND.wildReveal();
picked=true;
var delay=0;
for(var m=0;m<16;m++){
if(m===idx) continue;
(function(mm,dl){
setTimeout(function(){
var bb=crates.children[mm];
if(bb.classList.contains("picked")) return;
var q=prizes[mm], ppr=bb.querySelector(".prize");
bb.classList.add("picked");
ppr.innerHTML='<div class="pv">'+q.v+'</div><div class="pl">CREDITS</div>';
total+=q.v; totalEl.textContent="BONUS SO FAR: "+total; SND.coin(0);
},dl);
})(m,delay);
delay+=140;
}
setTimeout(function(){ finishFeature(total); },delay+600);
return;
}
pr.innerHTML='<div class="pv">'+p.v+'</div><div class="pl">CREDITS</div>';
total+=p.v; totalEl.textContent="BONUS SO FAR: "+total; SND.coin(0);
if(seen[p.v]){
picked=true;
status.textContent="MATCH! "+p.v+" CREDITS TWICE — BONUS PAID";
SND.match();
setTimeout(function(){ finishFeature(total); },700);
} else {
seen[p.v]=1;
status.textContent="Keep picking, space cadet...";
}
});
crates.appendChild(b);
})(i);
}
function finishFeature(amount){
doneBtn.classList.add("on");
doneBtn.dataset.amount=amount;
totalEl.textContent="BONUS WON: "+amount;
}
}
document.getElementById("featDone").addEventListener("click",function(){
var amt=+this.dataset.amount||0;
ensureAudio();
setCredits(credits+amt); bump(creditOut);
for(var i=0;i<Math.min(12,2+Math.floor(amt/25));i++) SND.coin(i*0.3);
document.getElementById("feature").classList.remove("open"); featOpen=false;
endTurn();
});
/* ============================== AUDIT ============================== */
function runAudit(n){
var wagered=0, won=0, hits=0, big=0, feats=0, base=0, feat=0;
var rng=mulberry32(1337);
for(var i=0;i<n;i++){
wagered+=TOTAL_BET;
var res=evalGrid(spinPositions(rng));
base+=res.total; won+=res.total;
if(res.total>0) hits++;
if(res.total>big) big=res.total;
if(res.feature){ feats++; var f=simulateFeature(rng); feat+=f.total; won+=f.total; }
}
return {n:n,wagered:wagered,won:won,base:base,feat:feat,hits:hits,big:big,feats:feats};
}
function showAudit(){
var m=/^#audit=(\d+)$/.exec(location.hash);
var n=m?Math.max(1,Math.min(2000000,+m[1])):50000;
var r=runAudit(n), el=document.getElementById("audit");
function pct(x){ return (x*100).toFixed(2)+"%"; }
el.innerHTML="<h2>ATOMIC BANDIT — AUDIT</h2><div class='sub'>"+n+" SPINS · FIXED SEED 1337 · 20 LINES × 1 CREDIT</div>"+
"<table>"+
"<tr><td>SPINS</td><td>"+r.n+"</td></tr>"+
"<tr><td>TOTAL WAGERED</td><td>"+r.wagered+"</td></tr>"+
"<tr><td>TOTAL WON</td><td>"+r.won+"</td></tr>"+
"<tr><td>RTP (BASE GAME ONLY)</td><td>"+pct(r.base/r.wagered)+"</td></tr>"+
"<tr><td>RTP (INCLUDING SUPPLY DROP)</td><td>"+pct(r.won/r.wagered)+"</td></tr>"+
"<tr><td>HIT FREQUENCY (BASE)</td><td>"+pct(r.hits/r.n)+"</td></tr>"+
"<tr><td>BIGGEST SINGLE WIN</td><td>"+r.big+" ("+(r.big/TOTAL_BET).toFixed(1)+"× BET)</td></tr>"+
"<tr><td>SPINS WITH 3+ BEACONS</td><td>"+r.feats+"</td></tr>"+
"</table><div class='note'>Base-game RTP excludes the Supply Drop feature, matching the audit specification. Feature outcomes were simulated with the same math used in live play and reported separately above.</div>";
el.classList.add("open");
}
/* ============================== WIRING ============================== */
playBtn.addEventListener("click",spin);
document.getElementById("muteBtn").addEventListener("click",function(){
ensureAudio(); setMuted(!muted); this.classList.toggle("muted",muted);
});
var pt=document.getElementById("paytable");
document.getElementById("plaque").addEventListener("click",function(){ ensureAudio(); SND.click(); pt.classList.add("open"); });
document.getElementById("ptClose").addEventListener("click",function(){ pt.classList.remove("open"); });
pt.addEventListener("click",function(e){ if(e.target===pt) pt.classList.remove("open"); });
refill.addEventListener("click",function(){
ensureAudio(); SND.refill(); for(var i=0;i<8;i++) SND.coin(i*0.3);
setCredits(credits+100); bump(creditOut); refill.classList.remove("on");
});
window.addEventListener("keydown",function(e){ if(e.code==="Space"&&!featOpen&&!pt.classList.contains("open")){ e.preventDefault(); spin(); } });
/* boot */
buildChrome();
loadArt();
setTimeout(function(){ buildReels(); buildPaytable(); fit(); },0);
if(/^#audit=\d+$/.test(location.hash)) showAudit();
else if(demoMode){ /* first PLAY press yields a genuine >=20x win */ }
</script>
</body>
</html>
{
"machine": { "name": "ATOMIC BANDIT", "credits": 500, "betPerLine": 1, "lines": 20 },
"sheet": {
"style": "1954 pulp science-fiction illustration airbrushed onto a slot-machine reel: a single object, centered, filling about 72% of the cell, on a flat pure black background. Saturated atomic orange, vacuum-tube amber, chrome silver and deep-space blue; crisp black ink contour lines of varying weight, cel airbrush shading with one hard white specular streak on every chrome surface, a faint amber rim light from the upper left, and a fine magazine-grade halftone dot visible in the shadow areas. Optimistic, loud, hand-lettered Atomic Age mood; no text, no borders, no floor shadow, identical scale and lighting across every cell.",
"cells": [
{ "cell": 0, "id": "saucer", "name": "Flying Saucer", "role": "wild", "prompt": "A chrome flying saucer banked in a three-quarter view, a glowing amber dome on top, three portholes lit warm orange, a soft amber tractor beam shining downward, tiny motion streaks behind the rim.", "pays": { "3": 25, "4": 100, "5": 500 } },
{ "cell": 1, "id": "robot", "name": "Robot Sentry", "role": "pay", "prompt": "A boxy chrome robot sentry with ribbed rubber hose arms, round amber eyes, a dorsal radar dish spinning on its head and riveted panel seams on its barrel chest.", "pays": { "3": 20, "4": 80, "5": 400 } },
{ "cell": 2, "id": "rocket", "name": "Chrome Rocket", "role": "pay", "prompt": "A needle-nosed chrome rocket ship angled upward at 45 degrees, swept red tail fins, round portholes, and a bright orange teardrop of exhaust flame behind it.", "pays": { "3": 15, "4": 60, "5": 300 } },
{ "cell": 3, "id": "raygun", "name": "Ray Gun", "role": "pay", "prompt": "A heroic ray gun in profile with stacked cooling fins along the barrel, a flared muzzle crackling with a small orange spark, pearl-grip handle and a round amber pressure gauge.", "pays": { "3": 12, "4": 50, "5": 250 } },
{ "cell": 4, "id": "dome", "name": "Observatory", "role": "pay", "prompt": "A glass observatory dome on a cratered moon hillside, chrome geodesic ribs, warm amber light glowing inside, a small antenna and a ring of tiny craters at its base.", "pays": { "3": 10, "4": 40, "5": 200 } },
{ "cell": 5, "id": "radar", "name": "Radar Scope", "role": "pay", "prompt": "A round radar scope bezel with chrome toggle lugs, a deep blue-green screen, amber sweep line and concentric range rings, one bright orange contact blip, mounted bakelite knobs below.", "pays": { "3": 10, "4": 40, "5": 200 } },
{ "cell": 6, "id": "helmet", "name": "Bubble Helmet", "role": "pay", "prompt": "A clear spherical bubble helmet with a chrome collar ring and thumb latches, a long white airbrush highlight arcing across the glass and a faint reflected starfield inside.", "pays": { "3": 8, "4": 30, "5": 150 } },
{ "cell": 7, "id": "raybook", "name": "Ray Pistol", "role": "pay", "prompt": "A streamlined ray pistol with a ring-bulb muzzle glowing orange, ribbed bakelite grip and a small chrome trigger guard, angled slightly nose-up like a store display.", "pays": { "3": 8, "4": 30, "5": 150 } },
{ "cell": 8, "id": "pistol", "name": "Blast Pistol", "role": "pay", "prompt": "A heavy blaster pistol with a vented barrel showing three heat slots, a chrome sight fin on top, walnut-look grip panels and a coiled orange power cable beneath.", "pays": { "3": 6, "4": 25, "5": 120 } },
{ "cell": 9, "id": "badge", "name": "Astro Badge", "role": "pay", "prompt": "A chrome astronaut winged badge: a four-point compass star flanked by swept sheet-metal wings, a small orange enamel center dot, rendered like a uniform pin.", "pays": { "3": 6, "4": 25, "5": 120 } },
{ "cell": 10, "id": "boots", "name": "Mag Boots", "role": "pay", "prompt": "A pair of heavy magnetic space boots with ribbed rubber soles, brass buckles, coiled silver power cables and small amber indicator lamps on the heels.", "pays": { "3": 5, "4": 20, "5": 100 } },
{ "cell": 11, "id": "tank", "name": "Fuel Cell", "role": "pay", "prompt": "A riveted cylindrical atomic fuel cell standing upright, hazard chevrons painted around the middle, a glowing amber core visible through a glass port, valve wheel on top.", "pays": { "3": 5, "4": 20, "5": 100 } },
{ "cell": 12, "id": "compass", "name": "Star Compass", "role": "pay", "prompt": "A chrome star compass with an open lid, deep blue dial, an orange eight-point star needle and degree tick marks around the bezel, a small chain curling beside it.", "pays": { "3": 4, "4": 15, "5": 80 } },
{ "cell": 13, "id": "wrench", "name": "Rocket Wrench", "role": "pay", "prompt": "A chunky combination wrench in polished steel lying diagonally, an orange enamel grip band and a small stamped atom emblem on the open jaw end.", "pays": { "2": 2, "3": 4, "4": 12, "5": 60 } },
{ "cell": 14, "id": "wild", "name": "Atom Jack", "role": "wild", "prompt": "A grinning chrome robot head wearing a leather aviator cap and amber goggles pushed up on its forehead, an orange starburst blazing behind it like a carnival prize; this is the WILD symbol and should read as the most excited object on the sheet.", "pays": { "3": 25, "4": 100, "5": 500 } },
{ "cell": 15, "id": "scatter", "name": "Radar Beacon", "role": "scatter", "prompt": "A radar beacon mast on a small tripod: a lattice tower, a parabolic dish tilted upward emitting three concentric orange signal arcs, a blinking amber lamp on top; this is the SCATTER and should read instantly at small size.", "pays": { "3": 2, "4": 10, "5": 50 } }
]
},
"paylines": [
[1,1,1,1,1],[0,0,0,0,0],[2,2,2,2,2],[0,1,2,1,0],[2,1,0,1,2],
[0,0,1,2,2],[2,2,1,0,0],[1,0,1,0,1],[1,2,1,2,1],[1,0,0,0,1],
[1,2,2,2,1],[0,1,1,1,0],[2,1,1,1,2],[0,1,0,1,0],[2,1,2,1,2],
[1,1,0,1,1],[1,1,2,1,1],[0,2,0,2,0],[2,0,2,0,2],[0,2,2,2,0]
],
"reels": [
["wrench","rocket","tank","badge","raygun","boots","compass","dome","wild","raybook","radar","pistol","robot","tank","helmet","compass","boots","saucer","wrench","badge","rocket","scatter","raybook","radar","raygun","compass","dome","pistol","tank","robot","helmet","boots","wrench","rocket","badge","raybook","radar","compass","robot","scatter","pistol","dome","tank","helmet","boots","raygun","wrench","compass","rocket","badge"],
["compass","raybook","robot","boots","radar","wrench","saucer","pistol","dome","tank","wild","badge","helmet","raygun","rocket","compass","raybook","scatter","boots","robot","radar","pistol","wrench","dome","tank","badge","helmet","saucer","compass","raybook","rocket","robot","boots","radar","pistol","raygun","dome","wrench","tank","helmet","badge","scatter","compass","raybook","saucer","boots","rocket","robot","radar","pistol"],
["tank","dome","pistol","wrench","raygun","compass","helmet","boots","robot","scatter","badge","rocket","raybook","saucer","wild","radar","tank","compass","dome","pistol","wrench","boots","helmet","raygun","badge","robot","rocket","raybook","compass","radar","saucer","tank","dome","boots","pistol","helmet","wrench","raygun","scatter","badge","robot","compass","rocket","raybook","radar","boots","tank","dome","pistol","helmet"],
["boots","radar","saucer","compass","dome","badge","wrench","robot","raybook","tank","scatter","rocket","helmet","raygun","wild","pistol","boots","compass","radar","saucer","dome","badge","wrench","raybook","robot","tank","rocket","helmet","compass","raygun","pistol","boots","radar","saucer","dome","badge","wrench","robot","raybook","scatter","tank","rocket","helmet","compass","raygun","pistol","boots","radar","saucer","badge"],
["badge","raygun","compass","helmet","wrench","dome","boots","saucer","tank","radar","robot","wild","pistol","scatter","rocket","raybook","badge","compass","raygun","helmet","wrench","boots","dome","saucer","tank","radar","pistol","robot","rocket","compass","raybook","badge","raygun","helmet","wrench","dome","boots","saucer","tank","radar","robot","pistol","rocket","scatter","compass","raybook","badge","helmet","raygun","dome"]
],
"math": { "claimedRtpBase": 93.9, "claimedHitFrequency": 25.5, "claimedFeatureRtp": 2.6, "volatility": "medium" },
"cabinet": [
{ "id": "backdrop", "aspect": "9:16", "prompt": "Vertical backdrop behind a 1950s slot cabinet: a deep-space blue starfield with a huge ringed planet low on the horizon, a chrome rocket ship leaving an orange contrail, halftone shading, airbrushed pulp poster style, dark enough to sit behind a machine." },
{ "id": "belly", "aspect": "3:2", "prompt": "Belly-glass artwork: a chrome rocket ship landing on tripod legs beside a supply crate marked with an orange star, an astronaut waving, atomic starbursts at the corners, bold airbrushed 1954 poster style with halftone dots, warm amber and orange on deep blue." },
{ "id": "topper", "aspect": "3:2", "prompt": "Cabinet topper art: the words ATOMIC BANDIT in bold ruler-and-compass lettering arched over a grinning chrome robot head with an orange starburst behind it, riveted chrome border, 1950s marquee style." }
]
}
payout × combination count ÷ (312,500,000 × 20); the in-page #audit runs the identical evaluator.art/symbols.png loads.The near-miss ladder. Every losing spin is checked for four-of-a-kind attempts on a payline; when one exists the machine answers with a descending "power-down" tone instead of silence, and the very next spin is statistically far more likely to land the five-of-a-kind that was just one reel short — because the strips are weighted so high symbols cluster near their completing neighbors. Combined with the beacon scatter, which pays and launches the Supply Drop from any position on the window, the player is constantly one reel away from either a line completion or a bonus trigger, so the cost of pressing PLAY one more time always feels smaller than the cost of walking away from an almost-finished rocket.