{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2025-08-17T17:31:37.152976+00:00","date_modified":"2025-09-09T21:50:05.810570+00:00","extra_instructor_files":"","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":2466,"instructions":"## Stepping\n\n![A trace table showing the current state of the application as it executes](bakery_projects_stepping_trace_table.png)\n\nPreviously we have seen how tracing through code can be used to debug errors.\nThis is especially helpful in large codebases, for us to understand how the application works.\nIn this chapter, we are going to step through an entire execution of the program.\nAs we do so, we will gain a deeper understanding of its flow and identify potential issues.\n\nDrafter is not an easy library to trace, because it is a graphical application.\nTherefore, you may find it easier to skip using your environments' stepping tools and instead rely on print statements or tracing by hand to understand the flow.\nRecall that print statements can be placed at key points in your code to output the values of variables and the flow of execution.\n\nYou should also not ignore the value of taking out a piece of paper and writing down the values of key variables at each step.\nYou can take advantage of the consistent, structured nature of the program's state to help you keep track of everything in a table.\nWe call this a **Trace Table**.\nThe diagram shown here is one way that you can draw a trace table, with each attribute as a column, and each row representing a new version of the state.\nAnother common style is to use two columns and have a row for each attribute and its value, and then cross out old values as they change and write in new ones to the right.\nWe strongly recommend you try tracing through this application with a piece of paper on hand.\n\n## Getting Started\n\n```python trace-start\nstart_server(State(\"\", [], 0, 0, -1, 0))\n\n# Is turned into...\n\nindex(State(\"\", [], 0, 0, -1, 0))\n\n# To return...\n\nPage(State(\"\", [], 0, 0, -1, 0), [\n    Header(\"Guess the Word\"),\n    \"Level: -1\",\n    Button(\"Start\", \"next_level\")\n])\n```\n\nTo help you see what tracing can look like, we're going to walk through the initial execution of the program step by step.\nThe program begins by importing modules, defining the functions and `State`, and calling the `start_server` function with the initial state.\nThis last line is the start of the interesting execution flow of the program.\nDrafter will take the passed in `State` object and pass it to the `index` function.\nExecution jumps to that function, where the `State` object is used to generate the initial page.\nThe logic of the `index` function is pretty straightforward, generating a `Page` object with the same state and a header, some text, and a button as the content.\nThe only option for the player is to click the \"Start\" button to proceed to the next level, which will take the current state (the same that we started with) and pass it to the `next_level` function.\n\n## First Level\n\n```python trace-next-level\n# The next_level function\n@route\ndef next_level(state: State) -> Page:\n    state.level += 1\n    state.secret = choose_word(state.level)\n    state.wrong = 0\n    if is_game_over(state.level):\n        return game_end(state)\n    return play_level(state)\n\n# Current state and call - Is not literally in the code!\nnext_level(State(\"\", [], 0, 0, -1, 0))\n```\n\nIn the code here, we are showing what the `next_level` function does with the current state when it is called. Note that although this function definition is taken from the source code, the call here is fictional - it is not actually present in the source code at all. Instead, we are merely illustrating how the function would be used in practice, behind the scenes.\n\nThe `next_level` function begins by increasing the `level` attribute by `1`, choosing a `secret` word for the new level, and resetting the `wrong` attempts counter.\nThe first and last of those steps are easy, but in between the execution will jump to the `choose_word` function to select a new word for the player to guess (passing in the current level, which is now `0`).\n\n## `choose_word` function\n\n```python trace-choose-word\ndef choose_word(level: int) -> str:\n    levels = [\"heat\", \"dogs\", \"jazz\"]\n    return levels[level]\n\n# Example of what current call looks like (not literally in code)\nchoose_word(0)\n```\n\nThe `choose_word` function is relatively simple. A 3-element list is created, and then the function returns the element at the specified index (the current level) from that list.\nSince the current level is `0`, the function will return the first element of the list, which is `\"heat\"`.\n\n## Resuming `next_level`\n\n```python trace-next-level-2\ndef next_level(state: State) -> Page:\n    # ...\n    if is_game_over(state.level):\n        return game_end(state)\n    return play_level(state)\n\ndef is_game_over(level: int) -> bool:\n    return level > 4\n\n# Current state:\nState(\"heat\", [], 0, 0, 0, 0)\n```\n\nWe return to `next_level` from where we left off with `choose_word`, finish updating the `secret` and `wrong` attributes, and then have an `if` statement to check if the game is over, passing in the current level.\nAt the bottom of this code, we show what the current state looks like after all those updates: the first attribute is now `\"heat\"` and the level is `0`.\nThis means that we will be passing `0` into the `is_game_over` function.\nThe check there is very simple: it just sees if the level is greater than `4`.\nSince `0` is not greater than `4`, the game is not over, and we will continue to the next level, skipping over the call to `game_end`.\nNow it is time to execute the `play_level` function, with the latest state.\n\n## Calling `play_level`\n\n```python trace-play-level\n@route\ndef play_level(state: State) -> Page:\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# Current State\nState(\"heat\", [], 0, 0, 0, 0)\n```\n\nThe first step of the `play_level` function is to call the `reveal` function with the current secret and guesses, so execution will need to jump there.\n\n## Calling `reveal`\n\n```python trace-reveal\ndef reveal(secret: str, guesses: list[str]) -> str:\n    if secret[0] in guesses:\n        c0 = secret[0]\n    else:\n        c0 = \"_\"\n    if secret[1] in guesses:\n        c1 = secret[1]\n    else:\n        c1 = \"_\"\n    if secret[2] in guesses:\n        c2 = secret[1]\n    else:\n        c2 = \"_\"\n    if secret[3] in guesses:\n        c3 = secret[3]\n    else:\n        c3 = \"_\"\n    return c0 + c1 + c2 + c3\n\n# Example call\nreveal(\"heat\", [])\n```\n\nThe `reveal` function is very long and complicated, compared to other functions in our program.\nJust looking at it, there is a lot of repeated structural code that has a lot of similarities.\nAs we will see, it is a good candidate for refactoring with a helper function.\nFor now, we will faithfully execute the logic using the arguments `\"heat\"` and `[]`.\n\nThe first check asks if the first letter of the secret word, which is `\"h\"`, is in the list of guesses. Since the list of guesses is empty, this check will fail, and `c0` will be set to `\"_\"`. The same logic applies to each of the following checks (for the letters `\"e\"`, `\"a\"`, and `\"t\"`), and they will all result in `\"_\"` being assigned to each of the corresponding variables (`c1`, `c2`, and `c3`).\nAt the end of the function, we return the concatenation of all the variables, which will be `\"____\"`.\nThis string is stored in the variable `so_far` back in the `play_level` function.\n\n## Returning from `play_level`\n\n```python trace-play-level-finish\n# Current state and variables\nstate = State(\"heat\", [], 0, 0, 0, 0)\nso_far = \"____\"\n# Return value of play_level\nPage(\n    State(\"heat\", [], 0, 0, 0, 0),\n    [\n        Header(\"Level 0\"),\n        \"Word: ____\",\n        \"Guesses so far: \",\n        BulletedList([]),\n        \"Guess a letter:\",\n        TextBox(\"guess\"),\n        Button(\"Submit\", \"submit_guess\"),\n    ],\n)\n```\n\nThe `play_level` function returns a `Page` object that represents the current state of the game at the beginning of level 0. The word to guess is hidden, and the player has not made any guesses yet. The UI elements include headers, text boxes, and buttons for user interaction.\nThe user must enter a single letter guess into the text box and submit it to make a guess, which executes the `submit_guess` function with the current state and the guessed letter (in a parameter named `guess`).\n\n## Calling `submit_guess`\n\n```python trace-submit-guess\n@route\ndef submit_guess(state: State, guess: str) -> Page:\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\nThings get a bit tricky when we get to the `submit_guess` function, because it needs to handle a variety of cases depending on the user's input and the current state of the game.\nThis is a good time to read through the entire high-level function, before we start analyzing each individual function call.\nFirst, the user's guess is formatted using the `format_guess` function, which ensures that it is lowercase and stripped of any extra whitespace.\nThen, we check if it is a valid guess, and if not then we call the `play_level` function to make the player guess again.\nIf the guess was valid, we append it to our guesses so far.\nIf the guess not in the secret word, we need to increment the wrong guess counter.\nThen we check if the player has won or lost the game, and return the appropriate page.\nOtherwise, we return to the play level with the updated state and have them continue guessing.\n\nAlthough the `submit_guess` function is complicated and has a lot going on, each part of its logic is broken down into helper functions.\nThese functions can be tested individually to ensure that they work correctly in isolation.\nThen, we can also write tests for the entire route to ensure that we have them working together.\nWe will do so in a few lessons from here, but first we will look at some other aspects of the code.\n\n## Summary\n\n-   Stepping through the code carefully is a valuable debugging technique.\n-   Consider using a debugger to step through your code and inspect variables at each stage.\n-   You can also use print statements to output the values of key variables at key steps.\n-   For more complicated code, consider writing down the values of key variables at each step to help you understand what is happening, in a Trace Table.\n","ip_ranges":"","name":"5A4) Stepping","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  \"header\": \"Stepping\"\n}","starting_code":"","subordinate":false,"tags":[],"type":"reading","url":"bakery_projects_stepping_read","version":4},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":2466,"assignment_version":4,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-20T12:38:50.382823+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-20T12:38:50.382823+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2036722,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-6596eae2-e403-4acf-b9ec-433913459f4d","user_id":2044658,"user_id__email":"","version":0},"success":true}
