bowling
A bowling game engine in Rust. It handles scoring, frame tracking, multiplayer turn rotation, and split detection for ten-pin, candlepin, and duckpin bowling.
The interesting bit is that the game is modelled as a typestate machine. Rolling on a finished game or building a game with no players are compile errors, not runtime ones. Frames work the same way internally: each delivery consumes the current frame state and produces the next one, so invalid transitions can't happen.
Usage
use *;
let alice = new.unwrap;
let bob = new.unwrap;
let mut progress = new
.add_player
.build;
loop
roll_count(n) picks n pins for you. If you need to control which specific pins get knocked down, or record a foul, use roll() with a Roll directly:
// specific pins
let delivery = clean;
let progress = game.roll.unwrap;
// foul: pins physically fell, but score is zero
let foul = foul;
Turn rotation between players is handled automatically. After each player finishes a frame, the next one bowls the same frame, and so on.
Supported rulesets
| Ten-pin | Candlepin | Duckpin | |
|---|---|---|---|
| Balls per frame | 2 | 3 | 3 |
| Deadwood | Cleared | Remains | Cleared |
| 3-ball clearance | n/a | Spare (bonus) | AllDown (no bonus) |
| Split detection | Yes | No | No |
All three use 10 pins, 10 frames, and the usual bonus scoring (strike = next 2 balls, spare = next 1 ball). The Ruleset trait is open, so you can implement your own variant if you need different pin counts, frame counts, deadwood rules, etc.
Pins
Pins are tracked with PinSet, a u16 bitset. Set operations like difference, union, intersection, and complement are all there, and most are const:
let rack = ;
let knocked = of;
let standing = rack - knocked;
assert_eq!;
Scoring
game.scoreboard(i) computes per-frame scoring. Frames waiting on future bonus rolls show as Pending rather than giving you a wrong number. Once enough rolls exist they resolve with base, bonus, and cumulative totals:
Frame | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Base | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 10 | 30 |
Bonus | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 20 | 10 | 0 |
Cum. | 30 | 60 | 90 | 120 | 150 | 180 | 210 | 240 | 270 | 300 |
Total: 300
Split detection
For ten-pin, TEN_PIN_GEOMETRY models the physical pin layout as an adjacency graph. A split is when the head pin is down and the remaining pins form disconnected groups:
let standing = of; // 7-10 split
assert!;
Detection uses BFS over the PinSet bitset, so there's no allocation involved.
Examples
Tests
This runs unit tests, property tests (proptest), and compile-fail tests (trybuild). The compile-fail tests check that rolling on a completed game and building with no players are type errors.
License
Licensed under the GNU General Public License v3.0 or later.
For commercial licensing options (use without GPL obligations), contact the project maintainer.