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
This commit is contained in:
@@ -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"
|
||||
)
|
||||
@@ -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}"
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
]
|
||||
@@ -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}")
|
||||
Reference in New Issue
Block a user