daniel156161 f14d780f29
Build and Push Docker Container / build-and-push (push) Successful in 7m53s
feat: add live replays and PostgreSQL maintenance tools
- Stream compact live replay updates across local and clustered dashboards.
- Render responsive snake bodies as SVG paths with aligned custom icons.
- Add cache-busted assets, replay fallback routes, and live-follow playback.
- Support PostgreSQL benchmark sampling and idempotent SQLite migration.
- Add dry-run cleanup for old low-quality PostgreSQL replay payloads.
- Reward safe perimeter lanes and bump Prism to version 1.5.0.
- Add backend, migration, dashboard, and perimeter regression coverage.
2026-08-02 00:50:46 +02:00
2026-04-04 10:23:12 +02:00
2025-05-15 09:56:01 +02:00
2022-01-24 15:30:05 +01:00

Battlesnake Python Starter Project

An official Battlesnake template written in Python. Get started at play.battlesnake.com.

Battlesnake Logo

This project is a great starting point for anyone wanting to program their first Battlesnake in Python. It can be run locally or easily deployed to a cloud provider of your choosing. See the Battlesnake API Docs for more detail.

Technologies Used

This project uses Python 3 and Flask. It also comes with an optional Dockerfile to help with deployment.

Run Your Battlesnake

Install dependencies using pip

pip install -r requirements.txt

Start your Battlesnake

python main.py

You should see the following output once it is running

Running your Battlesnake at http://0.0.0.0:8000
 * Serving Flask app 'My Battlesnake'
 * Debug mode: off

Open localhost:8000 in your browser and you should see

{"apiversion":"1","author":"","color":"#888888","head":"default","tail":"default"}

Play a Game Locally

Install the Battlesnake CLI

Command to run a local game

battlesnake play -W 11 -H 11 --name 'Python Starter Project' --url http://localhost:8000 -g solo --browser

Next Steps

Continue with the Battlesnake Quickstart Guide to customize and improve your Battlesnake's behavior.

Included Competitive Snake

This repo retains snakes/legacy/BestBattleSnake.py, a stronger historical snake that combines:

  • collision and head-to-head risk checks
  • flood-fill space evaluation to avoid traps
  • food routing that gets more aggressive as health drops
  • tail access checks for better long-term survival

Run it explicitly with:

SNAKE=BestBattleSnake python main.py

Optional duel tuning (when only 2 snakes are alive):

BATTLE_SNAKE_DUEL_STYLE=balanced python main.py

Allowed values: safe, balanced, aggressive.

Snake package layout

The snake code is split by responsibility:

  • snakes/strategies/ — actively maintained Apex and Prism entry points
  • snakes/engine/ — reusable bitboards, spatial mixins, duel search, and survival search
  • snakes/core/ — shared base classes
  • snakes/legacy/ — historical snakes retained for compatibility and benchmarks

Snake selection still uses the existing registry names, so deployment values such as SNAKE=PrismBattleSnake_GPT_5_6_Sol remain unchanged.

PrismBattleSnake_GPT_5_6_Sol

PrismBattleSnake_GPT_5_6_Sol is a separate snake that keeps Apex's strategy while accelerating hot spatial operations with a Python-integer bitboard engine. It also shares duel transpositions across candidate moves, uses principal-variation ordering, aspiration windows, path-aware food races, and a deeper tactical horizon, and runs a compact adversarial multiplayer rollout with simultaneous enemy responses and cached occupancy/evaluation states. Its filename, class, and registry key include the model name, while its public Battlesnake API name remains PrismBattleSnake.

Run it with:

SNAKE=PrismBattleSnake_GPT_5_6_Sol python main.py

Benchmark Apex and Prism against sampled positions from a gameplay database:

python scripts/benchmark_snakes_from_db.py \
  --database /path/to/gameplay.sqlite3 \
  --samples 100

The benchmark opens SQLite read-only and reports mean, median, p95, and maximum move latency. Increase --samples for a broader but slower comparison.

Run the deterministic CI-friendly arena benchmark without a gameplay database:

just bench-snake-arena positions=100

It rotates through duel, hazard, multiplayer, constrictor, and cramped-endgame positions. It reports latency, completed duel/rollout depth, searched nodes, cache hits, deadline exits, and move disagreements between Apex and Prism. Use --scenario hazard (repeatable) when invoking the Python script to isolate a scenario. Add output=data/arena-report.json to save a machine-readable report. For representative strategy evaluation, provide recorded positions to scripts/benchmark_snake_arena.py --database /path/to/gameplay.sqlite3.

Run paired seeded games through the official local Battlesnake rules engine:

just bench-snake-tournament games=20 gametype=standard map=standard

