georgios 996918ff8c Widen centre drain gap; calm the music down
Gameplay (you should now be able to lose balls):
- flipper length 170 -> 154, so the rest-position tips meet with a ~50px
  gap instead of ~18px - balls dropping down the middle now drain freely
- keep the outlane guard posts so side drains stay fair (all drains now
  happen through the widened centre), and verify with autoplay: centre
  drains = 40, side = 0, ~10s average ball life for the bot

Audio (was a harsh machine-gun):
- completely rewrote both music loops: no more square waves or noise
  hats in the music; sparse triangle bass + soft melody with rests, at
  lower volume (play loop now ~47% audible windows, was a solid wall)
- softened the wall-hit thud (frequent during rallies) and the charge
  ticks
2026-09-09 16:51:56 +02:00

🍬 Cosmic Candy Pinball

A cute, 8-bit, pixel-art pinball game, written from scratch in Python with pygame-ce. 100% procedural — there are no image or audio asset files: every sprite, texture, particle, chiptune and sound effect is generated by code at runtime.

Not web-based: it opens its own native window and renders on a low-res 480×360 virtual canvas, upscaled with crisp nearest-neighbour scaling for an authentic retro look.

demo

Running it

Fastest (uv, already used in this project):

uv venv .venv --python 3.12      # once
uv pip install --python .venv/bin/python -r requirements.txt   # once
.venv/bin/python main.py

Or with plain venv + pip (Python 3.10+):

python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/python main.py

Note: the dependency is pygame-ce, not pygame (the community edition supports newer Python versions and is what this was built against).

Controls

Key Action
← → (or Z / /) Flippers
hold Space / ↑ Charge the Candy Cannon — release to fire
M Mute
P Pause
R Restart game
Esc Quit
Enter Start / play again

How to play

  • Light the 3 star lanes up top → CANDY FEVER! (double points for 20s — keep hitting stars to extend it).
  • Bounce off the three kawaii candy bumpers and the two slingshots. Every hit inside 2.5s builds your combo multiplier (up to ×9).
  • Watch the bottom-centre drain and the two outlanes… or lose the ball.
  • Every 50,000 points earns an extra ball.
  • Squirrel says encouraging things. Blush at the cat in the corner.

Project layout

file purpose
main.py entry point / main loop
game.py game states, scoring, juice, all rendering (pixel art)
physics.py pure pinball physics (flippers, bumpers, slingshots, drains) — no rendering
font.py hand-made 5×5 pixel bitmap font
sfx.py procedural chiptune + music synthesis (pure code, no samples)
highscore.json best score (written at game over)

Dev / test tools (run headless, no window needed):

.venv/bin/python autotest.py 40000    # auto-playing bot, prints game-health stats
.venv/bin/python runtest.py 900       # simulated session incl. rendering
.venv/bin/python shots.py shots       # dumps screenshots of every state
.venv/bin/python probe.py             # pixel-probes the rendered layout

Design notes

  • Physics: fixed 60 Hz logic with up to 6 collision substeps so the ball never tunnels; kinematic flippers whose angular velocity is transferred to the ball (real "scoop" feel); inward-tilting floor funnels; centre drain + guarded outlanes; a softlock rescue nudge if a ball ever parks too long.
  • Fun tuning was data-driven: an autotest.py bot plays thousands of frames and reports hit/drain rates per table element, so bumper, slingshot and flipper geometry was tuned against measured behaviour, not guesswork.
  • Audio: square/triangle/noise oscillators -> PCM at startup; distinct pitched bump sounds that climb with your combo, plus two generative music loops (menu + play).

Licence: MIT — go wild.

S
Description
No description provided
Readme
157 KiB
Languages
Python 100%