A relaxed, incremental implementation of the classic French card game Mille Bornes — built first as a clean, testable engine with a simple terminal interface.
The goal is not to recreate every historical rule immediately, but to produce a playable, extensible core that can later grow into richer versions (including GUI or mobile ports).
- Build one solid engine first
- Keep rules clear and testable
- Prefer simple, readable code over cleverness
- Add features gradually
- Leave space for future ports (Android, web, etc.)
This is a steady stroll, not a sprint.
Mille Bornes (French for “a thousand milestones”) is a racing card game where players attempt to travel exactly 1000 miles before their opponents.
Players:
- Play distance cards to advance
- Use hazards to block opponents
- Use remedies to recover
- Use safeties for permanent protection
- 25, 50, 75, 100, 200 miles
- Add to your total
- Must reach exactly 1000
Played on opponents:
- Stop
- Speed Limit
- Out of Gas
- Flat Tire
- Accident
Fix hazards:
- Go (removes Stop)
- End of Limit
- Gasoline
- Spare Tire
- Repairs
- Right of Way (Stop + Speed Limit immunity)
- Extra Tank (fuel immunity)
- Puncture-Proof (tyres immune)
- Driving Ace (accident immunity)
This project currently implements a clean subset of the rules:
- 2 players (human vs computer)
- Draw 1 card per turn
- Play 1 card or discard
- Must be “moving” to play distance
- Must resolve hazards before moving
- Speed limit restricts distance > 50
- Maximum 2 × 200-mile cards
- Exact 1000 required to win
- No scoring system yet (single-hand play)
Not yet implemented:
- Coup fourré
- Multi-hand scoring
- Teams
millebornes/
├── main.py # Terminal UI entry point
├── mille/
│ ├── cards.py # Card definitions
│ ├── state.py # Game + player state
│ ├── rules.py # Legal moves + rule logic
│ └── game.py # Game flow, deck, turn handling
└── tests/
└── test_rules.py # Unit tests
python main.pyRun tests:
pytestThe project is split into:
- Rules engine (
rules.py) - Game state (
state.py) - Game flow (
game.py) - UI layer (
main.py)
This allows:
- Easy testing
- Future UI replacements
- Clean separation of concerns
Holds:
- players
- deck
- discard pile
- turn tracking
- winner state
Tracks:
- mileage
- hazards
- speed limit
- safeties
- hand
Represents a single action:
- play on self
- attack opponent
- discard
Focus on rule correctness, not UI.
Example tests:
- Cannot play distance while stopped
- Correct remedy clears hazard
- Speed limit enforced
- Exact 1000 rule enforced
- Basic rules
- Turn loop
- Minimal AI
- Terminal UI
- Improve remedy logic flow
- Add coup fourré
- Enforce all official restrictions
- Clarify edge cases
- Prefer winning moves
- Target leading opponent
- Smarter hazard timing
- Strategic safety usage
- Mileage scoring
- Win bonuses
- Safety bonuses
- Multi-hand matches
- Cleaner terminal display
- Optional colour (e.g.
rich) - Better prompts and feedback
- Save/load game state (JSON)
- Replay capability (optional)
- Web interface (Flask / FastAPI)
- Desktop UI (Tkinter / Qt)
- Android version (Kotlin or shared engine)
To keep this enjoyable:
- Do not start multiple UIs at once
- Do not port before finishing the engine
- Add one feature at a time
- Keep commits small and meaningful
- Multiplayer (networked)
- Replay viewer
- Rule variants / house rules
- AI difficulty levels
- Statistics tracking
This project is deliberately paced. The aim is to enjoy the process, explore clean design, and end up with something that works well rather than something that tries to do everything at once.
Think of it as a long, scenic drive to 1000 miles — not a drag race.
Happy coding 🚗💨