Each seed is played twice with Apex and Prism swapping initial engine slots. The report includes wins, draws, win rates, and average game length. Save all per-game results with output=data/tournament-report.json. The tournament starts both snake servers with gameplay persistence disabled, adds the engine identity header required by the API, and shuts them down when finished.

Compact gameplay database

New gameplay turns use normalized storage: the turn row stores food, hazards, move, and thinking data once; snake identity is stored once per game in game_snakes; and changing snake state/body data lives in snake_turns. Replay loading rebuilds the normal Battlesnake board payload.

Create and verify a separate compact copy of an existing SQLite database. By default, replay-heavy rows are retained for games rated medium or high by structural completeness, valid moves, thinking coverage, game length, move diversity, opponent data, and terminal outcome. All game-result rows remain stored, so historical win/loss rates stay persistent when low-quality replay data is removed.

python scripts/migrate_gameplay_database.py \
  --source /path/to/gameplay.sqlite3 \
  --destination /path/to/gameplay.compact.sqlite3 \
  --minimum-quality medium

After reviewing the compact copy, --replace renames the original to a timestamped backup and puts the verified compact database at the original path. Stop all writers before using it:

python scripts/migrate_gameplay_database.py \
  --source /path/to/gameplay.sqlite3 \
  --replace

The migration never modifies the source in place. It verifies row counts and runs SQLite's integrity_check before any replacement.

Record new games while cleanup runs

Point the running server at a temporary delta database while the old database is being compacted. After stopping the writer and flushing the delta database, merge it into the cleaned copy:

python scripts/merge_gameplay_databases.py \
  --base /path/to/gameplay.compact.sqlite3 \
  --delta /path/to/gameplay.delta.sqlite3 \
  --destination /path/to/gameplay.merged.sqlite3 \
  --minimum-quality medium

The merger keeps all game results, quality-rates delta games, regenerates numeric turn IDs, and verifies row counts, foreign keys, and database integrity. Identical game IDs are skipped; conflicting duplicates abort the merge. After reviewing the result, --replace-base backs up and replaces the cleaned base. Stop the delta writer before the final merge and file swap.

Export Training Dataset

Game saves now include a dataset section with labeled move samples.

Export all stored samples to JSONL:

python -m server.DatasetExporter --input data --output data/dataset/good_moves.jsonl

Or with just:

just export-dataset

Curate a high-quality training subset (single file):

python -m server.DatasetCurator --input good_moves-2026-04-03.jsonl --output data/dataset/best_moves.jsonl

Curate from multiple JSONL sources (repeat --input):

python -m server.DatasetCurator \
  --input good_moves-2026-04-03.jsonl \
  --input good_moves-2026-04-04.jsonl \
  --output data/dataset/best_moves.jsonl

Curate from folder or glob:

python -m server.DatasetCurator --input data/dataset --output data/dataset/best_moves.jsonl
python -m server.DatasetCurator --input "good_moves-*.jsonl" --output data/dataset/best_moves.jsonl

Append mode (keeps existing curated rows and deduplicates against them):

python -m server.DatasetCurator --input "good_moves-*.jsonl" --output data/dataset/best_moves.jsonl --append

Archive processed input files after curation:

python -m server.DatasetCurator --input "good_moves-*.jsonl" --output data/dataset/best_moves.jsonl --append --archive-input
python -m server.DatasetCurator --input "good_moves-*.jsonl" --output data/dataset/best_moves.jsonl --append --archive-input --archive-dir data/dataset/archive

Or with just:

just curate-dataset
just curate-dataset append=true
just curate-dataset append=true archive=true archive_dir=data/dataset/archive

Analyze dataset quality overall and by day (best game overall/day included):

python -m server.DatasetStats --input "good_moves-*.jsonl"
python -m server.DatasetStats --input data/dataset --output data/dataset/stats-report.json

The stats report now includes both:

  • best_game (survival/length focused)
  • best_pressure_game (high-pressure quality focused: fewer safe options + strong survival)

Or with just:

just analyze-dataset
just analyze-dataset input=data/dataset output=data/dataset/stats-report.json

To store compact dataset-only records (JSONL) and skip full per-game JSON files:

STORE_DATASET_ONLY=true DATASET_JSONL_PATH=data/dataset/good_moves.jsonl python main.py

Optional compact storage tuning:

  • DATASET_ROTATE_DAILY=true creates one JSONL file per day (default: true)
  • DATASET_JSONL_MAX_MB=50 rotates when file reaches max size in MB (default: 50)
  • DATASET_COMPRESS_ROTATED=true gzip-compresses rotated/old JSONL files (default: true)

Note: To play games on play.battlesnake.com you'll need to deploy your Battlesnake to a live web server OR use a port forwarding tool like ngrok to access your server locally.

S
Description
Languages
Python 88.7%
JavaScript 7.2%
CSS 1.7%
Just 1.1%
HTML 1.1%
Other 0.2%