A pure PHP Blackjack engine built with strict TDD (Test-Driven Development).
- PHP 8.5.4
- PHPUnit 13.2.1 (unit tests)
- PHPStan 2.2.2 (level 6 static analysis)
- PHP CS Fixer 3.95.10
- GitHub Actions (CI/CD)
src/Domain/
├── Card.php # Immutable Value Object (rank, suit, numeric value)
├── Hand.php # Hand logic (flexible Ace, Blackjack, bust, soft/pair detection)
├── Shoe.php # Card deck (1-8 decks, shuffle, draw)
├── Dealer.php # Dealer logic (hits until 17)
├── Decision.php # Enum (Hit, Stand, Double, Split, Surrender)
├── BasicStrategy.php # Complete basic strategy (hard/soft/split/surrender)
└── HiLoCounter.php # Hi-Lo card counting system
Complete implementation based on the standard 4/6/8 deck chart (Dealer Stands on All 17s):
- Hard hands : 8 and under → Hit, 9-11 → Double/Hit, 12-16 → Stand/Hit, 17+ → Stand
- Soft hands : A,2 to A,9 — all correct decisions (Hit/Stand/Double)
- Splits : 2,2 through A,A — all correct decisions
- Surrender : Hard 15/16 against dealer 9/10/Ace
- Low cards (2-6) → +1
- Neutral cards (7-9) → 0
- High cards (10/J/Q/K/A) → -1
git clone https://github.com/florentdevk/blackjack.git
cd blackjack
composer installvendor/bin/phpunit# Static analysis
vendor/bin/phpstan analyse --memory-limit=512M
# Code style check
vendor/bin/php-cs-fixer fix src --dry-runThis project was built with strict TDD — every class was written test-first:
- RED — write a failing test
- GREEN — write minimal code to pass
- REFACTOR — clean up without breaking tests
Cardis a final immutable Value Object — invalid ranks/suits throw exceptions at constructionDecisionis a PHP 8.1+ enum — type-safe, no invalid states possibleBasicStrategyhandles soft hands before hard hands to avoid rule conflictsHand::value()automatically adjusts Ace from 11 to 1 when bust