Metadata-Version: 2.4
Name: wack-a-wombat
Version: 1.0.0
Summary: A terminal whack-a-mole starring wombats. WASD to move, SPACE to whack.
License-Expression: MIT
Keywords: game,tui,curses,terminal,wombat,whack-a-mole,arcade
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console :: Curses
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Games/Entertainment :: Arcade
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: windows-curses; sys_platform == "win32"
Dynamic: license-file

# Wack-a-Wombat

A terminal whack-a-mole with 100% more wombat. Pure Python 3.9+, no
dependencies (just the standard library's `curses`; on Windows it pulls in
`windows-curses`).

Play it right now, nothing to install:

```
uvx wack-a-wombat
```

or install the `wack-a-wombat` command:

```
pipx install wack-a-wombat      # or: pip install --user wack-a-wombat
```

From a checkout: `python3 -m wack_a_wombat`.

## How to play

Wombats pop out of their burrows. Move the mallet onto one and whack it before it
ducks back down. One round on the clock; score as much as you can.

| Key                         | Action                         |
| --------------------------- | ------------------------------ |
| `W A S D` / arrows / `HJKL` | move the mallet                |
| `SPACE` / `ENTER`           | whack                          |
| `P` / `ESC`                 | pause (resume, end round, menu)|
| `M`                         | toggle the terminal bell       |
| `?`                         | how to play (title screen)     |

### Critters

| Sprite       | What to do                     | Points                          |
| ------------ | ------------------------------ | ------------------------------- |
| Wombat       | whack it                       | 10 x multiplier (+ speed bonus) |
| Golden wombat| whack it, fast - it's brief    | 50 x multiplier (+ speed bonus) |
| Sleeping     | **leave it alone**             | -25 and your streak is gone     |

- **Streak**: every 5 consecutive hits raise your multiplier, up to x4. A whiff, a
  bonked sleeper or an escaped wombat resets it.
- **Speed bonus**: up to +10 for hitting a wombat the instant it appears.
- **Danger**: the final 5 seconds - wombats stay up for less time and come faster.

### Modes

| Mode     | Grid | Round | Feel                                 |
| -------- | ---- | ----- | ------------------------------------ |
| Backyard | 3x3  | 30s   | one wombat at a time, gentle ramp    |
| Outback  | 4x4  | 45s   | up to two at once, more sleepers     |
| Frenzy   | 4x4  | 30s   | up to three at once, very short gaps |

Each mode has its own top-10 leaderboard with name, score, hits, accuracy, best
streak, fastest whack and date.

### Wombat skins

Three hand-drawn wombats, all with the signature big flat nose, small round ears
and loaf-shaped body. Switch with `WOMBAT < >` on the title screen or `--skin`.

| Skin       | Style                                             |
| ---------- | ------------------------------------------------- |
| `classic`  | nose-first: a giant filled nose pad on a flat head |
| `arcade`   | bursting out of a grassy mound, paws on the rim    |
| `lineart`  | 90s ASCII-forum line art                           |

The art came from a panel of ASCII artists each drawing in a different style,
scored by judges on wombat-likeness, how readable each state is mid-game, charm
and the title showpiece; `classic` won and was refined from the judges' notes.

## Command line

```
wack-a-wombat [--mode backyard|outback|frenzy] [--seed N] [--no-color] [--fps N]
              [--sound | --no-sound] [--skin NAME] [--data-dir DIR]
              [--list-modes] [--list-skins] [--reset-scores] [--version]
```

Settings and high scores are JSON files in `~/.config/wack-a-wombat/` (or
`$XDG_CONFIG_HOME/wack-a-wombat/`, or `$WACK_A_WOMBAT_HOME`). Writes are atomic;
a corrupt file is moved aside rather than overwritten.

`--seed N` makes every round reproducible: the first round after launch always
gets the same wombats, as does the second, and so on, however long you idle on
the title screen.

Backyard fits a classic 80x24 terminal (the HUD squeezes to two lines below 28
rows); the 4x4 modes need at least 31 rows, 35 for the full HUD. Anything smaller
gets a resize prompt and the round pauses until there's room.

## Architecture

```
wack_a_wombat/
  cli.py            argparse entry point
  app.py            curses bootstrap, frame loop, scene stack, shared services
  engine/           reusable TUI bits - no game knowledge
    input.py        key -> Action mapping (wasd / arrows / hjkl)
    palette.py      named attrs; 256 -> 8 colours -> mono degradation
    canvas.py       clipped, exception-free drawing over a curses window
    scene.py        Scene + SceneStack with transparent overlays
    tween.py        clamp/lerp/easing, Timer, Blink, RollingNumber
    fx.py           score popups, labels, screen shake, confetti
  game/             pure game logic - no curses, deterministic given a seed
    config.py       Mode definitions (grid, timing, difficulty curve)
    critters.py     CritterKind, Phase, Critter
    director.py     difficulty ramp: up-time, gaps, kind mix, simultaneous cap
    scoring.py      points, multipliers, speed bonus
    stats.py        per-round statistics
    model.py        Round: the state machine, emits events
  art/              Sprite type, wombat skins, block-letter banners, block font
  persistence/      atomic JSON store, per-mode leaderboard, settings
  scenes/           title (+attract demo), game, pause, game over, name entry,
                    high scores, help; board_view draws and colours the grid,
                    ui draws the shadowed banners
tests/              unit tests + headless end-to-end via a fake curses window
tools/preview.py    render every screen at any terminal size, as text or colour HTML
```

The `Round` in `game/model.py` is the heart: feed it `update(dt)`, `move()` and
`whack()`, drain its events, and it never touches the screen. Scenes translate
events into effects. That split is what makes the game fully testable without a
terminal - `tests/test_scenes.py` plays whole rounds through the real scenes into
a fake window.

## Development

```
python3 -m unittest discover -s tests -v      # everything, incl. a ~10s pty smoke test
WOMBAT_SKIP_PTY=1 python3 -m unittest discover -s tests
python3 tools/preview.py 40 100               # eyeball every screen as text
python3 tools/preview.py --html shots/all.html --skin arcade   # in colour
python3 -m wack_a_wombat --seed 1             # deterministic wombats
```
