24 Commits
Author SHA1 Message Date
Tirsvad 8a5aa3a9b6 Merge pull request 'MIL-004 Documentation and release: README, checks and reviews' (#27) from mil-004-docs-release into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 5s
Reviewed-on: #27
2026-10-04 18:04:42 +02:00
TirsvadandClaude Sonnet 5.5 5ebf05c735 Resolve pending commit links for MIL-004 acceptance
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 00:00:07 +08:00
Tirsvad 267e34fc9f Accept MIL-004 and review the source code
Review MIL-004 (RC-011, Go) and the Python source against QC-PY-001
(RC-012, Go) and accept MIL-004. Update the Traceability Matrix.

Task: MIL-004#3
Closes #19
2026-10-04 23:59:32 +08:00
Tirsvad 4abf984382 Fix lint, format and typing findings and pin dev tools
Order imports, format with ruff, make the account key constants Final
so mypy --strict accepts them, and type the test helpers. Configure
ruff and mypy in pyproject.toml and pin the dev tool versions. All
modules, classes and functions already carry Doxygen comments;
doxygen Doxyfile builds without warnings.

Task: MIL-004#2
Closes #18
2026-10-04 23:59:15 +08:00
Tirsvad 6e52a242fa Add README with set-up, run, test and Doxygen instructions
Describe the game, the .venv set-up with pip upgrade, running the game
and tests, the code checks, the Doxygen build and the layout. Verified
by following it in a clean copy of the tree.

Task: MIL-004#1
Task: MIL-004#4
Closes #17
Closes #20
2026-10-04 23:58:22 +08:00
Tirsvad cbdcba0a9f Merge pull request 'MIL-003 Console game: game loop, entry point and accepted use case' (#26) from mil-003-game-loop into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 3s
Reviewed-on: #26
2026-10-04 17:50:34 +02:00
TirsvadandClaude Sonnet 5.5 5e4dd48e3e Resolve pending commit links for accepted use case artifacts
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:49:52 +08:00
Tirsvad 0ada1588ee Add the console game loop and entry point
Add show_round, ask_choice and play_game with injectable input and
output, and make python -m higher_lower play a game. Each account is
shown once per game; a wrong guess or running out of accounts ends it.
Invalid input is re-asked. Ctrl+C and Ctrl+D leave with a goodbye.
Cover the loop with scripted tests (36 passing).

Task: MIL-003#1
Task: MIL-003#2
Task: MIL-003#3
Task: MIL-003#4
Task: MIL-003#5
Closes #12
Closes #13
Closes #14
Closes #15
Closes #16
2026-10-04 23:49:09 +08:00
Tirsvad c7164feab1 Accept UCD-001, US-001, UC-001 and SSD-001 after review
Review the use case chain for Play a Game (RC-007 to RC-010, all Go)
and accept the four documents. Update the Traceability Matrix.
2026-10-04 23:48:42 +08:00
Tirsvad 462deba2a0 Merge pull request 'Accept MIL-003 and add the Play a Game use case' (#25) from mil-003-console-game into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: #25
2026-10-04 17:43:31 +02:00
TirsvadandClaude Sonnet 5.5 0a74e22a43 Resolve pending commit links for MIL-003 acceptance
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:42:10 +08:00
Tirsvad 923dde7dc9 Accept MIL-003 and add the Play a Game use case
Create the use case chain for the game loop task: Use Case Diagram
UCD-001, User Stories US-001 (two stories), Use Case UC-001 and System
Sequence Diagram SSD-001, all Proposed. Link them from MIL-003.

Review MIL-003 (RC-006, Go) and accept it. Add the new artifacts to the
Traceability Matrix and register UCD, US, UC and SSD in the artifact
registry.

Refs #13
2026-10-04 23:41:35 +08:00
Tirsvad 64e3f04c6a Merge pull request 'MIL-002 Game logic: data, art, constants and pure functions' (#24) from mil-002-game-logic into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s
Reviewed-on: #24
2026-10-04 17:36:11 +02:00
TirsvadandClaude Sonnet 5.5 037900df94 Resolve pending commit links for MIL-002 acceptance
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:35:18 +08:00
Tirsvad ee58fcdc1c Add game data, art, constants and game logic
Add the assignment data set (50 accounts), the logo and vs art, the
constants module, and the pure functions get_random_account,
pick_pair, format_data and check_answer, all with Doxygen comments.
pick_pair never returns the same account twice; check_answer accepts
either choice on a tie. Cover them with pytest tests (22 passing).

The art is built from concatenated raw lines so the backslashes survive
and Doxygen does not read the backticks as markup.

Accept MIL-002 after review RC-005 and add it to the Traceability
Matrix.

Task: MIL-002#1
Task: MIL-002#2
Task: MIL-002#3
Task: MIL-002#4
Task: MIL-002#5
Task: MIL-002#6
Closes #6
Closes #7
Closes #8
Closes #9
Closes #10
Closes #11
2026-10-04 23:34:48 +08:00
Tirsvad b1f4116bc1 Merge pull request 'MIL-001 Project setup: pyproject, skeleton and Doxyfile' (#23) from mil-001-project-setup into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 7s
Reviewed-on: #23
2026-10-04 17:30:02 +02:00
Tirsvad 115e543ee3 Add project configuration, skeleton and Doxyfile
Add pyproject.toml (Python >=3.13, no runtime dependencies, pytest as a
dev extra), the src/higher_lower package with an empty constants module
and an entry-point stub, a smoke test, and the Doxyfile for the Doxygen
comments in the source. Ignore the Doxygen output in .gitignore.

Verified in a fresh .venv: pip install -e ".[dev]", pytest (2 passed),
python -m higher_lower and doxygen Doxyfile without warnings.

Task: MIL-001#1
Task: MIL-001#2
Task: MIL-001#3
Task: MIL-001#4
Closes #1
Closes #2
Closes #3
Closes #4
2026-10-04 23:29:03 +08:00
Tirsvad 4b1228fec0 Merge pull request 'Add inception baseline and milestone plan for Higher Lower' (#22) from g1-inception-baseline into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 5s
Reviewed-on: #22
2026-10-04 17:24:33 +02:00
Tirsvad 2782f29c66 Merge branch 'main' into g1-inception-baseline 2026-10-04 17:24:20 +02:00
TirsvadandClaude Sonnet 5.5 529b964481 Resolve pending commit links for accepted documents
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:23:54 +08:00
Tirsvad a1ff73580f Accept BC, SA, PP and MIL-001 after review
Review BC-001, SA-001, PP-001 and MIL-001 and record the results as
RC-001 to RC-004. All four verdicts are Go, so their Version History
rows become Accepted and the previous rows Deprecated. MIL-002 to
MIL-004 stay Proposed.

BC-001 failed criterion 1 (cost-benefit not justified as qualitative).
Add the justification and a target-completion constraint (2026-10-14)
that PP-001 and MIL-001 are checked against.

Add the Traceability Matrix (TM-001) and register RC and TM in the
artifact registry. PP has no QC checklist, so RC-003 checks the plan
against BC-001 and the milestones instead. Author and reviewer are
both S01, a deviation noted in each record.

Refs #5
2026-10-04 23:23:12 +08:00
Tirsvad a999fcda34 Merge pull request 'G1 inception baseline' (#21) from g1-inception-baseline into main
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 5s
Reviewed-on: #21
2026-10-04 17:15:12 +02:00
TirsvadandClaude Sonnet 5.5 cb28865787 Resolve pending commit links in Version History
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-04 23:11:32 +08:00
Tirsvad ddfe96ffa4 Add inception baseline and milestone plan for Higher Lower
Add the Business Case, Stakeholder Analysis and Project Plan, and four
milestones (MIL-001 Project setup, MIL-002 Game logic, MIL-003 Console
game, MIL-004 Documentation and release) with Go/No-Go criteria and
task tables.

Set the PO language to en in the artifact registry. Link the synced
Gitea Milestones (36-39) in the Project Plan. The 20 task rows were
synced as issues #1-#20.

Refs #5
2026-10-04 23:09:31 +08:00
42 changed files with 5600 additions and 1 deletions
+3
View File
@@ -174,3 +174,6 @@ cython_debug/
# PyPI configuration file
.pypirc
# Doxygen output
docs/doxygen/
+3
View File
@@ -0,0 +1,3 @@
[submodule "framework"]
path = framework
url = ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git
+2967
View File
File diff suppressed because it is too large Load Diff
+99 -1
View File
@@ -1,2 +1,100 @@
# 014-higher_lower
# Higher Lower
A console game from Udemy's *100 Days of Code: The Complete Python Pro Bootcamp*
(day 14). Two Instagram accounts are shown. Guess which one has more followers.
Every right guess adds a point and the winner stays for the next round. A wrong
guess ends the game.
```
Compare A: Cristiano Ronaldo, a Footballer, from Portugal.
_ __
| | / /____
| | / / ___/
| |/ (__ )
|___/____(_)
Against B: Ariana Grande, a Musician and actress, from United States.
Who has more followers? Type 'A' or 'B':
```
- Python 3.13 or newer, no runtime dependencies.
- The function names follow the assignment: `format_data`, `check_answer`.
- Each account is shown once per game; if you get through all 50, you win.
- Follower counts are in millions and come from the assignment's data set.
## Requirements
- [Python](https://www.python.org/downloads/) 3.13 or newer
- Optional: [Doxygen](https://www.doxygen.nl/) to build the source documentation
## Set up
Create a local virtual environment in `.venv`, activate it, and upgrade `pip`.
Windows (PowerShell):
```powershell
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
```
Windows (Git Bash), Linux and macOS:
```bash
python -m venv .venv
source .venv/Scripts/activate # Linux/macOS: source .venv/bin/activate
python -m pip install --upgrade pip
```
The game itself needs nothing more. To run the tests and the code checks, install the
development tools:
```bash
python -m pip install -e ".[dev]"
```
## Run the game
```bash
python -m higher_lower
```
Type `A` or `B` and press Enter. Press Ctrl+C to quit.
## Run the tests
```bash
python -m pytest
```
Check the code style and types:
```bash
python -m ruff check src tests
python -m ruff format --check src tests
python -m mypy src tests
```
## Build the source documentation
The source uses Doxygen comments. The HTML output goes to `docs/doxygen/html`.
```bash
doxygen Doxyfile
```
## Project layout
| Path | Content |
| --- | --- |
| `src/higher_lower/` | The game: `game_logic.py` (pure functions), `game.py` (the loop), `game_data.py`, `art.py`, `constants.py` |
| `tests/` | pytest tests |
| `docs/` | Planning and review documents (business case, plan, milestones, use case) |
| `pyproject.toml` | Project configuration |
| `Doxyfile` | Doxygen configuration |
## License
GNU Affero General Public License v3.0. See [LICENSE](LICENSE).
+49
View File
@@ -0,0 +1,49 @@
# Artifact Registry
This project's artifact state. Types, short names and `CrossReference
Candidates` come from the framework catalog
(`framework/registry/artifact-catalog.md`); this file only records where
each document lives in *this* project and the next version to use.
Delete rows for types you don't use. Add a row the first time you create a
document of a type. `Primary File` may contain a glob (e.g.
`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it.
| Short Name | Artifact Type | Primary File | Next Available Version |
| --- | --- | --- | --- |
| BC | Business Case | docs/business-case.md | 002 |
| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 |
| PP | Project Plan | docs/project-plan.md | 002 |
| MIL | Milestone / Gateway | docs/milestones/*.md | 005 |
| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 013 |
| TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 |
| UCD | Use Case Diagram | docs/use-case-diagram.md | 002 |
| US | User Story | docs/user-stories.md | 002 |
| UC | Use Case | docs/uc-*/uc.md | 002 |
| SSD | System Sequence Diagram | docs/uc-*/ssd.md | 002 |
## Languages
Set the PO language when the project starts; `project-planning` asks for it
if it is missing. A translated artifact is named `<artifact>.<language>.md`
(for example `business-case.da.md`); the English file stays the source.
| Setting | Value |
| --- | --- |
| PO language | en |
| High-level register | IT Executive English |
| Technical register | IT Professional English |
| Artifact types | Register | Also kept as a PO-language file |
| --- | --- | --- |
| BC, KPI, PP, MIL | IT Executive English | Yes |
| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes |
| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No |
## Notes
- "Next Available Version" is the zero-padded (3-digit) version to use the
*next* time a new document of that type is created. Increment it only when
a brand-new document is created, not when an existing document's
`## Version History` gets a row.
- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types.
+131
View File
@@ -0,0 +1,131 @@
# Business Case
## Metadata
| Key | Value |
| --- | --- |
| ID | BC-001 |
| CrossReference | [SA-001], [PP-001], [RC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Added target-date constraint and cost-benefit justification<br>Accepted after review RC-001 | [a1ff735] |
---
## Executive Summary
Build the "Higher Lower" console game from Udemy's 100 Days of Code (day 14) as a small, readable, dependency-free Python project. The player compares two Instagram accounts and guesses which has more followers; a correct guess adds a point and continues, a wrong guess ends the game. The repository is a learning artefact that other course participants and GitHub visitors can read, run and compare against.
## Methodological and Standards Foundation
Planning follows the project's SQA/QC framework (`framework/`): Business Case, Stakeholder Analysis, Project Plan, milestones synced as Gitea Milestones and Issues, then code. Product quality is described with ISO/IEC 25010:2023 characteristics.
## Problem Statement
The course assignment gives only a brief and a data set. Without a structured, tested and documented solution there is nothing to share, compare or reuse, and the practice value of the exercise is lost.
## Business Opportunity
A clean reference solution with the assignment's function names, tests and run instructions lets fellow participants compare approaches, and shows the author's practice of planning-first, tested development.
## Objectives
1. O1 — Deliver a playable console Higher Lower game using the assignment's data set, art and function names.
2. O2 — Keep the code readable: constants in `constants.py`, Doxygen comments in source, Python 3.13+.
3. O3 — Provide pytest tests for the game logic and the game loop.
4. O4 — Provide a README with venv, pip upgrade, run and test instructions, and a Doxyfile.
5. O5 — Publish the repository with a clear description and topics.
## Scope
### In Scope
- Console game: random pair selection, score tracking, one session that ends on a wrong guess.
- Assignment data set (50 entries), `logo` and `vs` ASCII art.
- `src/`, `tests/`, `docs/` layout, `pyproject.toml`, Python `.gitignore`, `Doxyfile`, `README.md`.
- Repository description and topics on the Gitea repository.
### Out of Scope
- Graphical or web user interface.
- Live data from Instagram or Google Trends.
- Persistent high scores.
- Runtime third-party dependencies.
- Committing or pushing (done by the author after review).
## Expected Benefits
### Tangible Benefits
- A runnable, tested repository other participants can clone.
- A reusable planning and documentation trail.
### Intangible Benefits
- Practice in decomposing a problem into small tasks and testing incrementally.
- Visibility of the author's work to GitHub viewers.
## Strategic Alignment
Supports the author's goal of completing the 100 Days of Code bootcamp with professional-quality practice (planning, tests, documentation).
## Success Criteria
| # | Criterion | Target | Measure |
| --- | --- | --- | --- |
| 1 | Game is playable | A full game runs from `python -m higher_lower` | Manual run |
| 2 | Tests pass | All pytest tests pass | `pytest` exit code 0 |
| 3 | No runtime dependencies | `dependencies = []` in `pyproject.toml` | File inspection |
| 4 | Docs complete | README, Doxyfile and Doxygen comments present | Review record |
| 5 | Repository metadata set | Description and topics visible on the host | Host page |
## Risks
| Risk | Impact | Mitigation |
| --- | --- | --- |
| Token from `.env` leaks into the project | Credential exposure | Keep `.env` gitignored; never import or test it; use only for host sync |
| Data set has entries out of order (e.g. Cardi B, David Beckham) | None for gameplay; may confuse readers | Keep data verbatim; the game compares counts, not list order |
| Same account drawn twice in a round | Meaningless round | Pair selection forces two distinct entries; tested |
## Assumptions
- The README template referenced in the brief will be supplied before the README task starts (see Project Plan open issues).
- Python 3.13 or newer is installed on the author's machine.
## Constraints
- Python 3.13+, `venv`, pytest, `pyproject.toml`, `constants.py`.
- No runtime dependencies unless needed.
- Do not commit, push or open a PR without the author's request.
- Product Owner language: English.
- Target completion 2026-10-14 (proposed by S01; no external deadline).
## Cost–Benefit Assessment
| Costs | Benefits |
| --- | --- |
| About one to two evenings of the author's time | A shareable, tested, documented reference solution |
The comparison is qualitative on purpose: this is a personal learning project with no revenue or budget, so a monetary ROI would be artificial. The cost is the author's time; the benefit is the shareable reference solution and practice gained.
## Stakeholders
| Stakeholder ID (SA) | Interest in this project |
| --- | --- |
| S01 | Product Owner, developer and reviewer; wants a correct, well-practised solution |
| S02 | Udemy coursists; want readable, runnable code with the assignment's function names |
| S03 | GitHub viewers; want a clear description, topics and README, and no dependencies |
## Recommendation
Proceed — the scope is small, the risks are low and the result serves all three stakeholders.
---
[SA-001]: ./stakeholder-analysis.md
[PP-001]: ./project-plan.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-001]: ./sqa/reviews/rc-001-business-case.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+75
View File
@@ -0,0 +1,75 @@
# MIL-001 Project setup
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-001 |
| CrossReference | [BC-001], [PP-001], [RC-004] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-004 | [a1ff735] |
---
## Purpose
Decide whether the project foundation (configuration, layout, tooling files and repository metadata) is ready for code to be written on top of it.
## Deliverable
`pyproject.toml`, Python `.gitignore`, `Doxyfile`, the `src/`, `tests/`, `docs/` layout with an importable empty package and `constants.py`, a passing smoke test, and the repository description and topics on the git host.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | `pyproject.toml` declares Python `>=3.13`, no runtime dependencies, pytest as a dev dependency | Present | Missing or has runtime dependencies |
| 2 | `.gitignore` excludes `.venv/`, `.env`, `__pycache__/`, `.pytest_cache/`, Doxygen output | All excluded | Any missing |
| 3 | `pip install -e ".[dev]"` and `pytest` succeed in a fresh `.venv` | Exit code 0 | Fails |
| 4 | `Doxyfile` exists and points at `src/` | Present | Missing |
| 5 | Repository has description and topics | Visible on host | Not set |
| 6 | `.env` is not referenced by any file under `src/` or `tests/` | No references | Any reference |
## Dependencies
| Depends on | Reason |
| --- | --- |
| None | First phase |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| O2, O4, O5 | [BC-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S01 |
## Target Date
2026-10-06 — first window of [PP-001], inside the proposed two-week-or-less target.
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Create pyproject.toml | Project configuration for a package named `higher_lower` under `src/`: `requires-python = ">=3.13"`, `dependencies = []`, an optional `dev` group with pytest, and pytest settings (`testpaths = ["tests"]`, `pythonpath = ["src"]`). Keeps runtime free of dependencies for S03. | No | |
| 2 | Verify Python .gitignore | A Python `.gitignore` already exists; confirm it ignores `.venv/`, `.env` (token file, personal use only), caches and Doxygen output (`docs/doxygen/`), adding entries where missing. | No | |
| 3 | Create src, tests and docs skeleton | Add `src/higher_lower/__init__.py`, `src/higher_lower/__main__.py` stub, `src/higher_lower/constants.py` (empty module with Doxygen header) and `tests/` with a smoke test importing the package. `docs/` already holds the planning artifacts. | No | |
| 4 | Add Doxyfile | Doxygen configuration with `INPUT = src`, `OUTPUT_DIRECTORY = docs/doxygen`, Python optimisation (`OPTIMIZE_OUTPUT_JAVA = YES`), recursive scan and `EXTRACT_ALL = YES`, so Doxygen comments in the source can be rendered. | No | |
| 5 | Set repository description and topics | Use the Gitea API with the token from `.env` (personal use; the token is never imported, copied or tested by the project) to set a description and topics such as `python`, `udemy`, `100-days-of-code`, `higher-lower`, `game`, `pytest`. Needs S01's go-ahead before it is run. | No | |
---
[BC-001]: ../business-case.md
[PP-001]: ../project-plan.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-004]: ../sqa/reviews/rc-004-mil-001.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+77
View File
@@ -0,0 +1,77 @@
# MIL-002 Game logic
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-002 |
| CrossReference | [BC-001], [PP-001], [RC-005] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-005 | [ee58fcd] |
---
## Purpose
Decide whether the game's data and pure functions are correct and tested before the interactive loop is built on them.
## Deliverable
The assignment data set and art as modules, constants in `constants.py`, and the pure functions `get_random_account`, `pick_pair`, `format_data` and `check_answer`, all with Doxygen comments and pytest tests.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | Data module holds the 50 assignment entries with `name`, `follower_count`, `description`, `country` | 50 entries, all keys | Count or keys differ |
| 2 | `logo` and `vs` art are available unchanged | Present | Missing or altered |
| 3 | Magic strings and numbers live in `constants.py` | None left in logic modules | Any left |
| 4 | `pick_pair` never returns the same entry twice | Verified by test | Can repeat |
| 5 | `check_answer` returns correct result for A, B and equal counts | Verified by tests | Any wrong |
| 6 | `pytest` passes; every public function has a Doxygen comment | Exit code 0, comments present | Fails or missing |
## Dependencies
| Depends on | Reason |
| --- | --- |
| [MIL-001] | Needs the package layout, pytest and constants module |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| O1, O2, O3 | [BC-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S01 |
## Target Date
2026-10-09 — second window of [PP-001].
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Add game data module | Store the assignment's list of 50 dictionaries (`name`, `follower_count`, `description`, `country`) verbatim in `src/higher_lower/game_data.py` as `data`. Order is kept as given; the game compares counts, not position. | No | |
| 2 | Add ASCII art module | Store `logo` and `vs` from the assignment in `src/higher_lower/art.py`, unchanged (raw strings so backslashes survive). | No | |
| 3 | Define constants | Put in `constants.py` the account dictionary keys, choice letters `"a"`/`"b"`, prompt and message texts, and the starting score, so the logic modules hold no magic values. | No | |
| 4 | Implement pair selection | `get_random_account()` returns a random entry; `pick_pair()` returns two distinct entries. Accept an injectable `random.Random` so tests are deterministic. | No | |
| 5 | Implement format_data and check_answer | `format_data(account)` returns "name, a description, from country"; `check_answer(guess, a_followers, b_followers)` returns whether the guess is right, using the assignment's names. Ties count as correct for either choice. | No | |
| 6 | Write unit tests for game logic | pytest tests for the data shape (50 entries, required keys), distinct pairs, `format_data` output and `check_answer` for A higher, B higher and equal. | No | |
---
[BC-001]: ../business-case.md
[PP-001]: ../project-plan.md
[MIL-001]: ./mil-001-project-setup.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-005]: ../sqa/reviews/rc-005-mil-002.md
[ee58fcd]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ee58fcdc1c9c83b0721022d623e4989591a47987
+79
View File
@@ -0,0 +1,79 @@
# MIL-003 Console game
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-003 |
| CrossReference | [BC-001], [PP-001], [US-001], [RC-006] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Linked UC-001 and the user stories<br>Accepted after review RC-006 | [923dde7] |
---
## Purpose
Decide whether the game can be played from start to finish in the console, with score tracking and robust input handling.
## Deliverable
An interactive console game started with `python -m higher_lower`, built on the [MIL-002] functions, with automated tests of the game loop.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | `python -m higher_lower` shows logo, Compare A, `vs` art, Against B and a prompt | All shown | Any missing |
| 2 | A correct guess increases the score by one and B becomes the next A | Verified by test | Not so |
| 3 | A wrong guess ends the game and shows the final score | Verified by test | Not so |
| 4 | Invalid input is rejected and re-asked, never crashes | Verified by test | Crash or accepted |
| 5 | Game ends gracefully if the data runs out | Verified by test | Error |
| 6 | `pytest` passes; no runtime dependencies added | Exit code 0 | Fails |
## Dependencies
| Depends on | Reason |
| --- | --- |
| [MIL-002] | Uses the data, art, constants and logic functions |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| O1, O3 | [BC-001] |
| US-001.01, US-001.02 | [US-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S01 |
## Target Date
2026-10-12 — third window of [PP-001].
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Implement display and input helpers | A function that prints the logo, "Compare A: ...", the `vs` art and "Against B: ..." using `format_data`, and one that asks for "A" or "B" and re-asks until valid (case-insensitive). Input and output go through injectable callables so tests need no real console. | No | |
| 2 | Implement the game loop | `play_game()` keeps the score, starts with a random pair, compares guesses with `check_answer`, moves B to A after a correct guess and picks a new B that differs from A. Prints the score after each round and "Sorry, that's wrong. Final score" at the end. | Yes | [UC-001], [US-001] |
| 3 | Handle exhausted data | If every account has been used, end the game with a win message instead of failing to find a new B. | No | |
| 4 | Add entry point | `python -m higher_lower` runs `main()`, which calls `play_game()`; no side effects on import. | No | |
| 5 | Write game loop tests | pytest tests with scripted input and a seeded random generator: correct streak, wrong first guess, invalid input then valid, data exhausted, and the score printed. | No | |
---
[BC-001]: ../business-case.md
[PP-001]: ../project-plan.md
[MIL-002]: ./mil-002-game-logic.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[US-001]: ../user-stories.md
[UC-001]: ../uc-001/uc.md
[RC-006]: ../sqa/reviews/rc-006-mil-003.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
+75
View File
@@ -0,0 +1,75 @@
# MIL-004 Documentation and release
## Metadata
| Key | Value |
| --- | --- |
| ID | MIL-004 |
| CrossReference | [BC-001], [PP-001], [RC-011] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-011 | [267e34f] |
---
## Purpose
Decide whether the repository is ready to be shared with S02 and S03: documented, reviewed and verifiable from a clean clone.
## Deliverable
`README.md` with venv, pip upgrade, run and test instructions, a Doxygen build that renders the source comments, review records for the project artifacts, and a verified clean-clone run.
## Go / No-Go Criteria
| # | Criterion (objectively checkable) | Go | No-Go |
| --- | --- | --- | --- |
| 1 | README follows the supplied template and states how to create `.venv`, run `python -m pip install --upgrade pip`, install, run and test | All present | Any missing |
| 2 | Following the README on a clean clone, the game starts and `pytest` passes | Yes | No |
| 3 | `doxygen Doxyfile` builds without warnings on public functions | No warnings | Warnings |
| 4 | Every reviewed artifact has an `RC-*` record in `docs/sqa/reviews/` | Present | Missing |
| 5 | `pyproject.toml` still lists no runtime dependencies | Empty | Any |
| 6 | No `.env` content or token appears in tracked files | Clean | Found |
## Dependencies
| Depends on | Reason |
| --- | --- |
| [MIL-003] | Needs the finished game to document and verify |
## Traceability
| Business Case objective / KPI / user story | Reference |
| --- | --- |
| O2, O4, O5 | [BC-001] |
## Ownership
| Role | Stakeholder ID (SA) |
| --- | --- |
| Owner | S01 |
| Approving reviewer | S01 |
## Target Date
2026-10-14 — final window of [PP-001].
## Tasks
| # | Task | Summary | Needs its own Use Case/User Story? | Reference |
| --- | --- | --- | --- | --- |
| 1 | Write README.md | Follow the template S01 supplies (still outstanding): what the game is, how to create and activate a local `.venv`, `python -m pip install --upgrade pip`, install with `pip install -e ".[dev]"`, run with `python -m higher_lower`, test with `pytest`, and how to build Doxygen docs. Serves S02 and S03. | No | |
| 2 | Audit Doxygen comments | Check that every module, function and constant group in `src/` has a Doxygen comment (`@brief`, `@param`, `@return`) and that `doxygen Doxyfile` builds cleanly. | No | |
| 3 | Review artifacts and record reviews | Review BC, SA, PP and the milestones against their QC checklists and create `RC-*` records in `docs/sqa/reviews/`; update version history rows to Accepted on a Go. | No | |
| 4 | Verify from a clean clone | In a fresh clone follow the README exactly: create `.venv`, upgrade pip, install, run the game, run pytest. Fix any gap in the README. | No | |
---
[BC-001]: ../business-case.md
[PP-001]: ../project-plan.md
[MIL-003]: ./mil-003-console-game.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-011]: ../sqa/reviews/rc-011-mil-004.md
[267e34f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/267e34fc9f1fb9d76cc20032f3b09dc8e9dd82c7
+99
View File
@@ -0,0 +1,99 @@
# Project Plan
## Metadata
| Key | Value |
| --- | --- |
| ID | PP-001 |
| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002], [MIL-003], [MIL-004], [RC-003] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-003 | [a1ff735] |
---
## Purpose
Schedule the four phases that deliver the Higher Lower game ([BC-001] objectives O1–O5) as a small, tested, documented Python project, with S01 reviewing each phase through its own branch and pull request.
## Planning Assumptions
- Week 1 starts 2026-10-04; the plan ends by 2026-10-14 (the Business Case sets no hard date; this is the author's evening-sized target, see Open Issues).
- One phase per branch and pull request, each closing its issues.
- Nothing under `src/` or `tests/` before the milestone is accepted and the task is a row in it.
- Only S01 reviews ([SA-001]).
## Gateway Schedule
| Gateway | Document | Window | Decision date | Owner | Stories | Main deliverable | Milestone |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Project setup | [MIL-001] | 2026-10-04 – 2026-10-06 | 2026-10-06 | S01 | — | pyproject, Doxyfile, skeleton, repo metadata | |
| Game logic | [MIL-002] | 2026-10-07 – 2026-10-09 | 2026-10-09 | S01 | — | Data, art, constants, pure functions, tests | |
| Console game | [MIL-003] | 2026-10-10 – 2026-10-12 | 2026-10-12 | S01 | — | Playable game loop with score, tests | |
| Documentation and release | [MIL-004] | 2026-10-13 – 2026-10-14 | 2026-10-14 | S01 | — | README, Doxygen run, review records | |
```plantuml
@startgantt
Project starts 2026-10-04
[Project setup] starts 2026-10-04 and ends 2026-10-06
[Project setup Go/No-Go] happens 2026-10-06
[Game logic] starts 2026-10-07 and ends 2026-10-09
[Game logic Go/No-Go] happens 2026-10-09
[Console game] starts 2026-10-10 and ends 2026-10-12
[Console game Go/No-Go] happens 2026-10-12
[Documentation and release] starts 2026-10-13 and ends 2026-10-14
[Documentation and release Go/No-Go] happens 2026-10-14
@endgantt
```
## Scope Coverage
| Business Case scope item | Gateway |
| --- | --- |
| `src/`, `tests/`, `docs/` layout, `pyproject.toml`, `.gitignore`, `Doxyfile` | [MIL-001] |
| Repository description and topics | [MIL-001] |
| Assignment data set, `logo` and `vs` art | [MIL-002] |
| Random pair selection, answer checking | [MIL-002] |
| Console game, score tracking | [MIL-003] |
| `README.md`, venv and test instructions | [MIL-004] |
## Dependencies
```
MIL-001 → MIL-002 → MIL-003 → MIL-004
```
A No-Go on any phase moves all later windows by the rework time.
## Plan Risks
| Risk | Impact | Mitigation |
| --- | --- | --- |
| README template not supplied | README task blocked | Ask S01 before MIL-004 starts |
| Host token missing or lacking scope | Cannot sync issues or set topics | Token read from `.env` for personal use only; ask S01 if absent |
| Doxygen not installed locally | Cannot verify docs build | Treat as a No-Go note; install or review comments manually |
## Open Issues
- README template: the brief says "using the template below" but none was included. Needed before MIL-004.
- Python version: "greater than 3.13" is planned as `>=3.13`; confirm if strictly 3.14+ is meant.
- Target dates are proposed, not stated in the brief; confirm.
- The brief links the organisation; the `origin` remote is the repository `014-higher_lower` within it, which is what will be synced.
---
[BC-001]: ./business-case.md
[SA-001]: ./stakeholder-analysis.md
[MIL-001]: ./milestones/mil-001-project-setup.md
[MIL-002]: ./milestones/mil-002-game-logic.md
[MIL-003]: ./milestones/mil-003-console-game.md
[MIL-004]: ./milestones/mil-004-docs-release.md
[Milestone MIL-001]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/milestones/36
[Milestone MIL-002]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/milestones/37
[Milestone MIL-003]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/milestones/38
[Milestone MIL-004]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/milestones/39
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-003]: ./sqa/reviews/rc-003-project-plan.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+48
View File
@@ -0,0 +1,48 @@
# SQA Review Record: Business Case
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-001 |
| CrossReference | [BC-001], [QC-BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] |
---
## Artifact Under Review
- Instance reviewed: [BC-001]
- Checklist used: [QC-BC-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | ROI/Cost-Benefit analysis is quantitative, or where qualitative, is explicitly justified | Pass | Qualitative; justification paragraph added under Cost–Benefit Assessment (personal project, no revenue or budget). |
| 2 | Risks are identified with documented impact and mitigation | Pass | Three risks, each with impact and mitigation. |
| 3 | Success criteria are measurable, stating explicit targets | Pass | Five criteria with target and measure. |
| 4 | Scope explicitly separates In Scope vs Out of Scope | Pass | Separate subsections. |
| 5 | Stakeholders are cross-referenced to Stakeholder Analysis IDs | Pass | S01–S03 used; the table only states interest. |
| 6 | Methodology and quality-standard foundation are stated explicitly (Optional) | Pass | SQA/QC framework and ISO/IEC 25010:2023 named. |
| 7 | Assumptions and constraints are explicit and distinguished | Pass | Separate sections; target date added to Constraints. |
| 8 | Document supports executive decision-making with a clear recommendation | Pass | Recommendation: Proceed, with rationale. |
## Overall Verdict
Go — all mandatory and optional criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[BC-001]: ../../business-case.md
[QC-BC-001]: ../../../framework/qc/qc-business-case.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
@@ -0,0 +1,48 @@
# SQA Review Record: Stakeholder Analysis
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-002 |
| CrossReference | [SA-001], [QC-SA-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] |
---
## Artifact Under Review
- Instance reviewed: [SA-001]
- Checklist used: [QC-SA-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Power/Interest grid is filled for every stakeholder | Pass | S01–S03 all classified. |
| 2 | Each stakeholder has a unique, stable ID | Pass | S01, S02, S03. |
| 3 | Roles and organizational context defined with explicit Power and Interest levels | Pass | Summary table. |
| 4 | Communication needs mapped to phases or milestones (Optional) | Pass | Communication Requirements table cites MIL-001 to MIL-004. |
| 5 | Conflicting interests identified with mitigation | Pass | One conflict (tooling versus simple repository), mitigated. |
| 6 | Stakeholder concerns traced to Business Case objectives | Pass | Business Goal Alignment table cites O1–O5. |
| 7 | Concerns expressed in business language and FURPS+ (Optional) | Pass | FURPS+ mapping table. |
| 8 | Understandable by non-technical stakeholders (Optional) | Pass | Plain language, short document. |
## Overall Verdict
Go — all criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[SA-001]: ../../stakeholder-analysis.md
[QC-SA-001]: ../../../framework/qc/qc-stakeholder-analysis.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+45
View File
@@ -0,0 +1,45 @@
# SQA Review Record: Project Plan
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-003 |
| CrossReference | [PP-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] |
---
## Artifact Under Review
- Instance reviewed: [PP-001]
- Checklist used: none exists for this type (`PP` has no QC checklist); criteria below follow `references/PP.md` "Validating".
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Every MIL-* gateway is scheduled | Pass | MIL-001 to MIL-004 listed with window, decision date and owner. |
| 2 | Schedule is consistent with the Business Case constraint | Pass | Ends 2026-10-14, matching the target-date constraint added to BC-001. |
| 3 | Gateway Schedule, Gantt, Scope Coverage, Dependencies, Plan Risks and Open Issues sections are present | Pass | All required sections present. |
| 4 | Each phase links to its synced Milestone | Pass | Milestones 36–39 linked. |
| 5 | Every Business Case scope item maps to a gateway | Pass | Scope Coverage table. |
## Overall Verdict
Go — no QC checklist exists for PP, so the plan was checked against BC-001 and the MIL-* documents. Open issues (README template, Python version, dates) remain listed in the plan. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Provide README template before MIL-004 task 1 | S01 | 2026-10-13 |
| Confirm requires-python (>=3.13 or 3.14+) | S01 | 2026-10-06 |
---
[PP-001]: ../../project-plan.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+46
View File
@@ -0,0 +1,46 @@
# SQA Review Record: MIL-001 Project setup
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-004 |
| CrossReference | [MIL-001], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] |
---
## Artifact Under Review
- Instance reviewed: [MIL-001]
- Checklist used: [QC-MIL-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Config files, skeleton, passing smoke test, repository metadata. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Six objectively checkable criteria. |
| 3 | Dependencies on other milestones are explicitly mapped (Optional) | Pass | First phase; stated as none. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | O2, O4, O5. |
| 5 | Milestone owner and approving reviewer are identified | Pass | S01 and S01. |
| 6 | Milestone has a target date consistent with project constraints | Pass | 2026-10-06, inside the 2026-10-14 constraint in BC-001. |
## Overall Verdict
Go — all criteria pass. MIL-002 to MIL-004 stay Proposed. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Confirm requires-python before issue #1 starts | S01 | 2026-10-06 |
---
[MIL-001]: ../../milestones/mil-001-project-setup.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+46
View File
@@ -0,0 +1,46 @@
# SQA Review Record: MIL-002 Game logic
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-005 |
| CrossReference | [MIL-002], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [ee58fcd] |
---
## Artifact Under Review
- Instance reviewed: [MIL-002]
- Checklist used: [QC-MIL-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | Data and art modules, constants, four named functions, Doxygen comments, pytest tests. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Six objectively checkable criteria. |
| 3 | Dependencies on other milestones are explicitly mapped (Optional) | Pass | Depends on MIL-001, with the reason. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | O1, O2, O3. |
| 5 | Milestone owner and approving reviewer are identified | Pass | S01 and S01. |
| 6 | Milestone has a target date consistent with project constraints | Pass | 2026-10-09, inside the 2026-10-14 constraint in BC-001. |
## Overall Verdict
Go — all criteria pass. Code under `src/` and `tests/` for MIL-002 may start once the MIL-001 deliverables exist (its dependency). MIL-003 and MIL-004 stay Proposed. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Deliver MIL-001 (issues #1-#4) before starting MIL-002 tasks | S01 | 2026-10-06 |
---
[MIL-002]: ../../milestones/mil-002-game-logic.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[ee58fcd]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ee58fcdc1c9c83b0721022d623e4989591a47987
+47
View File
@@ -0,0 +1,47 @@
# SQA Review Record: MIL-003 Console game
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-006 |
| CrossReference | [MIL-003], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [923dde7] |
---
## Artifact Under Review
- Instance reviewed: [MIL-003]
- Checklist used: [QC-MIL-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | A playable console game started with python -m higher_lower, with loop tests. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Six objectively checkable criteria. |
| 3 | Dependencies on other milestones are explicitly mapped (Optional) | Pass | Depends on MIL-002, with the reason. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | O1, O3; stories US-001.01 and US-001.02. |
| 5 | Milestone owner and approving reviewer are identified | Pass | S01 and S01. |
| 6 | Milestone has a target date consistent with project constraints | Pass | 2026-10-12, inside the 2026-10-14 constraint in BC-001. |
## Overall Verdict
Go — all criteria pass. UC-001 and its SSD were created for task 2 (the game loop); UCD-001, US-001, UC-001 and SSD-001 stay Proposed until they are reviewed. MIL-004 stays Proposed. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| Review UCD-001, US-001, UC-001 and SSD-001 against their QC checklists | S01 | 2026-10-12 |
| Add UCD-001 and US-001 to the CrossReference of BC-001 and PP-001 when those documents are next edited | S01 | 2026-10-14 |
---
[MIL-003]: ../../milestones/mil-003-console-game.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
+48
View File
@@ -0,0 +1,48 @@
# SQA Review Record: UCD-001 Use Case Diagram
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-007 |
| CrossReference | [UCD-001], [QC-UCD-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c7164fe] |
---
## Artifact Under Review
- Instance reviewed: [UCD-001]
- Checklist used: [QC-UCD-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Actors are defined with correct UML stereotypes (e.g. `<<System>>`, `<<Actor>>`) | Pass | Actor <<Actor>> and boundary <<System>> stereotyped. |
| 2 | System boundary is clearly drawn and labeled | Pass | Boundary rectangle labelled Higher Lower. |
| 3 | Include/extend relationships are used correctly per UML 2.5.1, not as generic "uses" arrows | Pass | No include/extend used; the Relationships section says so. |
| 4 | Every actor participates in at least one use case (no orphan actors) | Pass | Player is in Play a Game. |
| 5 | Diagram is traceable to a documented stakeholder need | Pass | Player traces to S01 and S02 in SA-001. |
| 6 | Use case names are verb phrases describing actor goals, not internal system operations | Pass | Play a Game is an actor goal. |
| 7 | Diagram is free of implementation detail (e.g. UI widgets, database tables) (Optional) | Pass | No UI or storage detail. |
| 8 | Actor and use case naming is consistent with corresponding Use Case and User Story documents (Optional) | Pass | Names match US-001 and UC-001. |
## Overall Verdict
Go — all applicable criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[UCD-001]: ../../use-case-diagram.md
[QC-UCD-001]: ../../../framework/qc/qc-use-case-diagram.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+47
View File
@@ -0,0 +1,47 @@
# SQA Review Record: US-001 User Stories
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-008 |
| CrossReference | [US-001], [QC-US-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c7164fe] |
---
## Artifact Under Review
- Instance reviewed: [US-001]
- Checklist used: [QC-US-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Follows INVEST criteria (Independent, Negotiable, Valuable, Estimable, Small, Testable) | Pass | INVEST check present, no exceptions. |
| 2 | Written in "As a / I want / So that" form | Pass | Both stories use As a / I want / so that. |
| 3 | Clear, testable acceptance criteria are included | Pass | Given/When/Then criteria on both stories. |
| 4 | Traceable to a use case or epic | Pass | Both trace to UC-001 and MIL-003. |
| 5 | Story is sized to fit within a single iteration (Optional) | Pass | Each fits one iteration. |
| 6 | Story statement avoids technical implementation detail (Optional) | Pass | Stated as player goals. |
| 7 | Role named in the story matches an actor defined in the Use Case Diagram | Pass | Role Player matches UCD-001. |
## Overall Verdict
Go — all applicable criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[US-001]: ../../user-stories.md
[QC-US-001]: ../../../framework/qc/qc-user-story.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+52
View File
@@ -0,0 +1,52 @@
# SQA Review Record: UC-001 Use Case
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-009 |
| CrossReference | [UC-001], [QC-UC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c7164fe] |
---
## Artifact Under Review
- Instance reviewed: [UC-001]
- Checklist used: [QC-UC-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Consists of a single, concise paragraph summarizing only the primary success scenario | N-A | Applies to the Brief format only; this use case is Fully Dressed. |
| 2 | Written as an informal multi-paragraph narrative; may mention some alternate flows without formal structure | N-A | Applies to the Casual format only; this use case is Fully Dressed. |
| 3 | All standard sections are present: actors, preconditions, postconditions, main success scenario, alternative/exception flows | Pass | Scope, level, actor, stakeholders, pre/postconditions, scenario, extensions, rules and open issues present. |
| 4 | Preconditions and postconditions are explicitly defined | Pass | Both defined. |
| 5 | Primary actor is explicitly stated | Pass | Player. |
| 6 | Stakeholders and their interests are stated | Pass | S01 and S02 with interests. |
| 7 | Main success scenario is written as clear, numbered steps | Pass | Seven numbered steps. |
| 8 | Alternative/exception flows correctly reference `<<include>>`/`<<extend>>` use cases where relevant, per UML 2.5.1 | Pass | No include/extend applies; stated. |
| 9 | Explicit business rules are captured per step where applicable, rather than embedded loosely in narrative text (Optional) | Pass | Rules listed for steps 2, 3b and 5. |
| 10 | Naming of actors and use case title is consistent with the corresponding Use Case Diagram and User Stories | Pass | Title and actor match UCD-001 and US-001. |
| 11 | Scope/level (e.g. summary, user-goal, subfunction) is explicitly stated (Optional) | Pass | Level: user goal. |
| 12 | Use case is written from the actor's goal perspective, free of UI or implementation detail | Pass | Goal wording, no UI or implementation detail. |
## Overall Verdict
Go — all applicable criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[UC-001]: ../../uc-001/uc.md
[QC-UC-001]: ../../../framework/qc/qc-use-case.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+47
View File
@@ -0,0 +1,47 @@
# SQA Review Record: SSD-001 System Sequence Diagram
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-010 |
| CrossReference | [SSD-001], [QC-SSD-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [c7164fe] |
---
## Artifact Under Review
- Instance reviewed: [SSD-001]
- Checklist used: [QC-SSD-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Input/output messages match the corresponding Use Case's main success scenario step-for-step | Pass | startGame and makeGuess cover steps 1 to 7 of UC-001. |
| 2 | Actor and System are treated strictly as black boxes (system shown as `:System`) | Pass | Only Player and :System. |
| 3 | Object creation/destruction of the System instance handled explicitly where relevant (Optional) | Pass | Lifecycle notes state when the game starts and ends. |
| 4 | Return values are shown for operations that produce one, using dashed return arrows | Pass | Dashed returns on every operation. |
| 5 | Alternate/exceptional flows are represented separately (or explicitly out of scope noted) | Pass | Alternative flows noted as out of scope. |
| 6 | Message names are verb phrases consistent with the use case's system responsibilities (Optional) | Pass | startGame, makeGuess. |
| 7 | Diagram references the specific Use Case (name and ID) it depicts | Pass | Cites UC-001 Play a Game. |
## Overall Verdict
Go — all applicable criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[SSD-001]: ../../uc-001/ssd.md
[QC-SSD-001]: ../../../framework/qc/qc-ssd.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+46
View File
@@ -0,0 +1,46 @@
# SQA Review Record: MIL-004 Documentation and release
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-011 |
| CrossReference | [MIL-004], [QC-MIL-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [267e34f] |
---
## Artifact Under Review
- Instance reviewed: [MIL-004]
- Checklist used: [QC-MIL-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | A concrete deliverable is defined for every gate | Pass | README, Doxygen build, review records and a verified clean copy. |
| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Six objectively checkable criteria. |
| 3 | Dependencies on other milestones are explicitly mapped (Optional) | Pass | Depends on MIL-003, with the reason. |
| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | O2, O4, O5. |
| 5 | Milestone owner and approving reviewer are identified | Pass | S01 and S01. |
| 6 | Milestone has a target date consistent with project constraints | Pass | 2026-10-14, the date in the BC-001 constraint. |
## Overall Verdict
Go — all criteria pass. Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[MIL-004]: ../../milestones/mil-004-docs-release.md
[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md
[267e34f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/267e34fc9f1fb9d76cc20032f3b09dc8e9dd82c7
+52
View File
@@ -0,0 +1,52 @@
# SQA Review Record: Source code of higher_lower
## Metadata
| Key | Value |
| --- | --- |
| ID | RC-012 |
| CrossReference | [QC-PY-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [267e34f] |
---
## Artifact Under Review
- Instance reviewed: the Python source in `src/higher_lower/` and `tests/` at the commit of this review (source code has no artifact ID)
- Checklist used: [QC-PY-001]
## Checklist Results
| # | Criterion | Status | Evidence/Notes |
| --- | --- | --- | --- |
| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | Modules and functions snake_case, constants UPPER_SNAKE, TypedDict PascalCase; ruff (E, F, I, UP, FLY, B) passes. |
| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names say purpose (check_answer, pick_pair); i/n-style names are not used. |
| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | `ruff check` and `ruff format --check` pass; the only suppression is E501 for the verbatim data file, with a comment in pyproject.toml. |
| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | All signatures annotated; `mypy --strict src tests` reports no issues. |
| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | Pass | No bare except; `main` catches EOFError and KeyboardInterrupt on purpose and leaves with a goodbye. |
| 6 | No mutable default arguments and no shadowed builtins | Pass | No mutable defaults; the data list default is a read-only module constant. |
| 7 | Files, locks and connections are managed with context managers | N-A | The code opens no files, locks or connections. |
| 8 | Public modules, classes and functions have docstrings that say what, not how (Optional) | Pass | Every module, class and function has a Doxygen docstring; checked with an AST script. |
| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | N-A | The game prints its interface to the console; there is no logging, and no secrets or personal data are printed. |
| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | N-A | No Design Class Diagram exists or is planned; the only class is the Account TypedDict, and the functions implement the operations of SSD-001. |
| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | 36 pytest tests named for behaviour, with scripted input and seeded randomness; no network, no order dependence. |
| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment (Optional) | Pass | mypy strict runs without errors; no Any is used. |
| 13 | Dependencies are declared and pinned in the project's dependency file, none unused (Optional) | Pass | No runtime dependencies; dev tools declared in pyproject.toml with version ranges. |
## Overall Verdict
Go — all applicable criteria pass. The review found a lint ordering error, format differences and strict-typing errors; they were fixed before this verdict (ruff import order and format, `Final` on the account key constants). Reviewer and author are both S01: the project has a single participant, so the framework's author/reviewer separation cannot be met. S01 accepted this deviation when requesting acceptance (2026-10-04).
## Action Items
| Action | Owner | Due |
| --- | --- | --- |
| None | — | — |
---
[QC-PY-001]: ../../../framework/qc/qc-programming-python.md
[267e34f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/267e34fc9f1fb9d76cc20032f3b09dc8e9dd82c7
+70
View File
@@ -0,0 +1,70 @@
# Traceability Matrix
## Metadata
| Key | Value |
| --- | --- |
| ID | TM-001 |
| CrossReference | [BC-001] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-007 to RC-010 for UCD-001, US-001, UC-001 and SSD-001 | [c7164fe] |
| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-011 and RC-012 | [267e34f] |
---
## Purpose
Tracks backward/forward links between artifact instances so that the Business Case traceability
target is measurable. A row is added or updated whenever an artifact instance is created or reviewed.
## Traceability Table
| Artifact Instance | Type | Upstream (Backward Link) | Downstream (Forward Link) | Last Reviewed (RC-ID) |
| --- | --- | --- | --- | --- |
| [BC-001] | BC | - | [SA-001], [PP-001] | [RC-001] |
| [SA-001] | SA | [BC-001] | [PP-001] | [RC-002] |
| [PP-001] | PP | [BC-001], [SA-001] | [MIL-001], [MIL-002], [MIL-003], [MIL-004] | [RC-003] |
| [MIL-001] | MIL | [BC-001], [PP-001] | - | [RC-004] |
| [MIL-002] | MIL | [BC-001], [PP-001], [MIL-001] | - | [RC-005] |
| [MIL-003] | MIL | [BC-001], [PP-001], [MIL-002], [US-001] | [MIL-004], [UC-001] | [RC-006] |
| [MIL-004] | MIL | [BC-001], [PP-001], [MIL-003] | - | [RC-011] |
| [UCD-001] | UCD | [SA-001], [BC-001] | [US-001], [UC-001] | [RC-007] |
| [US-001] | US | [UCD-001], [BC-001], [MIL-003] | [UC-001] | [RC-008] |
| [UC-001] | UC | [UCD-001], [US-001], [SA-001] | [SSD-001] | [RC-009] |
| [SSD-001] | SSD | [UC-001] | - | [RC-010] |
## Coverage Notes
- No domain model or design artifacts exist yet.
- RC-012 reviews the Python source against QC-PY-001; source code has no artifact ID, so it has no row above.
- `-` in Upstream means foundational; in Downstream, nothing is built on it yet; in Last Reviewed, no `RC-*` exists yet.
---
[BC-001]: ../business-case.md
[SA-001]: ../stakeholder-analysis.md
[PP-001]: ../project-plan.md
[MIL-001]: ../milestones/mil-001-project-setup.md
[MIL-002]: ../milestones/mil-002-game-logic.md
[MIL-003]: ../milestones/mil-003-console-game.md
[MIL-004]: ../milestones/mil-004-docs-release.md
[RC-001]: ./reviews/rc-001-business-case.md
[RC-002]: ./reviews/rc-002-stakeholder-analysis.md
[RC-003]: ./reviews/rc-003-project-plan.md
[RC-004]: ./reviews/rc-004-mil-001.md
[RC-005]: ./reviews/rc-005-mil-002.md
[UCD-001]: ../use-case-diagram.md
[US-001]: ../user-stories.md
[UC-001]: ../uc-001/uc.md
[SSD-001]: ../uc-001/ssd.md
[RC-006]: ./reviews/rc-006-mil-003.md
[RC-007]: ./reviews/rc-007-ucd-001.md
[RC-008]: ./reviews/rc-008-us-001.md
[RC-009]: ./reviews/rc-009-uc-001.md
[RC-010]: ./reviews/rc-010-ssd-001.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
[RC-011]: ./reviews/rc-011-mil-004.md
[RC-012]: ./reviews/rc-012-source-code.md
[267e34f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/267e34fc9f1fb9d76cc20032f3b09dc8e9dd82c7
+82
View File
@@ -0,0 +1,82 @@
# Stakeholder Analysis
## Metadata
| Key | Value |
| --- | --- |
| ID | SA-001 |
| CrossReference | [BC-001], [PP-001], [RC-002] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-002 | [a1ff735] |
---
## Purpose
Identify who is affected by the Higher Lower project and what each needs, using a power/interest grid. Stakeholder IDs are stable and cited by every other artifact.
## Stakeholder Summary Table
| ID | Name | Role/Title | Organization | Power Level | Interest Level | Quadrant | Primary Concern (Business Language) |
| --- | --- | --- | --- | --- | --- | --- | --- |
| S01 | Jens Tirsvad Nielsen | Course participant; Product Owner, developer and reviewer | Tirsvad | HIGH | HIGH | Manage Closely | A correct, tested, well-documented solution that shows good practice |
| S02 | Udemy coursists | Course participants who share and compare solutions | Udemy community | LOW | HIGH | Keep Informed | Readable, runnable code with the assignment's function names, and README instructions to run it |
| S03 | GitHub viewers | Visitors browsing the repository for ideas | Public | LOW | LOW | Monitor | Clear repository description, topics and README; no runtime dependencies |
## Power/Interest Classification Rationale
- **Manage Closely (S01):** decides scope, writes and reviews everything.
- **Keep Informed (S02):** no decision power, but they are the main audience; served by README and code clarity.
- **Monitor (S03):** casual visitors; served by repository metadata and a short README.
## Primary Concerns and FURPS+ Mapping
| ID | Concern | FURPS+ attribute |
| --- | --- | --- |
| S01 | Game behaves correctly and is covered by tests | Functionality, Reliability |
| S01 | Planning trail and review records exist | Supportability |
| S02 | Code is readable and uses the assignment's function names | Supportability |
| S02 | Can run the game and tests from the README | Usability |
| S03 | Findable and understandable repository | Usability |
| S03 | Nothing extra to install | Implementation (no runtime dependencies) |
## Communication Requirements
| ID | Channel | Frequency | Deliverable | Phase / Milestone |
| --- | --- | --- | --- | --- |
| S01 | Pull request review | Per milestone | Reviewed milestone deliverable | MIL-001 to MIL-004 |
| S02 | README | At release | Run and test instructions | MIL-004 |
| S03 | Repository description, topics, README | At release | Repository metadata | MIL-001, MIL-004 |
## Conflicting Interests and Mitigations
| Conflict | Stakeholders | Mitigation |
| --- | --- | --- |
| Heavy tooling (Doxygen, review records) versus a simple repository for visitors | S01, S03 | Keep tooling in `docs/` and config files; README stays short and runtime stays dependency-free |
## Traceability Analysis
### Business Goal Alignment
| Stakeholder | Concern | Business Case objective |
| --- | --- | --- |
| S01 | Correct, tested game | [BC-001] O1, O3 |
| S02 | Readable code, run instructions | [BC-001] O2, O4 |
| S03 | Description, topics, no dependencies | [BC-001] O4, O5 |
## Sign-Off
| Stakeholder | Decision | Date |
| --- | --- | --- |
| S01 | Go (RC-002) | 2026-10-04 |
---
[BC-001]: ./business-case.md
[PP-001]: ./project-plan.md
[ddfe96f]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ddfe96ffa459d5e4b2e30d71bd53878aba2cfc5d
[RC-002]: ./sqa/reviews/rc-002-stakeholder-analysis.md
[a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853
+57
View File
@@ -0,0 +1,57 @@
# System Sequence Diagram: Play a Game
## Metadata
| Key | Value |
| --- | --- |
| ID | SSD-001 |
| CrossReference | [UC-001], [RC-010] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [923dde7] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-010 | [c7164fe] |
---
## Source Use Case
[UC-001] Play a Game, main success scenario. The alternative flows (invalid answer, tie, no account left) are out of scope for this diagram.
## Diagram
```plantuml
@startuml
actor Player
participant ":System" as System
Player -> System : startGame()
System --> Player : accounts A and B
loop while the guess is right
Player -> System : makeGuess(choice)
System --> Player : right, score, new accounts A and B
end
Player -> System : makeGuess(choice)
System --> Player : wrong, final score
@enduml
```
## System Operations Table
| Step | Message (verb phrase) | Parameters | Return | Use case step |
| --- | --- | --- | --- | --- |
| 1 | startGame | none | accounts A and B | 1, 2 |
| 2 | makeGuess | choice (A or B) | right, new score, new accounts A and B | 3, 4, 5, 6 |
| 3 | makeGuess | choice (A or B) | wrong, final score | 3, 7 |
Steps 2 and 3 are the same operation; the loop shows the right-guess outcome repeating, and the last call shows the wrong-guess outcome.
## Lifecycle Notes
A game starts with `startGame` and ends when `makeGuess` returns wrong; the System keeps the score and the current accounts in between.
---
[UC-001]: ./uc.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
[RC-010]: ../sqa/reviews/rc-010-ssd-001.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+81
View File
@@ -0,0 +1,81 @@
# UC-001 Play a Game
## Metadata
| Key | Value |
| --- | --- |
| ID | UC-001 |
| CrossReference | [UCD-001], [US-001], [SA-001], [SSD-001], [RC-009] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [923dde7] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-009 | [c7164fe] |
---
Format: Fully Dressed
## Scope
The Higher Lower console game.
## Level
User goal.
## Primary Actor
Player.
## Stakeholders and Interests
| Stakeholder | Interest |
| --- | --- |
| S01 | A correct game that shows good practice ([SA-001]) |
| S02 | Readable behaviour that matches the assignment ([SA-001]) |
## Preconditions
The Player has started the game.
## Postconditions (Success Guarantee)
The Player has seen the final score of the game.
## Main Success Scenario
1. The Player starts a game.
2. The System shows two different accounts, A and B.
3. The Player guesses which account has more followers.
4. The System tells the Player the guess was right, adds one to the score, and shows the new score.
5. The System makes the account the Player compared against the new A and shows a new B.
6. The Player repeats from step 3 until a guess is wrong.
7. The System tells the Player the guess was wrong and shows the final score.
## Extensions / Alternative Flows
- 3a. The Player gives an answer that is not a valid choice: the System asks again; the round does not change.
- 3b. Both accounts have the same number of followers: either choice counts as right.
- 5a. No account is left that differs from the current A: the System ends the game and shows the final score.
No `<<include>>` or `<<extend>>` use cases apply.
## Special Requirements / Business Rules
- Step 2 and 5: A and B are never the same account.
- Step 3b: equal follower counts make both choices right.
## Open Issues
None.
---
[UCD-001]: ../use-case-diagram.md
[US-001]: ../user-stories.md
[SA-001]: ../stakeholder-analysis.md
[SSD-001]: ./ssd.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
[RC-009]: ../sqa/reviews/rc-009-uc-001.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+58
View File
@@ -0,0 +1,58 @@
# Use Case Diagram
## Metadata
| Key | Value |
| --- | --- |
| ID | UCD-001 |
| CrossReference | [SA-001], [BC-001], [US-001], [UC-001], [RC-007] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [923dde7] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-007 | [c7164fe] |
---
## Purpose and Scope
The system boundary is the Higher Lower console game. One actor, the Player, uses it to play a game. Traces to [BC-001] objective O1 and to stakeholders S01 and S02 ([SA-001]), who play the game; S03 only reads the repository.
## Diagram
```plantuml
@startuml
left to right direction
actor Player <<Actor>>
rectangle "Higher Lower <<System>>" {
usecase "Play a Game" as PlayAGame
}
Player --> PlayAGame
@enduml
```
## Actor Table
| Actor | Stereotype | Stakeholder ID (SA) | Goals (use cases) |
| --- | --- | --- | --- |
| Player | Actor | S01, S02 | Play a Game |
## Use Case Table
| Use Case | Actor(s) | Goal |
| --- | --- | --- |
| Play a Game ([UC-001]) | Player | Guess, round after round, which of two accounts has more followers, and see the final score |
## Relationships
None: there is a single use case, so no `<<include>>` or `<<extend>>` is used.
---
[SA-001]: ./stakeholder-analysis.md
[BC-001]: ./business-case.md
[US-001]: ./user-stories.md
[UC-001]: ./uc-001/uc.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
[RC-007]: ./sqa/reviews/rc-007-ucd-001.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
+54
View File
@@ -0,0 +1,54 @@
# User Stories
## Metadata
| Key | Value |
| --- | --- |
| ID | US-001 |
| CrossReference | [UCD-001], [UC-001], [BC-001], [MIL-003], [RC-008] |
## Version History
| Date | Status | Author | Reviewer | Change | Commit |
| --- | --- | --- | --- | --- | --- |
| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [923dde7] |
| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-008 | [c7164fe] |
---
## Purpose and Scope
The stories for epic [MIL-003] (Console game): playing rounds and seeing the result. They support [BC-001] objective O1.
## Story List
### US-001.01 Guess which account has more followers
- **As a** Player, **I want** to guess which of two accounts has more followers and keep playing while I am right, **so that** I can build a streak.
- **Acceptance Criteria:**
- Given a new game, when it starts, then two different accounts A and B are shown.
- Given two accounts, when I guess the one with more followers, then my score goes up by one and the next round starts with that account as A and a new account as B.
- Given an invalid answer, when I submit it, then I am asked again and the round does not change.
- **Traces to:** [UC-001], [MIL-003]
- **Size:** one iteration.
### US-001.02 See my final score
- **As a** Player, **I want** to see my final score when the game ends, **so that** I know how well I did.
- **Acceptance Criteria:**
- Given a round, when I guess the account with fewer followers, then the game ends and shows my final score.
- Given every account has been used, when no new account can be shown, then the game ends and shows my final score.
- **Traces to:** [UC-001], [MIL-003]
- **Size:** one iteration.
## INVEST Check
Both stories are Independent of each other in wording, Negotiable in detail, Valuable to the Player, Estimable, Small (one iteration) and Testable through the acceptance criteria. No exceptions.
---
[UCD-001]: ./use-case-diagram.md
[UC-001]: ./uc-001/uc.md
[BC-001]: ./business-case.md
[MIL-003]: ./milestones/mil-003-console-game.md
[923dde7]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/923dde7dc9b59d2c77eddc88336451193147c557
[RC-008]: ./sqa/reviews/rc-008-us-001.md
[c7164fe]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/c7164feab1333951bf6f557baf95a26764656be0
Submodule
+1
Submodule framework added at 14d221ec1c
+35
View File
@@ -0,0 +1,35 @@
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[project]
name = "higher-lower"
version = "0.1.0"
description = "Higher Lower console game: guess which Instagram account has more followers (Udemy 100 Days of Code, day 14)."
readme = "README.md"
requires-python = ">=3.13"
license = { file = "LICENSE" }
dependencies = []
[project.optional-dependencies]
dev = ["pytest>=9,<10", "ruff>=0.16,<0.17", "mypy>=2.4,<3"]
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "FLY", "B"]
[tool.ruff.lint.per-file-ignores]
"src/higher_lower/game_data.py" = ["E501"] # assignment data kept verbatim
[tool.mypy]
strict = true
mypy_path = "src"
+3
View File
@@ -0,0 +1,3 @@
"""@package higher_lower
@brief Higher Lower console game: guess which Instagram account has more followers.
"""
+19
View File
@@ -0,0 +1,19 @@
"""@file __main__.py
@brief Entry point for `python -m higher_lower`.
"""
from higher_lower.constants import MSG_GOODBYE
from higher_lower.game import play_game
def main() -> None:
"""@brief Play a game; leave quietly if the player quits with Ctrl+C or Ctrl+D."""
try:
play_game()
except (EOFError, KeyboardInterrupt):
print()
print(MSG_GOODBYE)
if __name__ == "__main__":
main()
+45
View File
@@ -0,0 +1,45 @@
"""@file art.py
@brief ASCII art shown by the game, as given by the assignment.
The lines are raw strings so the backslashes in the art survive unchanged.
They are concatenated line by line, rather than written as one multi-line
string, so Doxygen does not read the backticks in the art as markup.
"""
## The game logo.
logo = (
"\n"
r" __ ___ __ "
"\n"
r" / / / (_)___ _/ /_ ___ _____"
"\n"
r" / /_/ / / __ `/ __ \/ _ \/ ___/"
"\n"
r" / __ / / /_/ / / / / __/ / "
"\n"
r"/_/ ///_/\__, /_/ /_/\___/_/ "
"\n"
r" / / /____/_ _____ _____"
"\n"
r" / / / __ \ | /| / / _ \/ ___/"
"\n"
r" / /___/ /_/ / |/ |/ / __/ / "
"\n"
r"/_____/\____/|__/|__/\___/_/ "
"\n"
)
## The "vs" shown between the two accounts.
vs = (
"\n"
r" _ __ "
"\n"
r"| | / /____"
"\n"
r"| | / / ___/"
"\n"
r"| |/ (__ ) "
"\n"
r"|___/____(_)"
"\n"
)
+31
View File
@@ -0,0 +1,31 @@
"""@file constants.py
@brief Constants shared by the game modules.
"""
from typing import Final
## Keys of an account dictionary.
KEY_NAME: Final = "name"
KEY_FOLLOWER_COUNT: Final = "follower_count"
KEY_DESCRIPTION: Final = "description"
KEY_COUNTRY: Final = "country"
## The two choices the player can type, in lower case.
CHOICE_A = "a"
CHOICE_B = "b"
## Score at the start of a game.
START_SCORE = 0
## How an account is described to the player.
ACCOUNT_TEMPLATE = "{name}, a {description}, from {country}"
## Texts shown to the player.
MSG_COMPARE_A = "Compare A: {account}."
MSG_AGAINST_B = "Against B: {account}."
PROMPT_GUESS = "Who has more followers? Type 'A' or 'B': "
MSG_INVALID_CHOICE = "Please type 'A' or 'B'."
MSG_CORRECT = "You're right! Current score: {score}."
MSG_WRONG = "Sorry, that's wrong. Final score: {score}"
MSG_WIN = "You went through every account! Final score: {score}"
MSG_GOODBYE = "Goodbye!"
+122
View File
@@ -0,0 +1,122 @@
"""@file game.py
@brief The interactive game: what the player sees, what they type, and the game loop.
Input and output go through callables so tests can script a whole game.
"""
import random
from collections.abc import Callable, Sequence
from higher_lower.art import logo, vs
from higher_lower.constants import (
CHOICE_A,
CHOICE_B,
KEY_FOLLOWER_COUNT,
KEY_NAME,
MSG_AGAINST_B,
MSG_COMPARE_A,
MSG_CORRECT,
MSG_INVALID_CHOICE,
MSG_WIN,
MSG_WRONG,
PROMPT_GUESS,
START_SCORE,
)
from higher_lower.game_data import Account, data
from higher_lower.game_logic import (
check_answer,
format_data,
get_random_account,
pick_pair,
)
InputFunc = Callable[[str], str]
OutputFunc = Callable[[str], None]
def show_round(
account_a: Account,
account_b: Account,
output: OutputFunc = print,
) -> None:
"""@brief Show the two accounts the player compares.
@param account_a The account shown as A.
@param account_b The account shown as B.
@param output Where the text goes.
"""
output(MSG_COMPARE_A.format(account=format_data(account_a)))
output(vs)
output(MSG_AGAINST_B.format(account=format_data(account_b)))
def ask_choice(input_func: InputFunc = input, output: OutputFunc = print) -> str:
"""@brief Ask for A or B until the player gives a valid answer.
@param input_func Reads one line of input, given the prompt.
@param output Where the invalid-answer message goes.
@return "a" or "b", in lower case.
"""
while True:
answer = input_func(PROMPT_GUESS).strip().lower()
if answer in (CHOICE_A, CHOICE_B):
return answer
output(MSG_INVALID_CHOICE)
def _pick_next_account(
accounts: Sequence[Account],
shown_names: set[str],
rng: random.Random | None,
) -> Account | None:
"""@brief Draw an account that has not been shown in this game.
@param accounts All accounts of the game.
@param shown_names Names of the accounts already shown.
@param rng Random generator, or None for the default.
@return A new account, or None if every account has been shown.
"""
unused = [account for account in accounts if account[KEY_NAME] not in shown_names]
if not unused:
return None
return get_random_account(unused, rng)
def play_game(
accounts: Sequence[Account] = data,
rng: random.Random | None = None,
input_func: InputFunc = input,
output: OutputFunc = print,
) -> int:
"""@brief Play one game, from the logo to the final score.
Each account is shown once per game. A right guess adds a point and
makes B the next A; a wrong guess, or running out of accounts, ends
the game.
@param accounts The accounts to play with; needs at least two entries.
@param rng Random generator; pass a seeded one for a repeatable game.
@param input_func Reads one line of input, given the prompt.
@param output Where the text goes.
@return The final score.
"""
output(logo)
score = START_SCORE
account_a, account_b = pick_pair(accounts, rng)
shown_names = {account_a[KEY_NAME], account_b[KEY_NAME]}
while True:
show_round(account_a, account_b, output)
guess = ask_choice(input_func, output)
if not check_answer(
guess, account_a[KEY_FOLLOWER_COUNT], account_b[KEY_FOLLOWER_COUNT]
):
output(MSG_WRONG.format(score=score))
return score
score += 1
output(MSG_CORRECT.format(score=score))
next_account = _pick_next_account(accounts, shown_names, rng)
if next_account is None:
output(MSG_WIN.format(score=score))
return score
account_a, account_b = account_b, next_account
shown_names.add(next_account[KEY_NAME])
+324
View File
@@ -0,0 +1,324 @@
"""@file game_data.py
@brief The accounts the player compares, as given by the assignment.
The order is kept as in the assignment; the game compares follower counts,
never list positions.
"""
from typing import TypedDict
class Account(TypedDict):
"""@brief One account the player can be asked about.
follower_count is in millions of Instagram followers.
"""
name: str
follower_count: int
description: str
country: str
data: list[Account] = [
{
"name": "Instagram",
"follower_count": 346,
"description": "Social media platform",
"country": "United States",
},
{
"name": "Cristiano Ronaldo",
"follower_count": 215,
"description": "Footballer",
"country": "Portugal",
},
{
"name": "Ariana Grande",
"follower_count": 183,
"description": "Musician and actress",
"country": "United States",
},
{
"name": "Dwayne Johnson",
"follower_count": 181,
"description": "Actor and professional wrestler",
"country": "United States",
},
{
"name": "Selena Gomez",
"follower_count": 174,
"description": "Musician and actress",
"country": "United States",
},
{
"name": "Kylie Jenner",
"follower_count": 172,
"description": "Reality TV personality and businesswoman and Self-Made Billionaire",
"country": "United States",
},
{
"name": "Kim Kardashian",
"follower_count": 167,
"description": "Reality TV personality and businesswoman",
"country": "United States",
},
{
"name": "Lionel Messi",
"follower_count": 149,
"description": "Footballer",
"country": "Argentina",
},
{
"name": "Beyoncé",
"follower_count": 145,
"description": "Musician",
"country": "United States",
},
{
"name": "Neymar",
"follower_count": 138,
"description": "Footballer",
"country": "Brasil",
},
{
"name": "National Geographic",
"follower_count": 135,
"description": "Magazine",
"country": "United States",
},
{
"name": "Justin Bieber",
"follower_count": 133,
"description": "Musician",
"country": "Canada",
},
{
"name": "Taylor Swift",
"follower_count": 131,
"description": "Musician",
"country": "United States",
},
{
"name": "Kendall Jenner",
"follower_count": 127,
"description": "Reality TV personality and Model",
"country": "United States",
},
{
"name": "Jennifer Lopez",
"follower_count": 119,
"description": "Musician and actress",
"country": "United States",
},
{
"name": "Nicki Minaj",
"follower_count": 113,
"description": "Musician",
"country": "Trinidad and Tobago",
},
{
"name": "Nike",
"follower_count": 109,
"description": "Sportswear multinational",
"country": "United States",
},
{
"name": "Khloé Kardashian",
"follower_count": 108,
"description": "Reality TV personality and businesswoman",
"country": "United States",
},
{
"name": "Miley Cyrus",
"follower_count": 107,
"description": "Musician and actress",
"country": "United States",
},
{
"name": "Katy Perry",
"follower_count": 94,
"description": "Musician",
"country": "United States",
},
{
"name": "Kourtney Kardashian",
"follower_count": 90,
"description": "Reality TV personality",
"country": "United States",
},
{
"name": "Kevin Hart",
"follower_count": 89,
"description": "Comedian and actor",
"country": "United States",
},
{
"name": "Ellen DeGeneres",
"follower_count": 87,
"description": "Comedian",
"country": "United States",
},
{
"name": "Real Madrid CF",
"follower_count": 86,
"description": "Football club",
"country": "Spain",
},
{
"name": "FC Barcelona",
"follower_count": 85,
"description": "Football club",
"country": "Spain",
},
{
"name": "Rihanna",
"follower_count": 81,
"description": "Musician and businesswoman",
"country": "Barbados",
},
{
"name": "Demi Lovato",
"follower_count": 80,
"description": "Musician and actress",
"country": "United States",
},
{
"name": "Victoria's Secret",
"follower_count": 69,
"description": "Lingerie brand",
"country": "United States",
},
{
"name": "Zendaya",
"follower_count": 68,
"description": "Actress and musician",
"country": "United States",
},
{
"name": "Shakira",
"follower_count": 66,
"description": "Musician",
"country": "Colombia",
},
{
"name": "Drake",
"follower_count": 65,
"description": "Musician",
"country": "Canada",
},
{
"name": "Chris Brown",
"follower_count": 64,
"description": "Musician",
"country": "United States",
},
{
"name": "LeBron James",
"follower_count": 63,
"description": "Basketball player",
"country": "United States",
},
{
"name": "Vin Diesel",
"follower_count": 62,
"description": "Actor",
"country": "United States",
},
{
"name": "Cardi B",
"follower_count": 67,
"description": "Musician",
"country": "United States",
},
{
"name": "David Beckham",
"follower_count": 82,
"description": "Footballer",
"country": "United Kingdom",
},
{
"name": "Billie Eilish",
"follower_count": 61,
"description": "Musician",
"country": "United States",
},
{
"name": "Justin Timberlake",
"follower_count": 59,
"description": "Musician and actor",
"country": "United States",
},
{
"name": "UEFA Champions League",
"follower_count": 58,
"description": "Club football competition",
"country": "Europe",
},
{
"name": "NASA",
"follower_count": 56,
"description": "Space agency",
"country": "United States",
},
{
"name": "Emma Watson",
"follower_count": 56,
"description": "Actress",
"country": "United Kingdom",
},
{
"name": "Shawn Mendes",
"follower_count": 57,
"description": "Musician",
"country": "Canada",
},
{
"name": "Virat Kohli",
"follower_count": 55,
"description": "Cricketer",
"country": "India",
},
{
"name": "Gigi Hadid",
"follower_count": 54,
"description": "Model",
"country": "United States",
},
{
"name": "Priyanka Chopra Jonas",
"follower_count": 53,
"description": "Actress and musician",
"country": "India",
},
{
"name": "9GAG",
"follower_count": 52,
"description": "Social media platform",
"country": "China",
},
{
"name": "Ronaldinho",
"follower_count": 51,
"description": "Footballer",
"country": "Brasil",
},
{
"name": "Maluma",
"follower_count": 50,
"description": "Musician",
"country": "Colombia",
},
{
"name": "Camila Cabello",
"follower_count": 49,
"description": "Musician",
"country": "Cuba",
},
{
"name": "NBA",
"follower_count": 47,
"description": "Club Basketball Competition",
"country": "United States",
},
]
+78
View File
@@ -0,0 +1,78 @@
"""@file game_logic.py
@brief Pure game functions: draw accounts, describe them, check guesses.
"""
import random
from collections.abc import Sequence
from higher_lower.constants import (
ACCOUNT_TEMPLATE,
CHOICE_A,
CHOICE_B,
KEY_COUNTRY,
KEY_DESCRIPTION,
KEY_NAME,
)
from higher_lower.game_data import Account, data
_DEFAULT_RNG = random.Random()
def get_random_account(
accounts: Sequence[Account] = data,
rng: random.Random | None = None,
) -> Account:
"""@brief Return one random account.
@param accounts The accounts to draw from.
@param rng Random generator; pass a seeded one to make the draw repeatable.
@return A random entry of accounts.
"""
return (rng or _DEFAULT_RNG).choice(accounts)
def pick_pair(
accounts: Sequence[Account] = data,
rng: random.Random | None = None,
) -> tuple[Account, Account]:
"""@brief Return two different random accounts.
@param accounts The accounts to draw from; needs at least two entries.
@param rng Random generator; pass a seeded one to make the draw repeatable.
@return The accounts A and B, never the same entry.
@throws ValueError If accounts has fewer than two entries.
"""
first, second = (rng or _DEFAULT_RNG).sample(accounts, 2)
return first, second
def format_data(account: Account) -> str:
"""@brief Describe an account for the player.
@param account The account to describe.
@return Text such as "Nike, a Sportswear multinational, from United States".
"""
return ACCOUNT_TEMPLATE.format(
name=account[KEY_NAME],
description=account[KEY_DESCRIPTION],
country=account[KEY_COUNTRY],
)
def check_answer(guess: str, a_followers: int, b_followers: int) -> bool:
"""@brief Tell whether the guess names the account with more followers.
Equal follower counts make either choice correct.
@param guess "a" or "b", in any case.
@param a_followers Follower count of account A.
@param b_followers Follower count of account B.
@return True if the guess is right.
@throws ValueError If guess is neither "a" nor "b".
"""
choice = guess.lower()
if choice == CHOICE_A:
return a_followers >= b_followers
if choice == CHOICE_B:
return b_followers >= a_followers
raise ValueError(f"guess must be {CHOICE_A!r} or {CHOICE_B!r}, got {guess!r}")
+161
View File
@@ -0,0 +1,161 @@
"""@file test_game.py
@brief Tests for the game loop, run with scripted input and no real console.
"""
import io
import random
from collections.abc import Callable, Iterable, Sequence
import pytest
from higher_lower import __main__ as entry_point
from higher_lower.art import logo
from higher_lower.game import ask_choice, play_game, show_round
from higher_lower.game_data import Account
ACCOUNTS: list[Account] = [
{
"name": "First",
"follower_count": 30,
"description": "Test one",
"country": "Denmark",
},
{
"name": "Second",
"follower_count": 20,
"description": "Test two",
"country": "Norway",
},
{
"name": "Third",
"follower_count": 10,
"description": "Test three",
"country": "Sweden",
},
]
class FirstRng(random.Random):
"""@brief Random generator that takes the first entries, so a game is fixed."""
def sample( # type: ignore[override]
self, population: Sequence[Account], k: int, **kwargs: object
) -> list[Account]:
"""@brief Return the first k entries."""
return list(population)[:k]
def choice(self, seq: Sequence[Account]) -> Account: # type: ignore[override]
"""@brief Return the first entry."""
return seq[0]
def scripted(answers: Iterable[str]) -> Callable[[str], str]:
"""@brief Make an input function that gives the answers one by one.
@param answers The answers to give.
@return A function with the signature of input().
"""
iterator = iter(answers)
return lambda _prompt: next(iterator)
def run_game(answers: Iterable[str]) -> tuple[int, str]:
"""@brief Play a fixed game and return the score and everything shown.
@param answers The answers the player gives.
@return The final score and the output joined with newlines.
"""
lines: list[str] = []
score = play_game(ACCOUNTS, FirstRng(), scripted(answers), lines.append)
return score, "\n".join(lines)
def test_show_round_shows_both_accounts() -> None:
"""@brief Both accounts are described, with the vs art between them."""
lines: list[str] = []
show_round(ACCOUNTS[0], ACCOUNTS[1], lines.append)
assert lines[0] == "Compare A: First, a Test one, from Denmark."
assert lines[2] == "Against B: Second, a Test two, from Norway."
@pytest.mark.parametrize("typed", ["a", "A", " a "])
def test_ask_choice_accepts_a(typed: str) -> None:
"""@brief A is accepted in any case and with spaces around it."""
assert ask_choice(scripted([typed]), lambda _line: None) == "a"
def test_ask_choice_asks_again_after_an_invalid_answer() -> None:
"""@brief Invalid answers are rejected and the player is asked again."""
lines: list[str] = []
assert ask_choice(scripted(["x", "", "b"]), lines.append) == "b"
assert len(lines) == 2
def test_correct_streak_adds_a_point_per_round() -> None:
"""@brief Each right guess adds one to the score and B becomes the next A."""
score, shown = run_game(["a", "a"])
assert score == 2
assert "Current score: 1." in shown
assert "Compare A: Second" in shown
def test_wrong_first_guess_ends_the_game_with_zero() -> None:
"""@brief A wrong guess ends the game and shows the final score."""
score, shown = run_game(["b"])
assert score == 0
assert "Sorry, that's wrong. Final score: 0" in shown
def test_wrong_guess_after_a_point_keeps_the_score() -> None:
"""@brief The final score is the number of right guesses."""
score, shown = run_game(["a", "b"])
assert score == 1
assert "Final score: 1" in shown
def test_invalid_input_does_not_change_the_round() -> None:
"""@brief An invalid answer is re-asked without ending the game or scoring."""
score, shown = run_game(["x", "a", "a"])
assert score == 2
assert shown.count("Compare A: First") == 1
def test_game_ends_with_a_win_when_the_accounts_run_out() -> None:
"""@brief Using every account ends the game instead of failing."""
score, shown = run_game(["a", "a"])
assert score == 2
assert "You went through every account!" in shown
def test_game_starts_with_the_logo() -> None:
"""@brief The logo is shown first."""
_score, shown = run_game(["b"])
assert shown.startswith(logo)
def test_full_game_with_the_assignment_data_ends() -> None:
"""@brief A game with the real data set ends with a final score."""
lines: list[str] = []
score = play_game(
rng=random.Random(5), input_func=lambda _prompt: "a", output=lines.append
)
assert score >= 0
assert "Final score" in lines[-1]
def test_main_plays_a_game(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
"""@brief main() runs a game on the console."""
monkeypatch.setattr("sys.stdin", io.StringIO("a\n" * 100))
entry_point.main()
assert "Final score" in capsys.readouterr().out
def test_main_leaves_quietly_on_end_of_input(
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
"""@brief Ctrl+D ends the game with a goodbye, not a traceback."""
monkeypatch.setattr("sys.stdin", io.StringIO(""))
entry_point.main()
assert "Goodbye!" in capsys.readouterr().out
+46
View File
@@ -0,0 +1,46 @@
"""@file test_game_data.py
@brief Tests for the assignment data set and art.
"""
from higher_lower.art import logo, vs
from higher_lower.constants import (
KEY_COUNTRY,
KEY_DESCRIPTION,
KEY_FOLLOWER_COUNT,
KEY_NAME,
)
from higher_lower.game_data import data
EXPECTED_KEYS = {KEY_NAME, KEY_FOLLOWER_COUNT, KEY_DESCRIPTION, KEY_COUNTRY}
def test_data_has_fifty_accounts() -> None:
"""@brief The assignment data set has 50 entries."""
assert len(data) == 50
def test_every_account_has_the_required_keys() -> None:
"""@brief Every entry has exactly the four assignment keys."""
assert all(set(account) == EXPECTED_KEYS for account in data)
def test_follower_counts_are_positive_integers() -> None:
"""@brief Follower counts are positive integers."""
assert all(
isinstance(account[KEY_FOLLOWER_COUNT], int) and account[KEY_FOLLOWER_COUNT] > 0
for account in data
)
def test_account_names_are_unique() -> None:
"""@brief No account appears twice."""
names = [account[KEY_NAME] for account in data]
assert len(names) == len(set(names))
def test_art_is_available() -> None:
"""@brief The logo and the vs art keep their shape, including backslashes."""
assert len(logo.splitlines()) == 10
assert "\\" in logo
assert len(vs.splitlines()) == 6
assert "|___/____(_)" in vs
+88
View File
@@ -0,0 +1,88 @@
"""@file test_game_logic.py
@brief Tests for the pure game functions.
"""
import random
import pytest
from higher_lower.constants import KEY_NAME
from higher_lower.game_data import Account, data
from higher_lower.game_logic import (
check_answer,
format_data,
get_random_account,
pick_pair,
)
def test_get_random_account_returns_an_entry_of_the_data() -> None:
"""@brief The drawn account comes from the data set."""
assert get_random_account(rng=random.Random(1)) in data
def test_get_random_account_is_repeatable_with_a_seed() -> None:
"""@brief The same seed draws the same account."""
assert get_random_account(rng=random.Random(7)) == get_random_account(
rng=random.Random(7)
)
def test_pick_pair_never_returns_the_same_account() -> None:
"""@brief A and B are always different entries."""
rng = random.Random(0)
for _ in range(200):
first, second = pick_pair(rng=rng)
assert first[KEY_NAME] != second[KEY_NAME]
def test_pick_pair_with_two_accounts_returns_both() -> None:
"""@brief With only two accounts, the pair holds both of them."""
two = data[:2]
first, second = pick_pair(two, random.Random(3))
assert {first[KEY_NAME], second[KEY_NAME]} == {a[KEY_NAME] for a in two}
def test_pick_pair_with_one_account_raises() -> None:
"""@brief A pair cannot be drawn from a single account."""
with pytest.raises(ValueError):
pick_pair(data[:1])
def test_format_data_describes_the_account() -> None:
"""@brief The description holds name, description and country."""
account: Account = {
"name": "Nike",
"follower_count": 109,
"description": "Sportswear multinational",
"country": "United States",
}
assert (
format_data(account) == "Nike, a Sportswear multinational, from United States"
)
@pytest.mark.parametrize(
("guess", "a_followers", "b_followers", "expected"),
[
("a", 200, 100, True),
("a", 100, 200, False),
("b", 100, 200, True),
("b", 200, 100, False),
("A", 200, 100, True),
("B", 100, 200, True),
("a", 100, 100, True),
("b", 100, 100, True),
],
)
def test_check_answer(
guess: str, a_followers: int, b_followers: int, expected: bool
) -> None:
"""@brief The answer is right for the higher account, and for either on a tie."""
assert check_answer(guess, a_followers, b_followers) is expected
def test_check_answer_rejects_an_unknown_choice() -> None:
"""@brief A guess other than a or b is an error."""
with pytest.raises(ValueError):
check_answer("c", 1, 2)
+16
View File
@@ -0,0 +1,16 @@
"""@file test_smoke.py
@brief Smoke test: the package and its entry point can be imported.
"""
import higher_lower
from higher_lower import __main__ as entry_point
def test_package_imports() -> None:
"""@brief The package imports without side effects."""
assert higher_lower.__doc__
def test_entry_point_is_callable() -> None:
"""@brief The entry point exists; importing it does not start a game."""
assert callable(entry_point.main)