DartScore is an open-source experiment in detecting darts with cameras and turning board hits into game scores. The repository contains a historical Python 3 single-camera MVP and a new, documented direction for a two-Pi system.
- Available now: the original one-camera Pygame prototype, recorded test videos, a simple-score GameService, a durable camera-free simulator, an HTTP/SSE engine API, a web scoreboard/proxy, and optional boot-starting user services for the two Pis.
- In design: two-camera capture on a Raspberry Pi 5, 301/501 rules, browser kiosk startup, and status LEDs.
- Accuracy: the legacy detector is experimental. Replay candidate counts are a reproducible software baseline, not verified scoring accuracy.
The original MVP supports mounting and calibration states, one player, and a simple accumulating score. Its fixed 1680 × 1050 Pygame interface, hardcoded configuration and older camera dependencies have not been migrated to the new hardware. Do not treat the historical instructions as a current Pi 5 installation guide.
The engine is deployed on a Raspberry Pi 5 with 8 GB RAM and a 512 GB M.2 SSD; two-camera capture is planned. A separate GameService applies game rules, owns game state, and persists accepted events. The engine exposes state, commands and events through a versioned HTTP/JSON API and server-sent events.
A Raspberry Pi 4B with 2 GB RAM serves the web UI and drives the screen. All player interaction happens in that browser; game rules and authoritative scores stay with the Pi 5. Both Pis use PoE and wired Ethernet. A display Pi restart does not reset a game.
For the initial web UI, the agreed controls are Start Game, Pause/Resume, and review of uncertain hits: confirm the proposed score, enter a correct single-dart score as points, or reject a false detection. Throws during Pause do not change game state and are not queued for Resume. Evidence images remain on the Pi 5 until manually deleted in the first version. Sector selection, 301/501 rules and automatic image cleanup are later implementation steps.
Read the design documents:
- Current two-Pi implementation and round diagrams
- Two-Pi architecture and component migration
- Engine/presentation API contract
- First web UI and uncertain-hit review
The architecture documents distinguish running components from planned camera and LED work.
The board-hit simulator exercises the extracted one-player game, uncertain-hit review, Pause/Resume, and camera-health states without GPIO, Pygame, or video:
python3 SW/simulate_game.py Testdata/Simulations/basic_game.jsonl
python3 SW/simulate_game.py Testdata/Simulations/basic_game.jsonl --db runtime/game.sqlite3
python3 -m unittest discover -s tests -vEach JSON Lines action produces a game snapshot and result. The example ends
with 80 points after a corrected hit; a throw during Pause is ignored. The
simulator is a developer tool. With --db, accepted actions, pending hits and
camera health recover after a process restart. Use a fresh database for a new
simulation scenario. See the input contract
and the persistence notes.
For iterative testing across two Pis, use
the Python SSH/SFTP development deploy guide.
It copies only the current engine code to the Pi 5 and presentation code to the
Pi 4B, keeping game data at its existing path, then restarts those processes.
Boot startup is available through optional per-user system services.
Use --install-services once after enabling user lingering on both Pis for
boot startup. For a shareable status/log report, run
python3 deploy/collect_diagnostics.py. See the deploy guide for details.
From the repository root, run python3 SW/serve_engine.py --db runtime/game.sqlite3.
It listens on loopback port 8765. State, health, commands and an SSE stream
are available for the existing simple_score game. Add --dev-input to
simulate board hits through a loopback-only development route. See
the API contract and current scope.
Run python3 SW/serve_presentation.py in another terminal and open
http://127.0.0.1:8080. On the Pi 4B, set --engine-url to the Pi 5's
trusted-LAN address and use --host 0.0.0.0 to serve its screen/browser.
See web UI scope and setup. The page
supports one-player simple score, Start, Pause/Resume, and pending-hit review;
it does not yet display stored evidence images.
For hands-on testing without cameras, use python3 scripts/send_test_throw.py
to submit one random dart (30% uncertain) and see
the manual API command guide.
The headless replay runs on a development computer without a camera, GPIO or Pygame. From the repository root, using Python 3.10 or later:
python3 -m venv .venv
.venv/bin/python -m pip install -r requirements-replay.txt
.venv/bin/python SW/replay_video.py Testdata/Videos/dartscore_20191107_152701.avi --output replay-report.jsonThe JSON report contains frame numbers, timestamps, candidate points and legacy scores. The first bundled video produced 238 frames and 9 candidates in the documented baseline run. Those scores are not ground truth: replay assumes the first frame is an empty board and does not automatically calibrate the video. See the replay notes for the second video, options and limitations.
- Label recorded throws and verify board-hit/scoring behavior.
- Integrate Pi 5 hardware status, two-camera capture and independent calibration.
- Validate 301/501 rules and detection accuracy before treating scores as reliable.
Historical prototype setup instructions are retained in Legacy single-camera setup (2020). The original code layout is described in the software notes. The archived email Docs/Re_ Method to Identify and Score Darts....eml is kept for reference.
Licensed under GNU GPLv3.
