Workbook¶
Modern application development is usually done through web development. We can use Drafter to make web applications that are easy to write and test. This workbook takes you through developing a few different web applications, piece-by-piece. We provide unit tests that you can use to check that you are building the software correctly.
Important
Read over all the instructions and text as you go. Many of your questions will be answered directly in the instructions! If you just copy/paste code, you will not learn much from this activity.
Bank Account¶
A bank account let’s you withdraw and deposit money. We’ll make a simple bank account application to support this. Preview this website at https://drafter-edu.github.io/examples/bank/.
Step 1: Download bank.py
and put it into the same directory as the cookie clicker game.
The file contains the incomplete State
, five incomplete routes, six correct unit tests, and a call to start_server
.
You might be wondering why there are five routes when the image above only shows three pages.
The reason is because two of the routes (finish_withdraw
and finish_deposit
) are routes that modify
the state and then return the result of calling the index
route.
The diagram below visualizes the relationships between the routes.
flowchart start(("Start")) index["index"] start_withdraw["start_withdraw"] finish_withdraw[/"finish_withdraw"/] start_deposit["start_deposit"] finish_deposit[/"finish_deposit"/] start --> index index -- Withdraw --> start_withdraw start_withdraw -- "Withdraw(amount)" --> finish_withdraw start_withdraw -- Cancel --> index finish_withdraw -.-> index index -- Deposit --> start_deposit start_deposit -- "Deposit(amount)" --> finish_deposit start_deposit -- Cancel --> index finish_deposit -.-> index
To explain a little further:
The
index
route has buttons to go to thestart_withdraw
andstart_deposit
routes.The
start_withdraw
andstart_deposit
have buttons to go to thefinish_withdraw
andfinish_deposit
routes, respectively, and also have a button to go back to theindex
route (“Cancel”). Thestart_withdraw
andstart_deposit
routes also have aTextBox
to input the amount to withdraw or deposit, which is passed as a parameter to thefinish_withdraw
andfinish_deposit
routes.When the
finish_withdraw
andfinish_deposit
routes are called, they modify the state and then return the result of calling theindex
route.
Step 2: Inside the State
class, add a balance
field (an integer).
Step 3: You will need to create five routes:
The
index
route will consume aState
object and return aPage
(like most routes). Thestate
in the returnedPage
will be unchanged. The content of thePage
will be (in order):The text
"Your current balance is:: X"
except instead ofX
it will be thebalance
of theState
object.A
Button
with the text"Withdraw"
that links to astart_withdraw
route.A
Button
with the text"Deposit"
that links to astart_deposit
route.
The
start_withdraw
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will be (in order):The text
"How much would you like to withdraw?"
.A
TextBox()
that will be used to input theamount
to withdraw, initially10
. Make sure you match the name for theTextBox
to the name of the parameter in thefinish_withdraw
route.A
Button
with the text"Withdraw"
that links to afinish_withdraw
route.A
Button
with the text"Cancel"
that links to theindex
route.
The
finish_withdraw
route will consume aState
object and anamount
(anint
). Thebalance
of thestate
in the returnedPage
will be changed to reflect the withdrawal. The function should return the result of calling theindex
function with the modifiedstate
. Note: Use theindex
function inside of this function to avoid code duplication.The
start_deposit
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will be (in order):The text
"How much would you like to deposit?"
.A
TextBox()
that will be used to input theamount
to deposit, initially10
. Make sure you match the name for theTextBox
to the name of the parameter in thefinish_deposit
route.A
Button
with the text"Deposit"
that links to afinish_deposit
route.A
Button
with the text"Cancel"
that links to theindex
route.
The
finish_deposit
route will consume aState
object and anamount
(anint
). Thebalance
of thestate
in the returnedPage
will be changed to reflect the deposit. The function should return the result of calling theindex
function with the modifiedstate
.
Step 4: Run the application and check the tests to see if you have implemented the routes correctly.
This example has shown you how to have multiple pages that link together, with some routes that do not modify the state and some routes that do modify the state. You also have now seen a textbox, which is a way to get input from the user, and how that input can be passed to another route as a parameter in order to update the state.
Simple Adventure Game¶
Now we’ll make a little adventure game with multiple pages that link to each other in a more complicated way, and also an item that you can pick up and use in of the rooms. To make things more exciting, we’ll have images of the screens in the game (generated using ChatGPT). Preview this website at https://drafter-edu.github.io/examples/adventure/.
Step 1: Download the images and the starting file for the game from the following links:
Important
Make sure the files are in the same folder as the python file. You can change the images but make sure they have the same filenames, or the tests won’t run!
Once again, the number of routes and the number of pages do not match.
The diagram below shows the flow of the game. The game starts with the index
route, where the player
enters their name. The player then moves to the small_field
route, where they can choose to go to the
woods
or the cave
. In the woods
route, the player can pick up a key, which will allow them to
unlock the door in the cave
route. If the player does not have the key, they will be unable to unlock
the door and will have to leave the cave. The game ends in the ending
route, where the player finds a
treasure chest and wins the game.
flowchart start(("Start")) index["index"] begin[/"begin"/] small_field["small_field"] woods["woods"] check_has_key{has_key} check_missing_key{not has_key} cave["cave"] take_key[/"take_key"/] ending["ending"] start --> index index -- "Begin(name)" --> begin begin -.-> small_field small_field -- "Woods" --> woods small_field -- "Cave" --> cave woods --- check_missing_key check_missing_key -- "Take key" --> take_key take_key -.-> woods woods -- "Leave" --> small_field check_has_key -- "Unlock door" --> ending cave -.- check_has_key cave -- "Leave" --> small_field
Notice the diamond shaped nodes in the diagram. These are decision nodes. The game will have two different
paths depending on whether the player has the key or not. The Cave only shows the “Unlock door” button if
the player has the key. The Woods only shows the “Take key” button if the player does not have the key.
This means that you will need an if
statement in your route to decide which content to show.
Step 2: Add two fields to the State
class.
The
has_key
field is a booleanThe
name
field is a string
Step 3: Finish implementing the following routes:
The
index
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will be (in order):The text
"Welcome to the adventure! What is your name?"
A
TextBox
that will take the user’sname
(the default value is"Adventurer"
).A
Button
with the text"Begin"
that links to abegin
route.
The
begin
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will have thename
field set to the value of theTextBox
in the previous page. Use thesmall_field
route to return the next page, rather than defining a newPage
object.The
small_field
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will be (in order):The text
"You are NAME."
except replacingNAME
with the value of thename
field in theState
object.The text
"You are in a small field."
The text
"You see paths to the woods and a cave.
A
Button
with the textCave
that links to thecave
route.A
Button
with the textWoods
that links to thewoods
route.An
Image
with the filename"field.png"
.
The
cave
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will depend on whether or not thestate
has thehas_key
field set toTrue
. If it is isTrue
, then the content of the page should be:The text
"You enter the cave."
The text
"You see a locked door."
A
Button
with the text"Unlock door"
that links to theending
route.A
Button
with the text"Leave"
that links to thesmall_field
route.An
Image
with the filename"cave.png"
.
Otherwise, if the
has_key
field isFalse
, then the content of the page should be:The text
"You enter the cave."
The text
"You see a locked door."
A
Button
with the text"Leave"
that links to thesmall_field
route.An
Image
with the filename"cave.png"
.
The
woods
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will depend on whether or not thestate
has thehas_key
field set toTrue
. If it is isTrue
, then the content of the page should be:The text
"You are in the woods."
A
Button
with the text"Leave"
that links to thesmall_field
route.An
Image
with the filename"woods.png"
.
Otherwise, if the
has_key
field isFalse
, then the content of the page should be:The text
"You are in the woods."
The text
"You see a key on the ground."
A
Button
with the text"Take key"
that links to thetake_key
route.A
Button
with the text"Leave"
that links to thesmall_field
route.An
Image
with the filename"woods.png"
.
The
take_key
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will have thehas_key
field set toTrue
. Use thewoods
route to return the next page, rather than defining a newPage
object.The
ending
route will consume aState
object and return aPage
. Thestate
in the returnedPage
will be unchanged. The content of thePage
will be:The text
"You unlock the door.",
The text
"You find a treasure chest."
The text
"You win!"
An
Image
with the filename"victory.png"
.
Step 4: Run the application and check the tests to see if you have implemented the routes correctly.
This application showed you how to make multiple pages that link in more complicated ways, including
some pages that had different content depending on the state of the State
object. You also learned
how to use images in your application.
Store¶
This activity is a little store where you can purchase items for a game. Preview the activity at https://drafter-edu.github.io/examples/store/.
In this example, we’ve already written the state and all the routes. You just need to make all the tests for us!
This is most easily done by just running the site, and then interacting with the shop. As you explore unique pages, tests will be generated at the bottom in the “Combined Page History” section at the bottom of the page. This will only retain unique visits, so you can’t just spam the same page over and over again.
You can also manually create tests by writing them yourself. Ultimately, though, you need to make at least 10 unique tests!
Step 1: Download store.py
and put it into an appropriate directory,
where you placed everything else.
Step 2: Run the server, and then try purchasing things.
Step 3: Write tests for the store. Or rather, just copy the tests that have been generated for you at the bottom of the page.
Step 4: Run the tests with python store.py and make sure they all pass.