checkpoint

⚒️ Generated with [Fabro](https://fabro.sh)
This commit is contained in:
Fabro 2026-06-04 14:18:54 -04:00
parent 318fc6b17d
commit 094dc47722
5 changed files with 825 additions and 209 deletions

826
run.json

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,173 @@
diff --git a/solitaire-app/README.md b/solitaire-app/README.md
new file mode 100644
index 000000000..e176b81f5
--- /dev/null
+++ b/solitaire-app/README.md
@@ -0,0 +1,29 @@
+# Terminal Klondike Solitaire
+
+A terminal-based Klondike Solitaire game in Python 3.11+ using the standard `curses` library.
+
+## Features
+- Standard Draw-One Klondike Solitaire rules
+- Fully keyboard-driven curses TUI
+- Unlimited Undo functionality
+- Auto-move to foundations
+- Fully testable pure Python core game engine
+
+## Installation & Running
+
+To run the game, use:
+```bash
+python3 main.py
+```
+
+To run smoke tests (non-interactive):
+```bash
+python3 main.py --smoke
+```
+
+## Testing
+To run the automated tests using pytest:
+```bash
+cd solitaire-app
+pytest
+```
diff --git a/solitaire-app/main.py b/solitaire-app/main.py
index 71febf900..9715c3a3c 100644
--- a/solitaire-app/main.py
+++ b/solitaire-app/main.py
@@ -1,15 +1,24 @@
import sys
+import os
import argparse
import unittest
+# Ensure the "src" directory and current directory are on sys.path so imports work seamlessly
+current_dir = os.path.dirname(os.path.abspath(__file__))
+src_dir = os.path.join(current_dir, "src")
+if src_dir not in sys.path:
+ sys.path.insert(0, src_dir)
+if current_dir not in sys.path:
+ sys.path.insert(1, current_dir)
+
def run_smoke_test() -> None:
"""Proves imports work, instantiates core classes, and runs self-tests."""
print("Running Solitaire smoke tests...")
# 1. Test Imports
try:
- from game import Card, GameState
- from ui import SolitaireTUI
+ from solitaire_tui.game import Card, GameState
+ from solitaire_tui.ui import SolitaireTUI
print("✓ Successfully imported core modules (game, ui).")
except Exception as e:
print(f"✗ Failed to import core modules: {e}")
@@ -29,9 +38,14 @@ def run_smoke_test() -> None:
# 3. Run full unit tests to confirm rule-engine validity
print("Running automated unit tests...")
loader = unittest.TestLoader()
- # Discover and run tests in the solitaire-app folder
- from test_game import TestSolitaireGame
- suite = loader.loadTestsFromTestCase(TestSolitaireGame)
+ # Discover and run tests
+ try:
+ from tests.test_game import TestSolitaireGame
+ suite = loader.loadTestsFromTestCase(TestSolitaireGame)
+ except Exception as e:
+ print(f"✗ Failed to import tests: {e}")
+ sys.exit(1)
+
runner = unittest.TextTestRunner(verbosity=1)
result = runner.run(suite)
@@ -58,8 +72,8 @@ def main() -> None:
# Normal execution starts curses-based TUI
import curses
- from game import GameState
- from ui import SolitaireTUI
+ from solitaire_tui.game import GameState
+ from solitaire_tui.ui import SolitaireTUI
game = GameState()
tui = SolitaireTUI(game)
diff --git a/solitaire-app/pyproject.toml b/solitaire-app/pyproject.toml
new file mode 100644
index 000000000..1f5ee0594
--- /dev/null
+++ b/solitaire-app/pyproject.toml
@@ -0,0 +1,26 @@
+[build-system]
+requires = ["setuptools>=61.0.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "solitaire-tui"
+version = "0.1.0"
+description = "A terminal-based Klondike solitaire game in Python with a curses TUI"
+readme = "README.md"
+requires-python = ">=3.11"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+]
+dependencies = []
+
+[tool.pytest.ini_options]
+minversion = "6.0"
+addopts = "-ra -q"
+testpaths = [
+ "tests",
+]
+pythonpath = [
+ "src"
+]
diff --git a/solitaire-app/src/solitaire_tui/__init__.py b/solitaire-app/src/solitaire_tui/__init__.py
new file mode 100644
index 000000000..a1c1ef18a
--- /dev/null
+++ b/solitaire-app/src/solitaire_tui/__init__.py
@@ -0,0 +1 @@
+# solitaire_tui package
diff --git a/solitaire-app/game.py b/solitaire-app/src/solitaire_tui/game.py
similarity index 100%
rename from solitaire-app/game.py
rename to solitaire-app/src/solitaire_tui/game.py
diff --git a/solitaire-app/ui.py b/solitaire-app/src/solitaire_tui/ui.py
similarity index 99%
rename from solitaire-app/ui.py
rename to solitaire-app/src/solitaire_tui/ui.py
index 9950c1459..6705819aa 100644
--- a/solitaire-app/ui.py
+++ b/solitaire-app/src/solitaire_tui/ui.py
@@ -1,5 +1,5 @@
import curses
-from game import GameState, Card
+from .game import GameState, Card
from typing import Optional, Tuple
class SolitaireTUI:
diff --git a/solitaire-app/tests/__init__.py b/solitaire-app/tests/__init__.py
new file mode 100644
index 000000000..65140f2e3
--- /dev/null
+++ b/solitaire-app/tests/__init__.py
@@ -0,0 +1 @@
+# tests package
diff --git a/solitaire-app/test_game.py b/solitaire-app/tests/test_game.py
similarity index 99%
rename from solitaire-app/test_game.py
rename to solitaire-app/tests/test_game.py
index 4b7c2bce1..03ec90162 100644
--- a/solitaire-app/test_game.py
+++ b/solitaire-app/tests/test_game.py
@@ -1,5 +1,5 @@
import unittest
-from game import GameState, Card
+from solitaire_tui.game import GameState, Card
class TestSolitaireGame(unittest.TestCase):
def setUp(self):

View file

@ -0,0 +1,6 @@
{
"outcome": "succeeded",
"notes": "Stage completed: impl_setup",
"failure_reason": null,
"timestamp": "2026-06-04T18:17:47.239773Z"
}

View file

@ -0,0 +1,24 @@
Goal: Build a terminal-based Klondike solitaire game in Python with a curses TUI
## Completed stages
- **expand_spec**: succeeded
- Model: gemini-3.5-flash, 140.8k tokens in / 24.8k out
- Files: /home/daytona/workspace/fabro/.ai/solitaire-spec.md, /home/daytona/workspace/fabro/solitaire-app/game.py, /home/daytona/workspace/fabro/solitaire-app/main.py, /home/daytona/workspace/fabro/solitaire-app/test_game.py, /home/daytona/workspace/fabro/solitaire-app/ui.py, /home/daytona/workspace/fabro/status.json
- **impl_setup**: succeeded
- Model: gemini-3.5-flash, 148.0k tokens in / 18.3k out
- Files: /home/daytona/workspace/fabro/solitaire-app/README.md, /home/daytona/workspace/fabro/solitaire-app/main.py, /home/daytona/workspace/fabro/solitaire-app/pyproject.toml, /home/daytona/workspace/fabro/solitaire-app/src/solitaire_tui/__init__.py, /home/daytona/workspace/fabro/solitaire-app/src/solitaire_tui/game.py, /home/daytona/workspace/fabro/solitaire-app/src/solitaire_tui/ui.py, /home/daytona/workspace/fabro/solitaire-app/tests/__init__.py, /home/daytona/workspace/fabro/solitaire-app/tests/test_game.py, /home/daytona/workspace/fabro/status.json
Verify setup for the solitaire app.
Check:
1. solitaire-app/pyproject.toml exists
2. solitaire-app/main.py exists
3. solitaire-app/src/solitaire_tui exists
4. Python files compile
Run:
cd solitaire-app && python3 -m py_compile main.py src/solitaire_tui/*.py
Write findings to .ai/verify_setup.md.
Write status.json at workspace root: outcome=succeeded if all checks pass, outcome=failed with failure_reason otherwise.

View file

@ -0,0 +1,5 @@
{
"mode": "agent",
"provider": "gemini",
"model": "gemini-3.5-flash"
}