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
This commit is contained in:
@@ -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
|
||||||
+37
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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).
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ document of a type. `Primary File` may contain a glob (e.g.
|
|||||||
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
|
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
|
||||||
| PP | Project Plan | docs/project-plan.md | 002 |
|
| PP | Project Plan | docs/project-plan.md | 002 |
|
||||||
| MIL | Milestone / Gateway | docs/milestones/mil-*.md | 009 |
|
| 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 |
|
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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"]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""@file car_manager.py
|
||||||
|
@brief Creates and moves the cars.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class CarManager:
|
||||||
|
"""@brief Placeholder, implemented in milestone MIL-004."""
|
||||||
@@ -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"
|
||||||
+27
@@ -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()
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
"""@file player.py
|
||||||
|
@brief The turtle the user controls.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
"""@brief Placeholder, implemented in milestone MIL-003."""
|
||||||
@@ -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."""
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user