From ee58fcdc1c9c83b0721022d623e4989591a47987 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Sun, 4 Oct 2026 23:34:48 +0800 Subject: [PATCH 1/2] 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 --- docs/artifact-registry.md | 2 +- docs/milestones/mil-002-game-logic.md | 6 +- docs/sqa/reviews/rc-005-mil-002.md | 45 ++++ docs/sqa/traceability-matrix.md | 4 +- src/higher_lower/art.py | 31 +++ src/higher_lower/constants.py | 28 ++- src/higher_lower/game_data.py | 324 ++++++++++++++++++++++++++ src/higher_lower/game_logic.py | 78 +++++++ tests/test_game_data.py | 46 ++++ tests/test_game_logic.py | 89 +++++++ 10 files changed, 646 insertions(+), 7 deletions(-) create mode 100644 docs/sqa/reviews/rc-005-mil-002.md create mode 100644 src/higher_lower/art.py create mode 100644 src/higher_lower/game_data.py create mode 100644 src/higher_lower/game_logic.py create mode 100644 tests/test_game_data.py create mode 100644 tests/test_game_logic.py diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md index f586abf..8f38899 100644 --- a/docs/artifact-registry.md +++ b/docs/artifact-registry.md @@ -15,7 +15,7 @@ document of a type. `Primary File` may contain a glob (e.g. | SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 | | PP | Project Plan | docs/project-plan.md | 002 | | MIL | Milestone / Gateway | docs/milestones/*.md | 005 | -| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 005 | +| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 006 | | TM | Traceability Matrix | docs/sqa/traceability-matrix.md | 002 | ## Languages diff --git a/docs/milestones/mil-002-game-logic.md b/docs/milestones/mil-002-game-logic.md index 25bce63..647c4ae 100644 --- a/docs/milestones/mil-002-game-logic.md +++ b/docs/milestones/mil-002-game-logic.md @@ -4,12 +4,13 @@ | Key | Value | | --- | --- | | ID | MIL-002 | -| CrossReference | [BC-001], [PP-001] | +| CrossReference | [BC-001], [PP-001], [RC-005] | ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] | +| 2026-10-04 | Deprecated | Jens Tirsvad Nielsen | S01 | Initial version | [ddfe96f] | +| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-005 | pending | --- @@ -72,3 +73,4 @@ The assignment data set and art as modules, constants in `constants.py`, and the [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 diff --git a/docs/sqa/reviews/rc-005-mil-002.md b/docs/sqa/reviews/rc-005-mil-002.md new file mode 100644 index 0000000..92baa0b --- /dev/null +++ b/docs/sqa/reviews/rc-005-mil-002.md @@ -0,0 +1,45 @@ +# 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 | pending | + +--- + +## 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 diff --git a/docs/sqa/traceability-matrix.md b/docs/sqa/traceability-matrix.md index 4c2dd00..ffcc70b 100644 --- a/docs/sqa/traceability-matrix.md +++ b/docs/sqa/traceability-matrix.md @@ -10,6 +10,7 @@ | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | | 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] | +| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-005 review of MIL-002 | pending | --- @@ -26,7 +27,7 @@ target is measurable. A row is added or updated whenever an artifact instance is | [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-002] | MIL | [BC-001], [PP-001], [MIL-001] | - | [RC-005] | | [MIL-003] | MIL | [BC-001], [PP-001] | - | - | | [MIL-004] | MIL | [BC-001], [PP-001] | - | - | @@ -49,3 +50,4 @@ target is measurable. A row is added or updated whenever an artifact instance is [RC-003]: ./reviews/rc-003-project-plan.md [RC-004]: ./reviews/rc-004-mil-001.md [a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853 +[RC-005]: ./reviews/rc-005-mil-002.md diff --git a/src/higher_lower/art.py b/src/higher_lower/art.py new file mode 100644 index 0000000..bd09c03 --- /dev/null +++ b/src/higher_lower/art.py @@ -0,0 +1,31 @@ +"""@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" +) diff --git a/src/higher_lower/constants.py b/src/higher_lower/constants.py index 539ff96..22e9ffc 100644 --- a/src/higher_lower/constants.py +++ b/src/higher_lower/constants.py @@ -1,6 +1,28 @@ """@file constants.py @brief Constants shared by the game modules. - -Constants are added here as the game logic (MIL-002) and the game loop -(MIL-003) need them. """ + +## Keys of an account dictionary. +KEY_NAME = "name" +KEY_FOLLOWER_COUNT = "follower_count" +KEY_DESCRIPTION = "description" +KEY_COUNTRY = "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}" diff --git a/src/higher_lower/game_data.py b/src/higher_lower/game_data.py new file mode 100644 index 0000000..b916918 --- /dev/null +++ b/src/higher_lower/game_data.py @@ -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", + }, +] diff --git a/src/higher_lower/game_logic.py b/src/higher_lower/game_logic.py new file mode 100644 index 0000000..c28223d --- /dev/null +++ b/src/higher_lower/game_logic.py @@ -0,0 +1,78 @@ +"""@file game_logic.py +@brief Pure functions of the game: drawing accounts, describing them and checking guesses. +""" + +import random +from collections.abc import Sequence + +from higher_lower.constants import ( + ACCOUNT_TEMPLATE, + CHOICE_A, + CHOICE_B, + KEY_DESCRIPTION, + KEY_NAME, + KEY_COUNTRY, +) +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}") diff --git a/tests/test_game_data.py b/tests/test_game_data.py new file mode 100644 index 0000000..8704462 --- /dev/null +++ b/tests/test_game_data.py @@ -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 diff --git a/tests/test_game_logic.py b/tests/test_game_logic.py new file mode 100644 index 0000000..b153b65 --- /dev/null +++ b/tests/test_game_logic.py @@ -0,0 +1,89 @@ +"""@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) From 037900df94811e5e388c9b32269c9ef7e91cd01e Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Sun, 4 Oct 2026 23:35:18 +0800 Subject: [PATCH 2/2] Resolve pending commit links for MIL-002 acceptance Co-Authored-By: Claude Sonnet 5.5 --- docs/milestones/mil-002-game-logic.md | 3 ++- docs/sqa/reviews/rc-005-mil-002.md | 3 ++- docs/sqa/traceability-matrix.md | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/milestones/mil-002-game-logic.md b/docs/milestones/mil-002-game-logic.md index 647c4ae..febd056 100644 --- a/docs/milestones/mil-002-game-logic.md +++ b/docs/milestones/mil-002-game-logic.md @@ -10,7 +10,7 @@ | 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 | pending | +| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Accepted after review RC-005 | [ee58fcd] | --- @@ -74,3 +74,4 @@ The assignment data set and art as modules, constants in `constants.py`, and the [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 diff --git a/docs/sqa/reviews/rc-005-mil-002.md b/docs/sqa/reviews/rc-005-mil-002.md index 92baa0b..b2c95fb 100644 --- a/docs/sqa/reviews/rc-005-mil-002.md +++ b/docs/sqa/reviews/rc-005-mil-002.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-04 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [ee58fcd] | --- @@ -43,3 +43,4 @@ Go — all criteria pass. Code under `src/` and `tests/` for MIL-002 may start o [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 diff --git a/docs/sqa/traceability-matrix.md b/docs/sqa/traceability-matrix.md index ffcc70b..2c57e43 100644 --- a/docs/sqa/traceability-matrix.md +++ b/docs/sqa/traceability-matrix.md @@ -10,7 +10,7 @@ | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | | 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Initial version | [a1ff735] | -| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-005 review of MIL-002 | pending | +| 2026-10-04 | Proposed | Jens Tirsvad Nielsen | S01 | Added RC-005 review of MIL-002 | [ee58fcd] | --- @@ -51,3 +51,4 @@ target is measurable. A row is added or updated whenever an artifact instance is [RC-004]: ./reviews/rc-004-mil-001.md [a1ff735]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/a1ff73580fdcfad5bbb3854298fdfa0c16ada853 [RC-005]: ./reviews/rc-005-mil-002.md +[ee58fcd]: https://git.tirsystem.com/Tirsvad-Udemy-100_days_of_code/014-higher_lower/commit/ee58fcdc1c9c83b0721022d623e4989591a47987