From 15fffe6b880bf9e2d97111a6d3d4231fd69364dc Mon Sep 17 00:00:00 2001 From: Fabro Date: Thu, 4 Jun 2026 18:17:50 +0000 Subject: [PATCH] fabro(01KT9X0N42W6PRBVH2N5914EQJ): impl_setup (succeeded) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fabro-Run: 01KT9X0N42W6PRBVH2N5914EQJ Fabro-Completed: 3 Fabro-Checkpoint: 318fc6b17d87b5eafdbdbba331f8f16c7529d6aa ⚒️ Generated with [Fabro](https://fabro.sh) --- solitaire-app/README.md | 29 +++++++++++++++++++ solitaire-app/main.py | 28 +++++++++++++----- solitaire-app/pyproject.toml | 26 +++++++++++++++++ solitaire-app/src/solitaire_tui/__init__.py | 1 + solitaire-app/{ => src/solitaire_tui}/game.py | 0 solitaire-app/{ => src/solitaire_tui}/ui.py | 2 +- solitaire-app/tests/__init__.py | 1 + solitaire-app/{ => tests}/test_game.py | 2 +- 8 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 solitaire-app/README.md create mode 100644 solitaire-app/pyproject.toml create mode 100644 solitaire-app/src/solitaire_tui/__init__.py rename solitaire-app/{ => src/solitaire_tui}/game.py (100%) rename solitaire-app/{ => src/solitaire_tui}/ui.py (99%) create mode 100644 solitaire-app/tests/__init__.py rename solitaire-app/{ => tests}/test_game.py (99%) 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):