106 lines
4.9 KiB
Markdown
106 lines
4.9 KiB
Markdown
# Connect Four Pico 2W
|
|
|
|
Embedded Connect Four game with AI, running on a Raspberry Pi Pico 2W with a PicoResTouch-LCD-2.8 touchscreen (2.8" TFT + resistive touch).
|
|
|
|
## Build & Upload
|
|
|
|
```bash
|
|
# Build
|
|
pio run
|
|
|
|
# Upload to board
|
|
pio run --target upload
|
|
|
|
# Monitor serial output
|
|
pio device monitor
|
|
```
|
|
|
|
Requires PlatformIO CLI. The project uses the Arduino framework (earlephilhower core).
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
src/
|
|
config.h — Pin definitions, layout constants, colors, game defaults
|
|
game.h / .cpp — State enum, shared globals, board helpers, AI (minimax), game logic
|
|
touch.h / .cpp — XPT2046 raw reads, touch events, calibration
|
|
storage.h / .cpp — EEPROM persistence (calibration, settings, game log)
|
|
display.h / .cpp — All TFT drawing (board, menu, status, settings UI, game log UI)
|
|
main.cpp — setup(), loop(), state dispatch
|
|
```
|
|
|
|
Other files:
|
|
- `platformio.ini` — Build configuration and tunable parameters
|
|
- `connect_four.js` / `connect_four.html` — Browser edition (standalone, identical AI)
|
|
- `README.md` — Technical and practical documentation
|
|
- `Background information.md` — AI strategy explanation (accessible to all ages)
|
|
|
|
## Key Technical Details
|
|
|
|
- **Hardware**: Raspberry Pi Pico 2W (RP2350), PicoResTouch-LCD-2.8 (ST7789 TFT 320x240, XPT2046 resistive touch)
|
|
- **AI**: Minimax with alpha-beta pruning, three-phase move strategy (instant win/block, blunder, deep search)
|
|
- **Display**: Adafruit_ST7789 + Adafruit_GFX via SPI1, portrait mode (240x320), dark theme matching the JS canvas implementation
|
|
- **Touch**: Raw XPT2046 SPI reads (shared SPI1 bus with display). Calibration on first boot, stored in EEPROM. Hold touch during boot to recalibrate.
|
|
- **Persistence**: Settings and game log stored in EEPROM (flash-backed). Survives reboots.
|
|
- **Build flags**: Tunable game parameters defined as `-D` flags in `platformio.ini`
|
|
- **Dependencies**: Adafruit ST7735/ST7789 Library, Adafruit GFX Library, EEPROM + SPI (built-in)
|
|
|
|
## Module Responsibilities
|
|
|
|
- **config.h**: Constants only. No state, no functions. All pin defs, layout, colors, defaults.
|
|
- **game.h/cpp**: Owns all shared globals (`board`, `gameState`, `tft`, etc.) as extern/definitions. Board helpers, scanBoard, evaluateBoard, minimax, computeAiMove.
|
|
- **touch.h/cpp**: Owns touch calibration globals. readRawTouch, getTouchDown, colFromTouch, menuItemFromTouch, calibrateTouch.
|
|
- **storage.h/cpp**: EEPROM layout defines. save/load for calibration, settings, game log. logGame, clearGameLog.
|
|
- **display.h/cpp**: All drawing. drawCell, drawBoardFull, drawMenu, drawSettings, drawGameLogScreen, animateDrop. Settings/game log touch handlers.
|
|
- **main.cpp**: setup, loop, state handlers (startGame, returnToMenu, startDemo, handleAiTurn, handleDemoStep, handleFlash).
|
|
|
|
## Conventions
|
|
|
|
- **Naming**: camelCase for all variables and functions
|
|
- **Game states**: Use enum names (`MENU`, `PLAYING`, `AI_TURN`, `FINISHED_WIN`, `FINISHED_DRAW`, `DEMO`, `SETTINGS`, `GAME_LOG`), never magic numbers
|
|
- **Player colors**: Use `playerColor(player)` helper, never inline color constants directly
|
|
- **Board reset**: Use `resetBoard()`, never bare `memset(board, 0, sizeof(board))`
|
|
- **Column priority**: Use the shared `colOrder[]` constant, never duplicate `{3, 2, 4, 1, 5, 0, 6}`
|
|
- **Duplicate logic**: The win/draw/log check is handled by `checkGameEnd()` — do not duplicate this pattern
|
|
- **Colors**: RGB565 constants prefixed with `C_`, derived from the JS canvas palette via the `C565(r,g,b)` macro
|
|
|
|
## Display Layout (portrait 240x320)
|
|
|
|
- Cell size: 32px, disc radius: 13px
|
|
- Board origin: (8, 56), board size: 224x192
|
|
- Column numbers at y=44, status text at y=254, sub-status at y=278
|
|
- Board background with rounded corners, grid lines between cells, circular discs in cells
|
|
|
|
## Demo Mode
|
|
|
|
Demo uses asymmetric ply depths (`demoPly[0]` and `demoPly[1]`) randomized at each demo game start via `randomizeDemoPlies()`. This ensures games produce winners rather than draws while still looking strategic.
|
|
|
|
## Game Log
|
|
|
|
- Stored as `GameEntry` structs (type, level, winner, moves), not as formatted strings
|
|
- Persisted to EEPROM after each game via `saveGameLog()` / `loadGameLog()`
|
|
- Viewable on-device via Settings > View Game Log (paginated, newest first)
|
|
- Max entries configurable via `MAX_GAME_LOG` build flag
|
|
- Player wins highlighted in red
|
|
|
|
## Settings Menu
|
|
|
|
Accessible from the main menu (4th item). Allows adjusting:
|
|
- AI Ply depth (1-10)
|
|
- Blunder mode ON/OFF
|
|
- Blunder chance (5-100%, step 5)
|
|
- View Game Log (paginated, with clear option)
|
|
- Recalibrate touch screen
|
|
|
|
Settings are persisted to EEPROM on exit.
|
|
|
|
## EEPROM Layout
|
|
|
|
- Offset 0: Touch calibration magic + data (9 bytes)
|
|
- Offset 9: Settings magic + lookAhead, blunderEnabled, blunderChance, gameLogCount (5+1 bytes)
|
|
- Offset 16+: Game log entries (46 bytes each)
|
|
|
|
## Commit Style
|
|
|
|
Follow the existing pattern: `[type] Description.` where type is `fix`, `update`, `add`, or `refactor`.
|