Coverage monitoring for directed game play-testing (Game-Fuzz).
gamecov brings the idea of code coverage to video games.
Instead of measuring lines or branches executed,
it measures unique visual states observed during game-play.
A fuzzer drives the game, records sessions as MP4 videos,
and gamecov tells you how much of the game's visual space has been explored.
Frame coverage counts the number of perceptually unique frames across gameplay recordings. Two frames are considered duplicates if the Hamming distance between their perceptual hashes (pHash) falls within a configurable threshold.
How it works:
- Load game-play recording (MP4).
- Compute a perceptual hash (pHash) for each frame.
- Deduplicate frames by Hamming distance — frames within
RADIUSbits of an existing hash are discarded. - The remaining set of unique hashes is the coverage for that session.
- A monitor accumulates coverage across multiple sessions, tracking total unique frames seen.
Requires Python >= 3.11 and a Rust toolchain (for building from source).
Install directly from GitHub with uv:
uv add git+https://github.com/SecurityLab-UCD/gamecov.gitOr with pip:
pip install git+https://github.com/SecurityLab-UCD/gamecov.gitThis builds both the Python package and the embedded Rust extension (gamecov._gamecov_core) in a single step.
Clone the repo and sync dependencies:
git clone https://github.com/SecurityLab-UCD/gamecov.git
cd gamecov
uv syncfrom gamecov import FrameCoverage
cov = FrameCoverage("path/to/recording.mp4")
print(f"Unique frames: {len(cov.coverage)}")
print(f"Path ID: {cov.path_id}")from gamecov import FrameCoverage, BKFrameMonitor
monitor = BKFrameMonitor()
for recording in recordings:
cov = FrameCoverage(recording)
if not monitor.is_seen(cov):
monitor.add_cov(cov)
print(f"Total unique frames: {len(monitor.item_seen)}")
print(f"Unique paths: {len(monitor.path_seen)}")RustBKFrameMonitor provides the same interface as BKFrameMonitor,
backed by an embedded Rust extension for significantly higher throughput:
from gamecov import FrameCoverage, RustBKFrameMonitor
monitor = RustBKFrameMonitor() # same API as BKFrameMonitor
for recording in recordings:
cov = FrameCoverage(recording)
if not monitor.is_seen(cov):
monitor.add_cov(cov)
print(f"Coverage components: {monitor.coverage_count}")# Deduplicate frames and stitch a panorama
uv run python src/main.py --input-mp4-path path/to/video.mp4 --confidence-threshold 0.5- Python >= 3.11
- Rust toolchain (stable)
- uv
# Install dependencies (builds the Rust extension automatically)
uv sync
# Install pre-commit hooks
uv run pre-commit install# Run all Python tests in parallel
uv run pytest -n auto
# Run with coverage
uv run pytest -n auto --cov=gamecov
# Run Rust unit and property tests
cargo test# Compare Python vs Rust monitor throughput
uv run pytest benchmarks/ --benchmark-enable
# Side-by-side grouped by backend
uv run pytest benchmarks/ --benchmark-enable --benchmark-group-by=param:backend# Type checking
uv run mypy src/ tests/
# Linting
uv run ruff check src/GitHub Actions runs four checks on every PR: pytest, mypy, ruff, and pylint.
The CI workflow installs the Rust toolchain before building.
The embedded Rust extension (RustBKFrameMonitor) provides significant speedups
over the pure-Python BKFrameMonitor for the core add_cov/is_seen monitor operations.
The advantage grows with workload size as the BK-tree and union-find structures scale.
Benchmark results (mean time per iteration, lower is better):
| Recordings | Python (ms) | Rust (ms) | Speedup |
|---|---|---|---|
| 10 | 4.04 | 2.31 | 1.75x |
| 50 | 42.95 | 15.00 | 2.86x |
| 200 | 424.36 | 111.03 | 3.82x |
| 500 | 2,349.74 | 549.40 | 4.28x |
The Rust backend achieves 1.8x -- 4.3x speedup,
with larger gains at higher workloads where BK-tree traversal and union-find operations dominate.
Each recording contains randomly generated FrameCoverage objects with perceptual hashes.
Reproduce these results with:
uv run pytest benchmarks/ --benchmark-enable --benchmark-group-by=param:backend