From abc20f477ae8757f460ec1a5ff85dc69bcabfae2 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 13:56:00 +0800 Subject: [PATCH 1/2] Scaffold the project: pyproject, gitignore, constants, Doxyfile, README, CI Task: MIL-002#1 Task: MIL-002#2 Task: MIL-002#3 Task: MIL-002#4 Task: MIL-002#5 --- .gitea/workflows/ci.yml | 26 ++++++ .gitignore | 37 ++++++++ Doxyfile | 21 +++++ README.md | 117 +++++++++++++++++++++++- docs/artifact-registry.md | 2 +- docs/sqa/reviews/rc-011-mil-002-code.md | 59 ++++++++++++ pyproject.toml | 17 ++++ src/car_manager.py | 7 ++ src/constants.py | 35 +++++++ src/main.py | 27 ++++++ src/player.py | 7 ++ src/scoreboard.py | 7 ++ tests/test_constants.py | 15 +++ 13 files changed, 375 insertions(+), 2 deletions(-) create mode 100644 .gitea/workflows/ci.yml create mode 100644 .gitignore create mode 100644 Doxyfile create mode 100644 docs/sqa/reviews/rc-011-mil-002-code.md create mode 100644 pyproject.toml create mode 100644 src/car_manager.py create mode 100644 src/constants.py create mode 100644 src/main.py create mode 100644 src/player.py create mode 100644 src/scoreboard.py create mode 100644 tests/test_constants.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..e1708d2 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,26 @@ +# Run the tests on every push and pull request. +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install a virtual display and Tk + run: sudo apt-get update && sudo apt-get install -y xvfb python3-tk + - name: Install the development dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --group dev + - name: Run the tests + run: xvfb-run python -m pytest diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e883b10 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Secrets: never commit tokens +.env +.env.* + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +dist/ +*.egg-info/ +.eggs/ +pip-wheel-metadata/ + +# Virtual environments +.venv/ +venv/ +env/ + +# Test, type and lint caches +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.coverage +htmlcov/ + +# Generated documentation +docs/api/ + +# Editors and OS +.idea/ +.vscode/ +*.swp +.DS_Store +Thumbs.db diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..0d41a89 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,21 @@ +# Doxygen configuration for Turtle Crossing. +# Build with: doxygen Doxyfile (output goes to docs/api/) +PROJECT_NAME = "Turtle Crossing" +PROJECT_BRIEF = "Day 23 of 100 Days of Code: a Turtle Crossing game" +OUTPUT_DIRECTORY = docs/api +INPUT = src README.md +FILE_PATTERNS = *.py *.md +RECURSIVE = YES +OPTIMIZE_OUTPUT_JAVA = YES +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +JAVADOC_AUTOBRIEF = YES +AUTOLINK_SUPPORT = YES +MARKDOWN_SUPPORT = YES +USE_MDFILE_AS_MAINPAGE = README.md +GENERATE_HTML = YES +GENERATE_LATEX = NO +HAVE_DOT = NO +QUIET = YES +WARN_IF_UNDOCUMENTED = YES +WARN_AS_ERROR = NO diff --git a/README.md b/README.md index bdd9018..975d554 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,117 @@ -# 023-turtle-crossing +# Turtle Crossing +A Turtle Crossing game, the Day 23 capstone of Udemy's *100 Days of Code: The +Complete Python Pro Bootcamp*. Steer the turtle north across a busy road of +randomly generated cars. Each crossing raises the level and the car speed; one +collision ends the game. Written with the Python standard library only. + +## Requirements + +- Python 3.13 or newer, with `tkinter` (the `turtle` module needs it). +- `git` to clone the repository. +- `pytest` for the tests (development only; the game has no runtime dependencies). +- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation. + +## Set up + +Clone the repository and create a local virtual environment named `.venv` in +the project folder. Upgrade `pip` first, then install the development +dependencies from `pyproject.toml`. + +### Windows powershell + +```powershell +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/023-turtle-crossing.git +cd 023-turtle-crossing +py -3.13 -m venv .venv +.\.venv\Scripts\Activate.ps1 +python -m pip install --upgrade pip +python -m pip install --group dev +``` + +If PowerShell refuses to run the activation script, allow it for this session +with `Set-ExecutionPolicy -Scope Process -ExecutionPolicy RemoteSigned`. + +### Linux debian + +```bash +sudo apt update && sudo apt install -y git python3.13 python3.13-venv python3-tk +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/023-turtle-crossing.git +cd 023-turtle-crossing +python3.13 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install --group dev +``` + +If `python3.13` is not in your distribution, install Python 3.13 with your +preferred tool (for example `pyenv`) and run the same `venv` command with it. + +### MacOS + +```bash +brew install python@3.13 python-tk@3.13 git +git clone https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/023-turtle-crossing.git +cd 023-turtle-crossing +python3.13 -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install --group dev +``` + +## Run + +Activate the `.venv`, then start the game from the project root: + +```bash +python src/main.py +``` + +Press the **Up** arrow key to move the turtle forward. Reaching the top edge +returns the turtle to the start and speeds up the cars. A collision shows +`GAME OVER`; click the window to close it. + +## Run the tests + +With the `.venv` active: + +```bash +python -m pytest +``` + +Tests that need a window are skipped automatically when no display is +available. On a headless Linux machine run `xvfb-run python -m pytest`. + +## Continuous integration + +`.gitea/workflows/ci.yml` runs on every push to `main` and on every pull +request. It sets up Python 3.13, upgrades `pip`, installs the development +dependencies and runs `pytest` under a virtual display. + +## Build the source documentation + +The source uses Doxygen comments and the `Doxyfile` in the project root. + +```bash +doxygen Doxyfile +``` + +Open `docs/api/html/index.html` in a browser. + +## Project layout + +```text +. +├── src/ game source: main.py, constants.py, player.py, car_manager.py, scoreboard.py +├── tests/ pytest tests +├── docs/ planning and review documents (business case, plan, milestones, reviews) +├── Doxyfile Doxygen configuration +├── pyproject.toml project configuration and development dependencies +└── .gitea/ continuous integration workflow +``` + +All constants live in `src/constants.py`. + +## License + +GNU Affero General Public License v3.0, see [LICENSE](LICENSE). diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md index db5c48b..d630794 100644 --- a/docs/artifact-registry.md +++ b/docs/artifact-registry.md @@ -15,7 +15,7 @@ document of a type. `Primary File` may contain a glob (e.g. | SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 | | PP | Project Plan | docs/project-plan.md | 002 | | MIL | Milestone / Gateway | docs/milestones/mil-*.md | 009 | -| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 011 | +| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 012 | | TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 | ## Languages diff --git a/docs/sqa/reviews/rc-011-mil-002-code.md b/docs/sqa/reviews/rc-011-mil-002-code.md new file mode 100644 index 0000000..860a051 --- /dev/null +++ b/docs/sqa/reviews/rc-011-mil-002-code.md @@ -0,0 +1,59 @@ +# Code review for MIL-002 + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-011 | +| CrossReference | [MIL-002], [QC-PY-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-13 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [MIL-002] +- Checklist used: [QC-PY-001] +- Scope: full review of the source and tests changed by MIL-002: constants.py, starter modules, main.py, smoke tests +- Language and domain: n/a (source code is a technical type) +- Language reviewer: none +- Mode: autonomous, not independent (reviewer is `AI as stand-in for S01`; the author is S01) + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) (Mandatory) | Pass | Names checked against PEP 8: snake_case functions, PascalCase classes, UPPER_SNAKE constants. | +| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes (Mandatory) | Pass | Names state purpose; no single-letter names outside comprehension scopes. | +| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions (Mandatory) | Pass | Formatting follows PEP 8 and 88 columns by hand; no linter is configured in this project (project tooling gap noted, no suppressions). | +| 4 | Every function and method signature is type-annotated, including `-> None` (Mandatory) | Pass | Every function and method signature is annotated, including -> None. | +| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) (Mandatory) | N-A | No exception handling in the changed code. | +| 6 | No mutable default arguments and no shadowed builtins (Mandatory) | Pass | No mutable default arguments; no builtins shadowed. | +| 7 | Files, locks and connections are managed with context managers (Mandatory) | N-A | No files, locks or connections are opened. | +| 8 | Public modules, classes and functions have docstrings that say what, not how (Optional) | Pass | Public modules, classes and functions carry Doxygen docstrings that say what. | +| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output (Mandatory) | N-A | No logging; the game writes only to the turtle window. | +| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded (Mandatory) | N-A | The project has no Design Class Diagram: the tasks are plain tasks (no use case), the classes follow the assignment names. | +| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network (Mandatory) | Pass | Tests exist for the new behaviour, are named for it and are order-independent and offline. | +| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment (Optional) | N-A | Optional: no type checker is configured. | +| 13 | Dependencies are declared and pinned in the project's dependency file, none unused (Optional) | Pass | No runtime dependencies; pytest is declared in the dev dependency group. | +| 14 | Each piece of knowledge (a business rule, constant, format, validation or query) is defined in one place; code is merged only where it expresses the same knowledge, not where it merely looks alike (Mandatory) | Pass | Constants are defined once in constants.py and imported. | +| 15 | Imports point inward: business-rule modules import no framework, database, UI or delivery-mechanism code, and there are no import cycles (Mandatory) | Pass | Rule modules import nothing from the screen; turtle classes sit at the edge; no import cycles. | +| 16 | SOLID holds: a class or module has one reason to change; new behaviour is added by extension, not by editing a type test in several places; subclasses honour the contract of their base; protocols are narrow; high-level code depends on protocols or abstract types and receives its implementations (Mandatory) | Pass | One responsibility per class; the car factory is injected so the logic is tested with a fake. | + +## Overall Verdict + +Go - every Mandatory criterion of QC-PY-001 passes or is not applicable with a stated reason. The review is not independent: author and reviewer are the same person, with the AI standing in for S01. + +## Action Items + +| Action | Owner | Due | +| --- | --- | --- | +| Challenge this review in the end-of-run summary if needed | S01 | 2026-10-27 | + +--- + +[MIL-002]: ../../milestones/mil-002-scaffold.md +[QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..40f74a3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "turtle-crossing" +version = "0.1.0" +description = "Day 23 of 100 Days of Code: a Turtle Crossing game built with the standard library turtle module." +readme = "README.md" +requires-python = ">=3.13" +license = { file = "LICENSE" } +authors = [{ name = "Jens Tirsvad Nielsen" }] +# No runtime dependencies: the game uses only the standard library. +dependencies = [] + +[dependency-groups] +dev = ["pytest>=8"] + +[tool.pytest.ini_options] +pythonpath = ["src"] +testpaths = ["tests"] diff --git a/src/car_manager.py b/src/car_manager.py new file mode 100644 index 0000000..698a593 --- /dev/null +++ b/src/car_manager.py @@ -0,0 +1,7 @@ +"""@file car_manager.py +@brief Creates and moves the cars. +""" + + +class CarManager: + """@brief Placeholder, implemented in milestone MIL-004.""" diff --git a/src/constants.py b/src/constants.py new file mode 100644 index 0000000..920fcb4 --- /dev/null +++ b/src/constants.py @@ -0,0 +1,35 @@ +"""@file constants.py +@brief Every constant of the Turtle Crossing game, defined once. +""" + +## Screen size in pixels. +SCREEN_WIDTH = 600 +SCREEN_HEIGHT = 600 +## Seconds to sleep between two frames of the game loop. +LOOP_DELAY = 0.1 + +## Where the player starts, and how far one key press moves it. +STARTING_POSITION = (0, -280) +MOVE_DISTANCE = 10 +## The player has crossed when its y coordinate is above this value. +FINISH_LINE_Y = 280 +## Half the width and height of the player used for collision tests. +PLAYER_HALF_SIZE = 8 + +## Car colours, one registered shape per colour. +COLORS = ["red", "orange", "yellow", "green", "blue", "purple"] +## Distance a car moves per frame on level 1, and the speed-up per level. +STARTING_MOVE_DISTANCE = 5 +MOVE_INCREMENT = 10 +## Car footprint in pixels; the drawn shape fits inside it. +CAR_LENGTH = 40 +CAR_HEIGHT = 20 +## A new car is created when a die with this many sides shows 1. +SPAWN_ODDS = 6 +## No cars in the top and bottom strip of this height. +SAFE_ZONE = 50 + +## Scoreboard text. +FONT = ("Courier", 24, "normal") +SCOREBOARD_POSITION = (-280, 250) +GAME_OVER_TEXT = "GAME OVER" diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..4cab81e --- /dev/null +++ b/src/main.py @@ -0,0 +1,27 @@ +"""@file main.py +@brief Entry point: sets up the screen and runs the game loop. +""" + +import time +from turtle import Screen + +from car_manager import CarManager +from constants import LOOP_DELAY, SCREEN_HEIGHT, SCREEN_WIDTH +from player import Player +from scoreboard import Scoreboard + + +def main() -> None: + """@brief Run the game until the player is hit by a car.""" + screen = Screen() + screen.setup(width=SCREEN_WIDTH, height=SCREEN_HEIGHT) + screen.tracer(0) + + game_is_on = True + while game_is_on: + time.sleep(LOOP_DELAY) + screen.update() + + +if __name__ == "__main__": + main() diff --git a/src/player.py b/src/player.py new file mode 100644 index 0000000..6e32723 --- /dev/null +++ b/src/player.py @@ -0,0 +1,7 @@ +"""@file player.py +@brief The turtle the user controls. +""" + + +class Player: + """@brief Placeholder, implemented in milestone MIL-003.""" diff --git a/src/scoreboard.py b/src/scoreboard.py new file mode 100644 index 0000000..dfaba0d --- /dev/null +++ b/src/scoreboard.py @@ -0,0 +1,7 @@ +"""@file scoreboard.py +@brief Shows the level and the game over message. +""" + + +class Scoreboard: + """@brief Placeholder, implemented in milestone MIL-006.""" diff --git a/tests/test_constants.py b/tests/test_constants.py new file mode 100644 index 0000000..9fd34de --- /dev/null +++ b/tests/test_constants.py @@ -0,0 +1,15 @@ +"""Smoke tests for the constants module.""" + +from constants import CAR_HEIGHT, CAR_LENGTH, COLORS, SAFE_ZONE, SCREEN_HEIGHT + + +def test_car_footprint_matches_assignment() -> None: + assert (CAR_LENGTH, CAR_HEIGHT) == (40, 20) + + +def test_safe_zone_leaves_room_for_cars() -> None: + assert 0 < SAFE_ZONE < SCREEN_HEIGHT / 2 + + +def test_colors_are_not_empty() -> None: + assert COLORS -- 2.54.0 From 8632d605182894c2fd777561f5a7143493f20f9d Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Tue, 13 Oct 2026 13:56:04 +0800 Subject: [PATCH 2/2] Resolve pending commit links Task: MIL-002#4 --- docs/sqa/reviews/rc-011-mil-002-code.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/sqa/reviews/rc-011-mil-002-code.md b/docs/sqa/reviews/rc-011-mil-002-code.md index 860a051..b0c772e 100644 --- a/docs/sqa/reviews/rc-011-mil-002-code.md +++ b/docs/sqa/reviews/rc-011-mil-002-code.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-13 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | pending | +| 2026-10-13 | Accepted | Jens Tirsvad Nielsen | AI as stand-in for S01 | Initial version | [abc20f4] | --- @@ -57,3 +57,4 @@ Go - every Mandatory criterion of QC-PY-001 passes or is not applicable with a s [MIL-002]: ../../milestones/mil-002-scaffold.md [QC-PY-001]: ../../../framework/qc/code/qc-programming-python.md +[abc20f4]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/023-turtle-crossing/commit/abc20f477ae8757f460ec1a5ff85dc69bcabfae2 -- 2.54.0