sugarcraft/honey-flap

Flappy-Bird-style game — port of kbrgl/flapioca on the SugarCraft stack.

Maintainers

Package info

github.com/sugarcraft/honey-flap

Homepage

Documentation

Type:project

pkg:composer/sugarcraft/honey-flap

Transparency log

Statistics

Installs: 0

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

dev-master 2026-07-12 22:06 UTC

This package is auto-updated.

Last update: 2026-07-13 00:25:38 UTC


README

honey-flap

HoneyFlap

CI codecov Packagist Version License PHP

demo

Flappy-Bird-style game on the SugarCraft stack — port of kbrgl/flapioca. The bird's vertical motion is a HoneyBounce projectile (gravity + an upward velocity kick on each tap), pipes scroll left at a fixed cell rate, collision is per-cell.

Run it

composer install
./bin/honey-flap

Keys

Key Action
Space / / w Flap
r Restart
q / Esc Quit

Architecture

File Role
Bird Wraps a HoneyBounce Projectile — gravity pulls it down, flap() resets vertical velocity to a fixed kick, and fall speed is capped at terminal velocity.
Pipe Single-column pipe pair with a centred gap. Slides left one cell per tick.
TickMsg Frame-tick message scheduled by Cmd::tick(0.033, …) ≈ 30 fps.
Game (Model) Pure-state world: bird + pipes + score + crashed flag. Injects PRNG closure for deterministic gap placements in tests.
Renderer Pure view — single playfield walk, ANSI-styled glyphs, rounded border.
PipeGenerator Generates pipes with variable gap height — gap shrinks as score increases, raising difficulty. Gap starts at 6 cells, shrinks by 1 every 5 points, floors at 3.

The PRNG is injected as a Closure(int $maxInclusive): int so unit tests can pin the pipe layout to a specific sequence — the standard SugarCraft pattern.

Difficulty scaling

The pipe gap height adapts to the player's score:

Score range Gap height
0–4 6 cells
5–9 5 cells
10–14 4 cells
15+ 3 cells

Gap shrinks by 1 every 5 points, bottoming out at 3 cells to keep the game playable. This is implemented by PipeGenerator::gapHeightForScore() and applied automatically when Game spawns new pipes via PipeGenerator::makePipe().

Bird physics & tuning

The bird's vertical motion is a HoneyBounce Projectile in the Y-down convention (positive velocity = falling). Three constants on Bird tune the feel for an 18-row playfield at 30 ticks/sec:

Constant Value Role
Bird::FLAP_KICK -10.0 Upward velocity (cells/sec) applied by a flap.
Bird::GRAVITY 18.0 Constant downward acceleration (cells/sec²).
Bird::TICKS_PER_SEC 30 Simulation rate baked into the projectile's deltaTime.

Fall speed is clamped to Projectile::TERMINAL_GRAVITY (53 cells/sec) on every tick, so an uninterrupted drop can never accumulate enough velocity to teleport the bird several rows in a single frame and skip a pipe without a collision.

Crash rules

The game ends on any of three contacts, all handled identically:

  • Floor — the bird's row reaches Game::HEIGHT (row ≥ 18).
  • Top wall — the bird's row goes above the ceiling (row < 0). A hard flap into the top edge crashes just like the floor.
  • Pipe — the bird's cell lands outside a pipe's open gap.

High-score persistence

On a game-over that beats the current best, the new score is merged into a leaderboard and written to scores.json under the config dir ($XDG_CONFIG_HOME or $HOME/.config, in .honey-flap/). The list is bounded to the top Game::MAX_HIGH_SCORES (10) entries. The write runs off the synchronous update() path via a Cmd and swallows I/O errors, so a full or unwritable disk can never crash the render loop. Saved scores are re-seeded on the next Game::start(), and a corrupt/non-array save file is rejected via the shared candy-core Json::decodeArray guard.

Test

composer install
vendor/bin/phpunit

Snapshot tests

Game frame output is pinned via candy-testing's assertGoldenAnsi golden-file snapshots. Any change to the ANSI playfield output must be intentional — re-record the fixture with --update-golden to accept a new canonical render.