{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2025-08-17T17:34:38.924296+00:00","date_modified":"2025-09-09T21:50:05.906310+00:00","extra_instructor_files":"","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":2470,"instructions":"## Completion\n\n-   Exploring Code: The `is_valid_word` is never used anywhere\n-   Reading External Documentation: Using wrong `string` module variable\n-   Variable Names: Fixed bug in `check_if_valid_guess`\n-   Magic Constants: Revealed inconsistency with `choose_word` and `is_game_over`\n-   Decomposition: Found an error in `reveal`\n-   Unit Tests: Handled edge case for `check_if_valid_guess`\n-   Integration Tests: Found bug in `next_level` not resetting state\n\nPreviously, we showed you an incorrect version of the guessing game.\nNow, we can finally see what the full correct version looks like.\nBut first, let us review the mistakes we found along the way.\n\nWhen we first walked through the code, we made a Call diagram to visualize the relationships between functions, which exposed that the `is_valid_word` function is never used anywhere.\nThen, we read over the documentation for the `printable` variable from the `string` module, and discovered that we should have been using the `ascii_letters` variable instead.\nNext, we fixed the variable names in the `check_if_valid_guess` function to be more descriptive, which revealed an error in its logic.\nWe extracted the `LEVELS` list into a constant variable, which removed an inconsistency with the `choose_word` and `is_game_over` functions.\nThen we decomposed the `reveal` function, which helped fix a copy/paste error.\nWriting unit tests helped us find an edge case in `check_if_valid_guess`.\nFinally, we fixed a bug in `next_level` that was not resetting the game state properly by writing more complicated integration tests.\n\n## What Would Be Next?\n\n-   More Tests\n-   More Levels\n-   Better Menus\n\nThe version that we present below has some inadequacies.\nFor one, we don't have many tests for the Drafter routes.\nWe definitely have some nice integration testing, so it's not like we aren't testing that section.\nBut we would most certainly want to write some more tests for those routes (probably using Drafter's automatic test generation features).\n\nThe basic game we created has only three levels, and a very basic interface.\nYou could imagine having better layout, more color, and more engaging graphics.\nYou could also improve parts of the UI, like not showing the \"Level: -1\" at the beginning or the last revealed word (after the game has ended).\n\nThis entire example was designed to be a learning tool, so it may not be fully polished.\nIf you enjoyed it, and have more free time, you could try to improve it further!\nBut for now, your focus should probably be on applying the methods we learned here to make sure you truly understand the code you've written for the 3 Card Poker application.\n\n## The Correct Guessing Game\n\n```python correct-guessing-game\nfrom bakery import assert_equal\nfrom string import ascii_lowercase\nfrom drafter import *\nfrom random import choice\nfrom dataclasses import dataclass\n\n\nLEVELS = [\"heat\", \"dogs\", \"jazz\"]\n\n\ndef choose_word(level: int) -> str:\n    \"\"\"\n    Choose a word from the LEVELS list based on the given level.\n    Must be a valid level index (starting from 0).\n\n    Args:\n        level (int): The level of the word to choose.\n    Returns:\n        str: The chosen word.\n    \"\"\"\n    return LEVELS[level]\n\n\nassert_equal(choose_word(0), \"heat\")\nassert_equal(choose_word(1), \"dogs\")\nassert_equal(choose_word(2), \"jazz\")\n\n\ndef reveal_letter(secret: str, guesses: list[str], index: int) -> str:\n    \"\"\"\n    Reveal a letter in the secret word if it has been guessed.\n    The index must be a valid position in the word.\n\n    Args:\n        secret (str): The secret word.\n        guesses (list[str]): The list of guessed letters.\n        index (int): The index of the letter to reveal.\n\n    Returns:\n        str: The revealed letter or \"_\" if not guessed.\n    \"\"\"\n    if secret[index] in guesses:\n        return secret[index]\n    return \"_\"\n\n\nassert_equal(reveal_letter(\"heat\", [\"h\"], 0), \"h\")\nassert_equal(reveal_letter(\"heat\", [\"h\"], 1), \"_\")\nassert_equal(reveal_letter(\"heat\", [\"h\"], 2), \"_\")\nassert_equal(reveal_letter(\"heat\", [\"h\"], 3), \"_\")\nassert_equal(reveal_letter(\"heat\", [\"x\"], 0), \"_\")\nassert_equal(reveal_letter(\"heat\", [\"t\"], 3), \"t\")\nassert_equal(reveal_letter(\"heat\", [\"r\", \"x\", \"q\"], 0), \"_\")\nassert_equal(reveal_letter(\"heat\", [\"r\", \"x\", \"h\"], 0), \"h\")\nassert_equal(reveal_letter(\"heat\", [\"r\", \"x\", \"h\"], 1), \"_\")\n\n\ndef reveal(secret: str, guesses: list[str]) -> str:\n    \"\"\"\n    Reveal the letters in the secret word that have been guessed.\n\n    Args:\n        secret (str): The secret word.\n        guesses (list[str]): The list of guessed letters.\n\n    Returns:\n        str: The revealed letters or \"_\" for unguessed letters.\n    \"\"\"\n    c0 = reveal_letter(secret, guesses, 0)\n    c1 = reveal_letter(secret, guesses, 1)\n    c2 = reveal_letter(secret, guesses, 2)\n    c3 = reveal_letter(secret, guesses, 3)\n    return c0 + c1 + c2 + c3\n\n\nassert_equal(reveal(\"heat\", [\"h\"]), \"h___\")\nassert_equal(reveal(\"heat\", [\"a\"]), \"__a_\")\nassert_equal(reveal(\"heat\", [\"t\"]), \"___t\")\nassert_equal(reveal(\"heat\", [\"e\", \"a\"]), \"_ea_\")\nassert_equal(reveal(\"heat\", [\"x\"]), \"____\")\nassert_equal(reveal(\"heat\", [\"h\", \"e\", \"a\", \"t\"]), \"heat\")\nassert_equal(reveal(\"heat\", [\"h\", \"e\", \"a\", \"r\"]), \"hea_\")\n\n\ndef is_win(secret: str, guesses: list[str]) -> bool:\n    \"\"\"\n    Check if the player has won the game by guessing all letters in the secret word.\n\n    Args:\n        secret (str): The secret word.\n        guesses (list[str]): The list of guessed letters.\n\n    Returns:\n        bool: True if the player has won, False otherwise.\n    \"\"\"\n    if secret[0] not in guesses:\n        return False\n    if secret[1] not in guesses:\n        return False\n    if secret[2] not in guesses:\n        return False\n    if secret[3] not in guesses:\n        return False\n    return True\n\n\nassert_equal(is_win(\"heat\", [\"h\"]), False)\nassert_equal(is_win(\"heat\", [\"h\", \"e\"]), False)\nassert_equal(is_win(\"heat\", [\"h\", \"e\", \"a\"]), False)\nassert_equal(is_win(\"heat\", [\"h\", \"e\", \"a\", \"t\"]), True)\nassert_equal(is_win(\"heat\", [\"t\", \"e\", \"a\", \"h\"]), True)\nassert_equal(is_win(\"heat\", [\"t\", \"e\", \"a\", \"r\", \"h\"]), True)\n\n\ndef is_loss(wrong: int) -> bool:\n    \"\"\"\n    Check if the player has lost the game by making too many incorrect guesses.\n\n    Args:\n        wrong (int): The number of incorrect guesses made by the player.\n\n    Returns:\n        bool: Whether the player has guessed too many times.\n    \"\"\"\n    return wrong >= 6\n\n\nassert_equal(is_loss(0), False)\nassert_equal(is_loss(5), False)\nassert_equal(is_loss(6), True)\nassert_equal(is_loss(7), True)\n\n\ndef in_secret(secret: str, character: str) -> bool:\n    \"\"\"\n    Check if a character is in the secret word.\n\n    Args:\n        secret (str): The secret word.\n        character (str): The character to check.\n\n    Returns:\n        bool: True if the character is in the secret word, False otherwise.\n    \"\"\"\n    return character in secret\n\n\nassert_equal(in_secret(\"heat\", \"h\"), True)\nassert_equal(in_secret(\"heat\", \"a\"), True)\nassert_equal(in_secret(\"heat\", \"t\"), True)\nassert_equal(in_secret(\"heat\", \"e\"), True)\nassert_equal(in_secret(\"heat\", \"x\"), False)\n\n\ndef format_guess(guess: str) -> str:\n    \"\"\"\n    Format the player's guess by stripping whitespace and converting to lowercase.\n\n    Args:\n        guess (str): The player's guess.\n\n    Returns:\n        str: The formatted guess.\n    \"\"\"\n    return guess.lower().strip()\n\n\nassert_equal(format_guess(\" H \"), \"h\")\nassert_equal(format_guess(\"      A \"), \"a\")\nassert_equal(format_guess(\"t\"), \"t\")\n\n\ndef check_if_valid_guess(guess: str, guesses: list[str]) -> bool:\n    \"\"\"\n    Check if the player's guess is valid.\n\n    Args:\n        guess (str): The player's guess.\n        guesses (list[str]): The list of previously guessed letters.\n\n    Returns:\n        bool: True if the guess is valid, False otherwise.\n    \"\"\"\n    return len(guess) == 1 and guess in ascii_lowercase and guess not in guesses\n\n\nassert_equal(check_if_valid_guess(\"h\", []), True)\nassert_equal(check_if_valid_guess(\"H\", []), False)\nassert_equal(check_if_valid_guess(\"1\", []), False)\nassert_equal(check_if_valid_guess(\"\", []), False)\nassert_equal(check_if_valid_guess(\"hello\", []), False)\nassert_equal(check_if_valid_guess(\"oH\", []), False)\nassert_equal(check_if_valid_guess(\"!\", []), False)\nassert_equal(check_if_valid_guess(\"h\", [\"h\"]), False)\nassert_equal(check_if_valid_guess(\"h\", [\"x\", \"y\", \"h\"]), False)\nassert_equal(check_if_valid_guess(\"z\", [\"x\", \"y\", \"h\"]), True)\n\n\ndef is_game_over(level: int) -> bool:\n    \"\"\"\n    Check if the game is over.\n\n    Args:\n        level (int): The current level of the game.\n\n    Returns:\n        bool: True if the game is over, False otherwise.\n    \"\"\"\n    return level > len(LEVELS) - 1\n\n\nassert_equal(is_game_over(0), False)\nassert_equal(is_game_over(2), False)\nassert_equal(is_game_over(3), True)\n\n\n#### Drafter UI\n\n\n@dataclass\nclass State:\n    \"\"\"\n    Represents the state of the game.\n\n    Attributes:\n        secret (str): The secret word to guess.\n        guesses (list[str]): The list of guessed letters.\n        wrong (int): The number of incorrect guesses made by the player.\n        level (int): The current level of the game.\n        score (int): The player's score.\n    \"\"\"\n\n    secret: str\n    guesses: list[str]\n    wrong: int\n    level: int\n    score: int\n\n\n@route\ndef index(state: State) -> Page:\n    \"\"\"\n    Render the title screen, with a button to start the game.\n\n    Args:\n        state (State): The current state of the game.\n\n    Returns:\n        Page: The rendered index page.\n    \"\"\"\n    return Page(\n        state,\n        [\n            Header(\"Guess the Word\"),\n            \"Level: \" + str(state.level),\n            Button(\"Start\", \"next_level\"),\n        ],\n    )\n\n\nassert_equal(\n    index(State(\"heat\", [], 0, 0, 0)),\n    Page(\n        State(\"heat\", [], 0, 0, 0),\n        [\n            Header(\"Guess the Word\"),\n            \"Level: 0\",\n            Button(\"Start\", \"next_level\"),\n        ],\n    ),\n)\n\n\n@route\ndef next_level(state: State) -> Page:\n    \"\"\"\n    Move to the next level of the game, increasing the level,\n    choosing a new word, and resetting the guesses.\n    If the game is over, then transition to the game over page.\n    Otherwise, render the play level page.\n\n    Args:\n        state (State): The current state of the game.\n\n    Returns:\n        Page: The rendered page for the next level.\n    \"\"\"\n    state.level += 1\n    state.secret = choose_word(state.level)\n    state.guesses = []\n    state.wrong = 0\n    if is_game_over(state.level):\n        return game_end(state)\n    return play_level(state)\n\n\n@route\ndef play_level(state: State) -> Page:\n    \"\"\"\n    Render the play level page, including the current revealed\n    word, the list of guessed letters, and the number of wrong guesses.\n    Provide a text box for the player to enter their guess.\n\n    Args:\n        state (State): The current state of the game.\n\n    Returns:\n        Page: The rendered play level page.\n    \"\"\"\n    so_far = reveal(state.secret, state.guesses)\n    return Page(\n        state,\n        [\n            Header(\"Level \" + str(state.level)),\n            \"Word: \" + so_far,\n            \"Guesses so far: \",\n            BulletedList(state.guesses),\n            \"Guess a letter:\",\n            TextBox(\"guess\"),\n            Button(\"Submit\", \"submit_guess\"),\n        ],\n    )\n\n\n@route\ndef submit_guess(state: State, guess: str) -> Page:\n    \"\"\"\n    Handle the submission of a player's guess.\n    If the guess is valid, remember the guess and check if\n    the player has won the level.\n    If they have won, show the win screen; if they have lost,\n    show the lose screen.\n    Otherwise, keep playing.\n\n    Args:\n        state (State): The current state of the game.\n        guess (str): The player's single letter guess.\n\n    Returns:\n        Page: The rendered page after submitting the guess.\n    \"\"\"\n    guess = format_guess(guess)\n    if not check_if_valid_guess(guess, state.guesses):\n        return play_level(state)\n\n    state.guesses.append(guess)\n    if not in_secret(state.secret, guess):\n        state.wrong += 1\n\n    if is_win(state.secret, state.guesses):\n        return win_level(state)\n\n    if is_loss(state.wrong):\n        return lose_level(state)\n\n    return play_level(state)\n\n\nassert_equal(\n    submit_guess(State(\"heat\", [], 0, 0, 0), \"h\"),\n    Page(\n        State(\"heat\", [\"h\"], 0, 0, 0),\n        [\n            Header(\"Level 0\"),\n            \"Word: h___\",\n            \"Guesses so far: \",\n            BulletedList([\"h\"]),\n            \"Guess a letter:\",\n            TextBox(\"guess\"),\n            Button(\"Submit\", \"submit_guess\"),\n        ],\n    ),\n)\n\n\n@route\ndef win_level(state: State) -> Page:\n    \"\"\"\n    Render the win level page, including the current score and a button to continue.\n\n    Args:\n        state (State): The current state of the game.\n\n    Returns:\n        Page: The rendered win level page.\n    \"\"\"\n    state.score += 1\n    return Page(\n        state,\n        [\n            Header(\"You Win!\"),\n            \"The word was: \" + state.secret,\n            Button(\"Play Again\", \"next_level\"),\n        ],\n    )\n\n\n@route\ndef lose_level(state: State) -> Page:\n    \"\"\"\n    Render the lose level page, including the current score and a button to continue.\n\n    Args:\n        state (State): The current state of the game.\n\n    Returns:\n        Page: The rendered lose level page.\n    \"\"\"\n    return Page(\n        state,\n        [\n            Header(\"You Lost!\"),\n            \"The word was: \" + state.secret,\n            Button(\"Play Again\", \"next_level\"),\n        ],\n    )\n\n\n@route\ndef game_end(state: State) -> Page:\n    \"\"\"\n    Render the game over page, including the final score.\n    \"\"\"\n    return Page(\n        state,\n        [\n            Header(\"Game Over\"),\n            \"The word was: \" + state.secret,\n            \"Your final score is: \" + str(state.score),\n        ],\n    )\n\n\n#### Integration Tests\n\ninitial_state = State(\"\", [], 0, -1, 0)\n\nafter_index = index(initial_state).state\nassert_equal(after_index, State(\"\", [], 0, -1, 0))\n\nlevel_1 = next_level(after_index).state\nassert_equal(level_1, State(\"heat\", [], 0, 0, 0))\n\nfirst_guess = submit_guess(level_1, \"x\").state\nassert_equal(first_guess, State(\"heat\", [\"x\"], 1, 0, 0))\n\nclose_to_heat = State(\"heat\", [\"h\", \"e\", \"a\"], 0, 0, 0)\n\nwon_level = submit_guess(close_to_heat, \"t\").state\nassert_equal(won_level, State(\"heat\", [\"h\", \"e\", \"a\", \"t\"], 0, 0, 1))\n\nlevel_2 = next_level(won_level).state\nassert_equal(level_2, State(\"dogs\", [], 0, 1, 1))\n\n#### Start Game\n\nstart_server(State(\"\", [], 0, -1, 0))\n```\n\nTry the game, and look through this code carefully!","ip_ranges":"","name":"5B4) Completion","on_change":"","on_eval":"","on_run":"","owner_id":1,"owner_id__email":"acbart@udel.edu","points":1,"public":true,"reviewed":false,"sample_submissions":[],"settings":"{\n  \"small_layout\": true,\n  \"disable_timeout\": true,\n  \"header\": \"Completion\"\n}","starting_code":"","subordinate":false,"tags":[],"type":"reading","url":"bakery_projects_completion_read","version":7},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":2470,"assignment_version":7,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-20T12:38:51.247680+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-20T12:38:51.247680+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2036724,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-b7456c0d-bf2b-4084-a0ae-9d5ec4d3bf04","user_id":2044658,"user_id__email":"","version":0},"success":true}
