MIL-002 Project scaffold #29
@@ -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 |
|
||||
| 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
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# 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 | [abc20f4] |
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
[abc20f4]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/023-turtle-crossing/commit/abc20f477ae8757f460ec1a5ff85dc69bcabfae2
|
||||
@@ -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