mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
473 lines
14 KiB
Text
473 lines
14 KiB
Text
digraph solitaire {
|
|
graph [
|
|
goal="Build a terminal-based Klondike Solitaire game in Go with TUI",
|
|
rankdir=LR,
|
|
default_max_retries=3,
|
|
retry_target="impl_setup",
|
|
model_stylesheet="
|
|
* { model: gpt-5.2-codex;reasoning_effort: medium; }
|
|
.hard { model: gpt-5.2-codex;reasoning_effort: high; }
|
|
.verify { model: gpt-5.2-codex;reasoning_effort: medium; }
|
|
.review { model: gpt-5.2-codex;reasoning_effort: high; }
|
|
"
|
|
]
|
|
|
|
start [shape=Mdiamond, label="Start"]
|
|
exit [shape=Msquare, label="Exit"]
|
|
|
|
// Project setup
|
|
impl_setup [
|
|
shape=box,
|
|
timeout=600,
|
|
prompt="Read .ai/spec.md. Create Go project structure for solitaire game.
|
|
|
|
Create:
|
|
- go.mod with module name 'solitaire'
|
|
- cmd/solitaire/main.go (stub with hello world)
|
|
- pkg/game/ directory
|
|
- pkg/ui/ directory
|
|
- pkg/storage/ directory
|
|
- README.md with brief description
|
|
|
|
Run: go build ./...
|
|
|
|
Write status.json: outcome=succeeded if project builds, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_setup [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify project setup was completed correctly.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. Check that all required directories exist
|
|
|
|
Write results to .ai/verify_setup.md.
|
|
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
|
|
]
|
|
|
|
check_setup [shape=diamond, label="Setup OK?"]
|
|
|
|
// Core types (cards and deck)
|
|
impl_core_types [
|
|
shape=box,
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md sections on Game Logic and Package Structure.
|
|
|
|
Implement card and deck types in pkg/game/:
|
|
|
|
Create pkg/game/card.go with:
|
|
- Card type (Rank, Suit)
|
|
- Suit constants (Spades, Hearts, Diamonds, Clubs) with Unicode symbols
|
|
- Rank constants (Ace through King)
|
|
- String() method for card display
|
|
- NewDeck() function that creates standard 52-card deck
|
|
- Shuffle() method using crypto/rand for secure shuffling
|
|
|
|
Create pkg/game/card_test.go with tests for:
|
|
- Card string representation
|
|
- Deck creation (52 unique cards)
|
|
- Shuffle produces different order
|
|
|
|
Acceptance:
|
|
- go build ./...
|
|
- go test ./pkg/game/
|
|
|
|
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_core_types [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify core card types implementation.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./pkg/game/ -v
|
|
4. Check test coverage: go test -cover ./pkg/game/
|
|
|
|
Write results to .ai/verify_core_types.md.
|
|
Write status.json: outcome=succeeded if ALL pass and coverage >70%, outcome=failed with details."
|
|
]
|
|
|
|
check_core_types [shape=diamond, label="Types OK?"]
|
|
|
|
// Game logic
|
|
impl_game_logic [
|
|
shape=box,
|
|
class="hard",
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md sections on Game Logic and Core Features.
|
|
Read pkg/game/card.go for Card and Deck types.
|
|
|
|
Implement Klondike Solitaire game logic:
|
|
|
|
Create pkg/game/game.go with:
|
|
- Game struct with:
|
|
- Tableau (7 piles, [][]Card)
|
|
- Foundation (4 piles, [][]Card)
|
|
- Stock pile ([]Card)
|
|
- Waste pile ([]Card)
|
|
- Move history for undo
|
|
- NewGame() initializes properly (7 tableau piles with 1-7 cards, stock gets rest)
|
|
- IsWon() checks if all cards in foundations
|
|
- GetValidMoves() returns legal moves from current state
|
|
|
|
Create pkg/game/move.go with:
|
|
- Move type (source, destination, card count)
|
|
- MoveType constants (TableauToTableau, TableauToFoundation, WasteToTableau, etc.)
|
|
- ValidateMove() checks Klondike rules:
|
|
- Tableau: descending rank, alternating colors
|
|
- Foundation: ascending rank, same suit, starts with Ace
|
|
- Kings only to empty tableau
|
|
- ExecuteMove() applies move and updates game state
|
|
- UndoMove() reverts last move
|
|
|
|
Create pkg/game/game_test.go with tests for:
|
|
- Game initialization (correct card distribution)
|
|
- Move validation (legal and illegal moves)
|
|
- Move execution and undo
|
|
- Win detection
|
|
|
|
Acceptance:
|
|
- go build ./...
|
|
- go test ./pkg/game/ -v (all tests pass)
|
|
|
|
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_game_logic [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify game logic implementation.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./pkg/game/ -v
|
|
4. go test -cover ./pkg/game/ (check for >80% coverage)
|
|
|
|
Write results to .ai/verify_game_logic.md.
|
|
Write status.json: outcome=succeeded if ALL pass with good coverage, outcome=failed with details."
|
|
]
|
|
|
|
check_game_logic [shape=diamond, label="Logic OK?"]
|
|
|
|
// UI Rendering
|
|
impl_ui_render [
|
|
shape=box,
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md sections on User Interface and Input/Output.
|
|
Read pkg/game/game.go and pkg/game/card.go for game state types.
|
|
|
|
Implement TUI rendering using bubbletea framework:
|
|
|
|
First, add dependencies:
|
|
- go get github.com/charmbracelet/bubbletea
|
|
- go get github.com/charmbracelet/lipgloss
|
|
|
|
Create pkg/ui/render.go with:
|
|
- RenderGame() function that takes Game state and returns formatted string
|
|
- Display layout: foundations (top), tableau (middle), stock/waste (bottom)
|
|
- Card display: face-up shows rank+suit, face-down shows \"##\"
|
|
- Highlight selected pile/card
|
|
- Status line showing move count and messages
|
|
|
|
Create pkg/ui/tui.go with:
|
|
- model struct embedding Game and UI state (selected pile, cursor position)
|
|
- Init() bubbletea init function
|
|
- Update() stub (minimal, just quit on 'q')
|
|
- View() calls RenderGame()
|
|
|
|
Create basic test in pkg/ui/render_test.go for RenderGame output format.
|
|
|
|
Acceptance:
|
|
- go build ./...
|
|
- go test ./pkg/ui/
|
|
|
|
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_ui_render [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify UI rendering implementation.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./pkg/ui/ -v
|
|
4. Check that bubbletea dependency is properly added to go.mod
|
|
|
|
Write results to .ai/verify_ui_render.md.
|
|
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
|
|
]
|
|
|
|
check_ui_render [shape=diamond, label="Render OK?"]
|
|
|
|
// UI Input
|
|
impl_ui_input [
|
|
shape=box,
|
|
class="hard",
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md section on User Interface keyboard navigation.
|
|
Read pkg/game/game.go, pkg/game/move.go for game operations.
|
|
Read pkg/ui/tui.go for existing model struct.
|
|
|
|
Implement keyboard input handling:
|
|
|
|
Update pkg/ui/tui.go:
|
|
- Extend model with:
|
|
- selectedPile (which pile is selected)
|
|
- selectedCardIndex (which card in pile)
|
|
- message (for feedback)
|
|
- Update() handles:
|
|
- Arrow keys / hjkl for navigation
|
|
- Space/Enter to select source, then destination (execute move)
|
|
- 'd' to draw from stock
|
|
- 'u' to undo
|
|
- 'n' for new game
|
|
- 'q' to quit
|
|
- Call game.ValidateMove() before executing
|
|
- Show feedback messages for invalid moves
|
|
- Detect and display win condition
|
|
|
|
Create pkg/ui/input.go with helper functions for input processing.
|
|
|
|
Update cmd/solitaire/main.go to:
|
|
- Create new game
|
|
- Initialize bubbletea program with TUI model
|
|
- Run program
|
|
|
|
Acceptance:
|
|
- go build ./cmd/solitaire
|
|
- Binary runs and displays game (manual check)
|
|
- All keyboard controls work (manual check)
|
|
|
|
Write status.json: outcome=succeeded if builds and runs, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_ui_input [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify UI input handling.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./...
|
|
4. go build ./cmd/solitaire
|
|
5. Check that binary exists at ./cmd/solitaire/solitaire
|
|
|
|
Write results to .ai/verify_ui_input.md.
|
|
Write status.json: outcome=succeeded if ALL pass and binary exists, outcome=failed with details."
|
|
]
|
|
|
|
check_ui_input [shape=diamond, label="Input OK?"]
|
|
|
|
// Persistence
|
|
impl_persistence [
|
|
shape=box,
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md section on Game State Management and Input/Output.
|
|
Read pkg/game/game.go for Game struct.
|
|
|
|
Implement save/load functionality:
|
|
|
|
Create pkg/storage/save.go with:
|
|
- SaveGame(game *Game, filename string) error
|
|
- Serialize game state to JSON
|
|
- Write to file
|
|
- LoadGame(filename string) (*Game, error)
|
|
- Read from file
|
|
- Deserialize JSON to Game struct
|
|
- Validate loaded state
|
|
- Make sure Game struct fields are exported for JSON marshaling
|
|
|
|
Update cmd/solitaire/main.go to:
|
|
- Add --load flag
|
|
- Add 's' key to save game
|
|
- Auto-save on quit (to .solitaire-save.json)
|
|
- Load on startup if save exists
|
|
|
|
Create pkg/storage/save_test.go with:
|
|
- Test save/load cycle
|
|
- Test invalid file handling
|
|
|
|
Acceptance:
|
|
- go build ./...
|
|
- go test ./pkg/storage/
|
|
- Manual test: save game, quit, restart with --load
|
|
|
|
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_persistence [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify persistence implementation.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./pkg/storage/ -v
|
|
4. go test ./... (all tests)
|
|
|
|
Write results to .ai/verify_persistence.md.
|
|
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
|
|
]
|
|
|
|
check_persistence [shape=diamond, label="Persist OK?"]
|
|
|
|
// Integration and polish
|
|
impl_integration [
|
|
shape=box,
|
|
timeout=1200,
|
|
max_retries=2,
|
|
prompt="Read .ai/spec.md for full acceptance criteria.
|
|
Read all existing code to understand current state.
|
|
|
|
Polish and integrate the complete solitaire game:
|
|
|
|
Tasks:
|
|
1. Add command-line flags:
|
|
- --draw (1 or 3 cards, default 3)
|
|
- --seed (for reproducible games)
|
|
- --load (load saved game)
|
|
2. Add statistics display on quit (moves, time)
|
|
3. Improve error messages and user feedback
|
|
4. Add help screen ('h' or '?' key)
|
|
5. Ensure all tests pass
|
|
6. Update README.md with build instructions and how to play
|
|
|
|
Create integration test that:
|
|
- Initializes game
|
|
- Executes a sequence of moves
|
|
- Verifies game state
|
|
- Tests save/load cycle
|
|
|
|
Acceptance:
|
|
- go build ./...
|
|
- go test ./...
|
|
- go vet ./...
|
|
- ./cmd/solitaire/solitaire runs successfully
|
|
- All keyboard commands work
|
|
- Game is winnable
|
|
|
|
Write status.json: outcome=succeeded if all criteria pass, outcome=failed with failure_reason otherwise."
|
|
]
|
|
|
|
verify_integration [
|
|
shape=box,
|
|
class="verify",
|
|
timeout=300,
|
|
prompt="Verify integration and completeness.
|
|
|
|
Run:
|
|
1. go build ./...
|
|
2. go vet ./...
|
|
3. go test ./... -v
|
|
4. go test -cover ./...
|
|
5. go build ./cmd/solitaire
|
|
6. Test all command-line flags work
|
|
7. Verify README exists and has instructions
|
|
|
|
Write results to .ai/verify_integration.md.
|
|
Write status.json: outcome=succeeded if ALL pass, outcome=failed with details."
|
|
]
|
|
|
|
check_integration [shape=diamond, label="Integration OK?"]
|
|
|
|
// Final review
|
|
review [
|
|
shape=box,
|
|
class="review",
|
|
timeout=600,
|
|
goal_gate=true,
|
|
prompt="Read .ai/spec.md in full.
|
|
Review the complete solitaire implementation against all requirements.
|
|
|
|
Verify:
|
|
1. Core Gameplay:
|
|
- Game initializes correctly (proper card distribution)
|
|
- All legal moves accepted
|
|
- All illegal moves rejected
|
|
- Win condition works
|
|
2. User Experience:
|
|
- Full keyboard control
|
|
- Clear display
|
|
- Undo works
|
|
- Save/load works
|
|
3. Code Quality:
|
|
- go build ./... succeeds
|
|
- go test ./... succeeds
|
|
- go vet ./... clean
|
|
- Test coverage >80% for pkg/game/
|
|
4. Binary Works:
|
|
- Runs with ./cmd/solitaire/solitaire
|
|
- All features functional
|
|
- Can complete a game
|
|
|
|
Run full test suite and manual gameplay check.
|
|
|
|
Write comprehensive review to .ai/final_review.md.
|
|
Write status.json: outcome=succeeded if COMPLETE per spec, outcome=failed with specific missing items."
|
|
]
|
|
|
|
check_review [shape=diamond, label="Review OK?"]
|
|
|
|
// Wire up the graph
|
|
start -> impl_setup
|
|
impl_setup -> verify_setup
|
|
verify_setup -> check_setup
|
|
check_setup -> impl_core_types [condition="outcome=succeeded"]
|
|
check_setup -> impl_setup [condition="outcome=failed", label="retry"]
|
|
|
|
impl_core_types -> verify_core_types
|
|
verify_core_types -> check_core_types
|
|
check_core_types -> impl_game_logic [condition="outcome=succeeded"]
|
|
check_core_types -> impl_core_types [condition="outcome=failed", label="retry"]
|
|
|
|
impl_game_logic -> verify_game_logic
|
|
verify_game_logic -> check_game_logic
|
|
check_game_logic -> impl_ui_render [condition="outcome=succeeded"]
|
|
check_game_logic -> impl_game_logic [condition="outcome=failed", label="retry"]
|
|
|
|
impl_ui_render -> verify_ui_render
|
|
verify_ui_render -> check_ui_render
|
|
check_ui_render -> impl_ui_input [condition="outcome=succeeded"]
|
|
check_ui_render -> impl_ui_render [condition="outcome=failed", label="retry"]
|
|
|
|
impl_ui_input -> verify_ui_input
|
|
verify_ui_input -> check_ui_input
|
|
check_ui_input -> impl_persistence [condition="outcome=succeeded"]
|
|
check_ui_input -> impl_ui_input [condition="outcome=failed", label="retry"]
|
|
|
|
impl_persistence -> verify_persistence
|
|
verify_persistence -> check_persistence
|
|
check_persistence -> impl_integration [condition="outcome=succeeded"]
|
|
check_persistence -> impl_persistence [condition="outcome=failed", label="retry"]
|
|
|
|
impl_integration -> verify_integration
|
|
verify_integration -> check_integration
|
|
check_integration -> review [condition="outcome=succeeded"]
|
|
check_integration -> impl_integration [condition="outcome=failed", label="retry"]
|
|
|
|
review -> check_review
|
|
check_review -> exit [condition="outcome=succeeded"]
|
|
check_review -> impl_integration [condition="outcome=failed", label="fix"]
|
|
}
|