Project 2: Tumbleweed Town
Saddle up! You’re about to wander into an abandoned town …

Support
This can definitely be a hard project. In addition to the Design Check and the follow-up personal hours, feel free to come to TA Hours, or post on Campuswire for help with the project.
Y’all got this! :)
Similarly to project 1, there will be a design check phase and an implementation phase. Details on signing up for a design check can be found in the “Deadline 1: Design Check” section below.
Design check signup deadline: Monday, Mar 9 11:59pm
Last day for design checks: Wednesday, April 1
Last day for personal check ins: Wednesday, April 8
Final handin deadline: Thursday, April 9, 9pm (in any time zone, as per Kathi’s updates about due dates here)
A few important notes about the design check deadlines:
- If none of the remaining times on the Google form work for you and your partner, please email the HTAs before the form closes.
- Please make sure to sign up for a design check on time. If you anticipate having trouble making this deadline for whatever reason, email the HTAs immediately.
- Once the the Google form has closed (after the signup deadline), please email the HTAs to reschedule your design check. Make sure you do so at least 12 hours before your design check so we can ensure that someone can get back to you in time. Requests past 12 hours in advance will not be honored, except for extenuating circumstances.
- Please arrive to your design check on time. Arriving over 10 minutes late, or missing your design check entirely, will result in a 10% point deduction for your design check.
NEW (4/1/202): To maximize the amount of one-on-one TA time with students, and reduce load at general code hours, students will be allowed to have two personal check-ins with their projects TA instead of one. Students wishing to have a second check-in should reach out to their projects TA (the TA who did their design check and first check in) to schedule a second one after having their first check-in.
Summary

If you’re having trouble viewing the video above, you can find it here.
Note that the images used in our maze will differ from the ones used above.
You will be creating a maze game! The user will navigate a saddle-strappin’ character around an abandoned town using key presses and mouse clicks. The maze will contain some special items, such as cowboy hat to completely restore the character’s strength (shown here as the yellow bar on the right), cowboy boots to give them a small boost, snakes to deplete their strength, or horses to allow the cowboys to teleport. The goal: help the cowboy find the corral hidden at the end of the maze.
Note: the video above shows the maze functionality with different special items. The cowboy boots correspond to the bananas, the cowboy hats corresponds to the apples, the snake corresponds to the tomato, and the horses correspond to the circles (portals)
The maze will be populated based on a configuration Google Sheet which you can modify to make your own maze. The game will be played using a Pyret reactor
. The reactor will run on a GameState
datatype that you define; the GameState
data will evolve as the game progresses.
For the design check, you will be laying out the datatypes that will capture the necessary game information, and thinking through the tasks required to make the game run. Unlike Project 1, a lot of the work for this project will happen for the design check, including writing some of the code.
For the final handin, you will be implementing the functions and reactor required to make the game run.
After all of the design checks are done, we will provide our solution to the design check phase, which you can use if you wish in producing your implementation for the final handin.
Project Learning Goals
- Creating and using your own datatypes.
- Using recursion to process various types of lists.
- Breaking down complex goals into discrete, solveable tasks.
- Using tables for code configuration rather than as datasets.
Files to Submit on Gradescope
pen-and-paper.pdf
tumbleweed-town.arr
, which will contain the implemented function get-item
and datatypes for GameState
and other necessary datatypes
The Project
A Maze Game!
You will make a maze. The maze we provide in the Google Sheet is 35 squares per “row” and 19 squares per “column” (19x35), but you can add or remove rows, columns, and items as you please and make the maze your own! The goal of the game is for the (human) player to navigate the cowboy to the corral at the exit.
The maze will also have some number of special items scattered throughout that the cowboy can collect and use. The items will be populated based on another Google Sheet.
The entire maze should be surrounded by one layer of walls (except at the end location), so you don’t have to deal with the player moving outside of the maze and it is clear to the player where they need to navigate to in order to complete the game.
For the project, you will make a) the code to “render” (draw) the maze, its items, and the cowboy character, and b) the logic required to allow a (human) player to use the mouse and keyboard to navigate the player around the maze.
The Player and Character
The current position of the player in the maze is represented with an image of the cowboy character.
The player moves the cowboy using the W
(up), A
(left), S
(down), and D
(right) keys. The player cannot walk through walls of the maze, but can move through empty space.
As an optional add on, you have the choice to make the character turn based on the direction they are walking (as demonstrated in the video at the top of the assignment). Implementing this feature will give you extra practice with datatypes and code design (but again it is entirely optional) and will add +1 point to make up any point losses in functionality. To accomplish this, we’re giving you access to the images cowboy-up.png
, cowboy-down.png
, cowboy-left.png
, and cowboy-right.png
for the various orientations of the cowboy.
Items
You will implement either gadgets or portals into the game. You only need to implement one of these to get full credit. You may do both if you want, but you will not get extra credit. However, if you do both, it will be fun and we will grade the two implementations individually—the grade that is higher will be your grade for the project.
There can be an arbitrary number of these items placed on the maze. Items are picked up when the player moves into their cells; gadgets are immediately consumed, and portals are “held on to” until use. If multiple portals are collected, the cowboy will be able to “hold on to” all of them - and will only lose one each time he uses a portal.
Either option will be difficult in its own way; we do not think either is significantly easier than the other.
If you want to implement a different item (with different behavior from these), you must email the HTAs for approval before your design check.
-
Gadgets: If you implement gadgets, your character needs to have stamina. Stamina is a measure of energy often used in video games. In the Tumbleweed Town, your character’s stamina depletes by moving. If your character runs out of stamina, it’s game over.
There are three things you can encounter in the maze: a cowboy hat, cowboy boots and a snake.
- Finding a cowboy hat
replenishes the cowboy’s stamina to its original value.
- Finding cowboy boots
heals the cowboy for some fixed amount of stamina.
- Stepping on a snake
reduces the cowboy’s stamina to some low, fixed amount. If the current stamina is lower than this fixed amount, then the game should end.
For example, if the character’s stamina is 20 and they move onto a snake, they may move down to 7 stamina. Then moving onto cowboy boots may heal them to 7 + 8 = 15 stamina, and finding a cowboy hat will heal them to their original stamina (say, 30). Normal movement reduces stamina by 1 per cell.
There should be a visual indication of the character’s stamina (in our sample, it’s the yellow bar on the right, but you can handle this differently if you wish).
-
Portals: Your character can carry one or more portals (you may choose the limit), but starts with no portals. When the character has a portal (a horse) and the user clicks some square on the screen, the character will move there as long as the cell is within some reasonable range of the character’s current location. You can choose this range, but make sure it doesn’t allow the user to easily teleport to the end of the maze!
Each portal can be used only once. Your game screen should have some visual indication of how many portals the player has (simply a number using the Image library’s text
function is fine).
To compute how far the user is trying to move the character, you can use the distance formula, with the change in the x-coordinate Δx and the change in the y-coordinate Δy:
D=√Δx2+Δy2
For example, if the player is at (3,8) and wants to move to (6,4), D=√(6−3)2+(4−8)2=√9+16=5. This is a “birds-eye view” of distance.
In your game, if a player with a portal clicks on a cell, and the distance between the current and clicked cells is within the threshold you choose, the character moves to the new cell and uses up a portal. If the distance is larger than the threshold, nothing changes (the character stays in the same place and keeps the portal for another try).
Starting, Winning, and Losing
The player/character will start at a location of your choosing. The information of where they start should be hardcoded through constants in your code.
The player wins the game upon reaching the cattle corral at an exit location that you set. This information should also be hardcoded through constants. We recommend having the end location on one of the edges of the maze so that it is clear when playing the game where the exit is located. If your exit is not on the edge of the maze, you’ll have find some other way to make it clear to the player where they have to go.
The player loses the game if they run out of stamina. If you don’t implement gadgets, the player doesn’t have to worry about losing stamina.
Upon winning or losing, the game can simply close. You may also choose to somehow inform the player that they have won or lost (as was done in Lab 2), but this is not a requirement.
A video-based overview of the spreadsheets and how the gameplay works are in this video.
Implementation Details
Configuring the Maze (in Google Sheets)
The game will be populated based on your copy of a spreadsheet:
- Gadgets spreadsheet: link
- Portals spreadsheet: link
- Both items spreadsheet: link
Use the spreadsheet appropriate for your item implementation. Feel free to modify the maze layouts and item populations! See #2 in the “Support Code” section (below) for how to load the sheets.
Each spreadsheet has two sheets: maze-layout
and items
.
-
The maze-layout
sheet determines which parts of the maze are walls and which are blank and therefore where the player can walk. “x” corresponds to a wall, and “o” corresponds to a floor.
-
The items
sheet contains a list of items that will be placed on the maze. The x column is the distance from the left side of the screen, and the y column is the distance from the top of the screen. You will need to edit this sheet to put items in the appropriate places. Check out the images in this folder.
Note 1: Positions are zero-indexed; the row "Cowboy hat", 1, 7, "cowboy-hat.png"
corresponds to an
being placed in the 2nd column and the 8th row.
Note 2: You do not need to do any error checking for items being placed in invalid locations (like on walls or off the maze).
Name |
Image |
Maze Sheet |
 |
Items Shet |
 |
Using Reactors to Create Games
Remember the lasso and bull program? The position of the lasso was captured with a Number
: on each tick of time, the on-tick
function produced a new Number
value. It also had a function to draw the lasso based on the current Number
.
All animations in Pyret have a similar structure: there is a datatype that captures the current information for the animation (positions, colors, attributes of characters, etc), and a separate function that takes a value of that datatype and draws it. Pyret calls these functions alternately: draw the current information, update it, draw it, update it, draw it, and so on.
For games based on user interaction, we want the information to update when the user presses keys (or the mouse). Rather than using the on-tick
from the ghost program, we use a different reactor function called on-key
, which takes the game information and name of key pressed (as a string) and produces new game information. In this case, Pyret alternates between calling on-key
, to-draw
, on-key
, to-draw
, etc.
Thus, to make a game, you need a datatype that captures the information that changes as the game runs, you need a function that computes a revised datatype based on keys pressed, and you need a function that draws the current information. Pyret does the work of calling these functions to run your game.
You have seen the reactor properties init
, to-draw
, on-key
, stop-when
, and close-on-stop
before. If you are implementing portals, you will also need to use the on-mouse
function, which works similar to the on-key
function; check out the on-mouse
documentation for more. (for this, Pyret adds on-mouse
to the rotation of functions it calls.)
You can refresh yourself on reactor properties here. You may also want to look at your code or the solutions to Lab 2, which was on reactors.
Support Code
You will be given support code that provides four functions:
-
load-texture :: (String -> Image)
This function will load the images you can use to build up your game. Given a name of a file from the project 2 image directory, load-texture
will return that image. The textures (walls, empty sqaures) are 30x30 (pixels), and everything else (players, cowboy boots, portals, etc.) are 24x24 (pixels).
Examples:
- load-texture("cowboy-hat.png")
returns this Image
: 
- load-texture("horse.png")
returns 
-
load-items :: (String -> Table)
This function, given the ID of a spreadsheet, loads the "items"
sheet into a table with columns "name"
, "x"
, "y"
, and "url"
.
-
load-maze :: (String -> List<List<String>>)
and
load-maze-n :: (String, Number -> List<List<String>>)
This function, given the ID of a spreadsheet, loads the maze into a list of lists of the letters in the maze-layout
sheet. If you change the number of columns, you must use load-maze-n
which is the same as load-maze
except it has a second parameter which is the number of columns to load.
The resulting list looks like this:

-
get-maze-index :: (Number -> Number)
(portals only)
The reactor on-mouse
function takes in x and y coordinates that do not directly correspond to which row and column in the maze the user clicked on. Because of this, get-maze-index
is a function that takes in the coordinate that is input to on-mouse
and converts it to a game grid coordinate. For example, if the x
-coordinate clicked is 334, get-maze-index(334)
returns 11 (for the 12th column).
Deadline 1: Design Check
Signup
-
Once you have a partner, sign up for a design check on this Google form by the signup deadline. You may not work with your Project 1 partner. Only one partner should fill out the form, and it is very important that your login information is correct.
-
Once you fill out the form, you will get two confirmation emails:
- One, from Google forms, that will send you a copy of your responses. Only the person who filled out the form will get this email.
- An email from the staff, saying that your slot has either been confirmed or is now unavailable to you. Both partners should get this email. If you don’t get this email within a minute or two, you might have typed your logins incorrectly.
-
If you need help finding a partner, check out this Campuswire post, which was released earlier this week. If you are still having trouble finding a partner by Sunday, email the HTAs, and people will be paired up in the order that they email. This will ensure that you can fill out the form on time.
Requirements
For design check, you will hand in your tumbleweed-town.arr
and pen-and-paper.pdf
file.
There are two types of questions for the Design Check: “Pen & Paper” and “Coding”. For the Pen & Paper questions, come to your design check prepared to talk about your answers in thepen-and-paper.pdf
file. For the Coding questions, write the code in tumbleweed-town.arr
. Submit both files to Gradescope at least two hours before your design check starts.
We recommend doing the questions in order as much as possible, as each question will help you think about the next.
Basic Project Setup
- First, decide which item you will be implementing (gadgets or portals (or both))!
If you want to implement another item, it must be approved by an HTA before the design check.
- Code Setup:
- a) Make a copy of a configuration spreadsheet. In Google Sheets, you can do this by clicking
File > Make a Copy...
b) Share the spreadsheet with your partner, and
c) Give “Anyone with the link” the ability to view the spreadsheet (you have to go into the Advanced sharing menu to do this).
- Make a new Pyret file called
tumbleweed-town.arr
.
a) Copy and paste the code in the block below.
b) Replace “INSERT SPREADSHEET ID HERE” in the code you just pasted in with the ID of your spreadsheet
c) Make sure it runs. Then look at maze-data
and item-data
to get an idea of what they look like.
To find the ID of your spreadsheet, open the sheet and check the URL for the highlighted portion below:

# load the project support code
include shared-gdrive(
"table-functions.arr",
"14jG4wvAMhjJue1-EmY-u9hX4UwmPHCO8")
include shared-gdrive("project-2-support.arr",
"17JVXvE2leSvgBQ-S11Ittt4HY3E_G8dt")
include image
include tables
include reactors
import lists as L
ssid = "INSERT SPREADSHEET ID HERE"
maze-data = load-maze(ssid)
item-data = load-items(ssid)
#| un-comment when ready!
maze-game =
reactor:
init : init-state,
to-draw : draw-game,
# on-mouse : mouse-click, # portals only
on-key : key-pressed,
# stop-when : game-complete, # [up to you]
# close-when-stop : true, # [up to you]
title : "Tumbleweed Town!" # [you can change this title]
end
|#
Design Check Questions!
-
Pen & Paper: In Lab 2, you made a reactor where a lasso moved towards a bull. We can divide the components of the animation into two categories: “Dynamic” (changing throughout the animation) and “Static” (does not change throughout the animation). The position of the bull, the shape and color of the background, and the shape and the color of the bull and lasso were things that did not change as we interacted with our reactor. The lasso position did change, both on-tick (over time) and on-key (when we pressed certain keys on our keyboard). This is represented in the chart below:
Static |
Dynamic |
Bull position |
Lasso position |
Background |
|
Bull and Lasso shapes and colors |
|
Look at the gameplay video at the top of the document. Make a similar list or chart of the static and dynamic components of the maze game.
-
Coding: Create the datatypes you will need for this game. Keep in mind what you wrote for question 1: only “dynamic” information should be components of the shifting game state.(For reference, you can look at how we created the Posn datatype in Lab 2.)
- Create a datatype called
GameState
for the game state. Consider what you want to keep track of in the game: position, items, stamina, or whatever you need.
- You’ll probably find that you’ll need datatypes other than the
GameState
. Define those as well. We’ll be expecting you to provide at least one more additional datatype (other than the Posn, if you choose to use it), but most implementations make use of more than this. This is also good style!
- For each new datatype you make, make sure to include some examples.
Put the data types and examples into tumbleweed-town.arr
.
-
Pen & Paper: The reactor to-draw
function takes in the current GameState
value and outputs an Image
showing the corresponding frame of the game. Make a list of the tasks required to generate the maze image (3-5 one sentence bullet points). We suggest you spend a decent amount of time on this question: the more thorough you are, the more your TA will be able to help you at design check and the easier your jobs will be for week 2 of the project.
-
Pen & Paper:
(1) If you are working on the gadgets version of the game: The reactor on-key
function takes in the current GameState
value and outputs a new GameState
value that reflects changes based on which key has been pressed. Pick one of the four keys (W
, A
, S
, D
) and write out the tasks (3-5 one sentence bullet points) needed to compute the new GameState
based on the current GameState
.
(2) If you are working on the portals version of the game: The reactor on-mouse
function takes in the current GameState
value, the x
and y
value of the position of the mouse event (in the reactor), and a String
that represents the name of the mouse event. Write out the tasks needed to compute the new GameState
based on the current GameState
.
Again, work here should save you considerable time in week 2.
-
Pen & Paper:
(1) If you are working on the gadgets version of the game: Write one simple example of the function named key-pressed
you will use as on-key
in the reactor. Consider how the game state will change.
(2) If you are working on the portals version of the game: Write one simple example of the function named use-portal
you will use as on-mouse
in the reactor.
Be sure to provide (a) what are the inputs to this function, (b) what is the output of the function, (c) what the function is doing, and (d) an example, something along the lines of:
key-pressed(..., "w") is ...
or
use-portal(..., 35, 70, "button-down") is ...
-
Coding: Write a function get-item(l :: List<List<String>>, x :: Number, y :: Number) -> String
in Pyret that takes in a List of Lists, and get the xth element from the yth list.
-
Pen & Paper: Write down a list of 3-5 edge cases that you can think of in the program and how you plan to tackle these edge cases. Some questions to think about: What are the edge cases of the keys being pressed/communicated to the program? What are the edge cases that could happen with the stamina logic?
NEW (3/20/20): The design check solutions are posted in Canvas. Please don’t look at them until after you have completed your design check (we can see who is accessing Canvas files). If you are done with the design check, you are welcome to look.
In this file, we have also provided our implementation of the function update-stamina
. Feel free to use this function in your code, or use it as a template for your other functions.
Deadline 2: Final Handin
Work on One Feature at a Time
The key to tackling a larger assignment like this is to build the features up gradually. For example, get the game working on just basic functionality before adding more advanced features. What might that mean here?
- Implement a version with just the walls (no gadgets or portals), such that the player can move around the maze while respecting walls.
- Read in the items (cowboy hats, horses, etc) from the Google Sheet, store them in your data structure(s), and make them show up when you draw the GameState.
- Get the items to disappear when a player visits their cells. For example, when a player moves to a cell with a snake, the snake disappears and the stamina decreases.
- Make the game handle the items (modifying stamina for gadgets or teleporting for portals).
These phases correspond to the grading levels for the project (see the Grading section below). You can pass the project even if you don’t get beyond phase 1.
We strongly recommend saving a copy of your file after you get each stage working, just in case you need to get back to your last stable version (practicing programmers do this all the time).
Grading
Testing
Since there are many functions that will be written for this project, we will only be requiring minimal testing for all functions that are not critical functions. In an ideal world, you would thoroughly test all functions; however, we would rather see you test one or two functions really well (to show you understand the concept) and others partially, rather than only doing partial testing or no testing at all for all functions, especially critical functions.
Because of this, we will look for the following:
(1) minimal testing (>= 2 test cases) for helper functions that output things other than an Image, and
(2) your thorough testing (try to hit as many tests of edge cases as possible!) for the function that moves the character (the one you use as on-key
, or on-mouse
). Test this function thoroughly, covering as many cases as you can.
Regarding your other functions, write as many tests as you think you need to be comfortable with it working - but include at least 2.
Your design, testing, and clarity grades will not be based on your functionality grade. Functionality will be weighted more heavily in your final grade for this project in particular. Here is what we’re looking at when grading functionality:
Minimum Functionality:
- A character that moves in response to key presses.
- Player cannot pass through walls.
- Game stops/ends when the character reaches a certain destination.
Mid-tier functionality:
- All above.
- Gadgets or portals (horses) are rendered on the maze.
- If doing gadgets: your character must have stamina that depletes upon moving. The gadgets do not need to be functional for mid-tier functionality. Upon reaching 0 stamina, the game should end.
- If doing portals: upon picking up a portal, your character can teleport. There does not need to be a limit of the portal range or the number of times the player can teleport for mid-tier functionality.
For full functionality:
- All above.
- If doing gadgets: cowboy hat bring the character to full stamina; snakes brings the character to a low stamina; cowboy boots heal for some amount; items disappear when walked over.
- If doing portals: there is a limit to how far the character can teleport and how many times a portal can be used.
You can use the categories above as a roadmap as you’re implementing, or as a gauge of how far along you are!
NEW (3/30/2020): Correct implementation of minimum functionality will constitute a passing grade on this project.
Reflection
This project was designed to give you practice with organizing and manipulating structured data. Answer the following questions about these topics in a block comment at the bottom of tumbleweed-town.arr
.
- Our support code represented the maze layout (walls vs floors) as a list-of-lists of strings, rather than a table. What were the advantages and disadvantages of this choice?
- In this project, you worked with the maze in three formats: the Google Sheet with the configuration of walls, the list-of-lists version, and the image itself. For each representation, briefly describe what it is good and bad for.
- Describe one key insight that each partner gained about programming or data organization from working on this project.
- Describe one or two misconceptions or mistakes that you had to work through while doing the project.
- State one or two followup questions that you have about programming or data organization after working on this project.
Note on Late Days
If you plan on using one or more of your late days on this project, note that you are only allowed to use as many late days as the partner with the least number of late days remaining has. In other words, if one partner has 1 late day left and the other has two late days left, you are only allowed to use 1 late day on this project. As always, the 3-day maximum policy applies.
Campuswire and Feedback
- Campuswire can be your friend for this Project!
- Have feedback for the class or for this project? Submit your feedback here.
Project 2: Tumbleweed Town
Saddle up! You’re about to wander into an abandoned town …
Support
This can definitely be a hard project. In addition to the Design Check and the follow-up personal hours, feel free to come to TA Hours, or post on Campuswire for help with the project.
Y’all got this! :)
Due Date Information
Similarly to project 1, there will be a design check phase and an implementation phase. Details on signing up for a design check can be found in the “Deadline 1: Design Check” section below.
Design check signup deadline: Monday, Mar 9 11:59pm
Last day for design checks: Wednesday, April 1
Last day for personal check ins: Wednesday, April 8
Final handin deadline: Thursday, April 9, 9pm (in any time zone, as per Kathi’s updates about due dates here)
A few important notes about the design check deadlines:
NEW (4/1/202): To maximize the amount of one-on-one TA time with students, and reduce load at general code hours, students will be allowed to have two personal check-ins with their projects TA instead of one. Students wishing to have a second check-in should reach out to their projects TA (the TA who did their design check and first check in) to schedule a second one after having their first check-in.
Summary
If you’re having trouble viewing the video above, you can find it here.
Note that the images used in our maze will differ from the ones used above.
You will be creating a maze game! The user will navigate a saddle-strappin’ character around an abandoned town using key presses and mouse clicks. The maze will contain some special items, such as cowboy hat to completely restore the character’s strength (shown here as the yellow bar on the right), cowboy boots to give them a small boost, snakes to deplete their strength, or horses to allow the cowboys to teleport. The goal: help the cowboy find the corral hidden at the end of the maze.
Note: the video above shows the maze functionality with different special items. The cowboy boots correspond to the bananas, the cowboy hats corresponds to the apples, the snake corresponds to the tomato, and the horses correspond to the circles (portals)
The maze will be populated based on a configuration Google Sheet which you can modify to make your own maze. The game will be played using a Pyret
reactor
. The reactor will run on aGameState
datatype that you define; theGameState
data will evolve as the game progresses.For the design check, you will be laying out the datatypes that will capture the necessary game information, and thinking through the tasks required to make the game run. Unlike Project 1, a lot of the work for this project will happen for the design check, including writing some of the code.
For the final handin, you will be implementing the functions and reactor required to make the game run.
After all of the design checks are done, we will provide our solution to the design check phase, which you can use if you wish in producing your implementation for the final handin.
Project Learning Goals
Files to Submit on Gradescope
pen-and-paper.pdf
tumbleweed-town.arr
, which will contain the implemented functionget-item
and datatypes forGameState
and other necessary datatypesThe Project
A Maze Game!
You will make a maze. The maze we provide in the Google Sheet is 35 squares per “row” and 19 squares per “column” (19x35), but you can add or remove rows, columns, and items as you please and make the maze your own! The goal of the game is for the (human) player to navigate the cowboy to the corral at the exit.
The maze will also have some number of special items scattered throughout that the cowboy can collect and use. The items will be populated based on another Google Sheet.
The entire maze should be surrounded by one layer of walls (except at the end location), so you don’t have to deal with the player moving outside of the maze and it is clear to the player where they need to navigate to in order to complete the game.
For the project, you will make a) the code to “render” (draw) the maze, its items, and the cowboy character, and b) the logic required to allow a (human) player to use the mouse and keyboard to navigate the player around the maze.
The Player and Character
The current position of the player in the maze is represented with an image of the cowboy character.
The player moves the cowboy using the
W
(up),A
(left),S
(down), andD
(right) keys. The player cannot walk through walls of the maze, but can move through empty space.As an optional add on, you have the choice to make the character turn based on the direction they are walking (as demonstrated in the video at the top of the assignment). Implementing this feature will give you extra practice with datatypes and code design (but again it is entirely optional) and will add +1 point to make up any point losses in functionality. To accomplish this, we’re giving you access to the images
cowboy-up.png
,cowboy-down.png
,cowboy-left.png
, andcowboy-right.png
for the various orientations of the cowboy.Items
You will implement either gadgets or portals into the game. You only need to implement one of these to get full credit. You may do both if you want, but you will not get extra credit. However, if you do both, it will be fun and we will grade the two implementations individually—the grade that is higher will be your grade for the project.
There can be an arbitrary number of these items placed on the maze. Items are picked up when the player moves into their cells; gadgets are immediately consumed, and portals are “held on to” until use. If multiple portals are collected, the cowboy will be able to “hold on to” all of them - and will only lose one each time he uses a portal.
Either option will be difficult in its own way; we do not think either is significantly easier than the other.
If you want to implement a different item (with different behavior from these), you must email the HTAs for approval before your design check.
Gadgets: If you implement gadgets, your character needs to have stamina. Stamina is a measure of energy often used in video games. In the Tumbleweed Town, your character’s stamina depletes by moving. If your character runs out of stamina, it’s game over.
There are three things you can encounter in the maze: a cowboy hat, cowboy boots and a snake.
For example, if the character’s stamina is 20 and they move onto a snake, they may move down to 7 stamina. Then moving onto cowboy boots may heal them to 7 + 8 = 15 stamina, and finding a cowboy hat will heal them to their original stamina (say, 30). Normal movement reduces stamina by 1 per cell.
There should be a visual indication of the character’s stamina (in our sample, it’s the yellow bar on the right, but you can handle this differently if you wish).
Portals: Your character can carry one or more portals (you may choose the limit), but starts with no portals. When the character has a portal (a horse) and the user clicks some square on the screen, the character will move there as long as the cell is within some reasonable range of the character’s current location. You can choose this range, but make sure it doesn’t allow the user to easily teleport to the end of the maze!
Each portal can be used only once. Your game screen should have some visual indication of how many portals the player has (simply a number using the Image library’s
text
function is fine).To compute how far the user is trying to move the character, you can use the distance formula, with the change in the x-coordinate Δx and the change in the y-coordinate Δy:
D=√Δx2+Δy2
For example, if the player is at (3,8) and wants to move to (6,4), D=√(6−3)2+(4−8)2=√9+16=5. This is a “birds-eye view” of distance.
In your game, if a player with a portal clicks on a cell, and the distance between the current and clicked cells is within the threshold you choose, the character moves to the new cell and uses up a portal. If the distance is larger than the threshold, nothing changes (the character stays in the same place and keeps the portal for another try).
Starting, Winning, and Losing
The player/character will start at a location of your choosing. The information of where they start should be hardcoded through constants in your code.
The player wins the game upon reaching the cattle corral at an exit location that you set. This information should also be hardcoded through constants. We recommend having the end location on one of the edges of the maze so that it is clear when playing the game where the exit is located. If your exit is not on the edge of the maze, you’ll have find some other way to make it clear to the player where they have to go.
The player loses the game if they run out of stamina. If you don’t implement gadgets, the player doesn’t have to worry about losing stamina.
Upon winning or losing, the game can simply close. You may also choose to somehow inform the player that they have won or lost (as was done in Lab 2), but this is not a requirement.
A video-based overview of the spreadsheets and how the gameplay works are in this video.
Implementation Details
Configuring the Maze (in Google Sheets)
The game will be populated based on your copy of a spreadsheet:
Use the spreadsheet appropriate for your item implementation. Feel free to modify the maze layouts and item populations! See #2 in the “Support Code” section (below) for how to load the sheets.
Each spreadsheet has two sheets:
maze-layout
anditems
.The
maze-layout
sheet determines which parts of the maze are walls and which are blank and therefore where the player can walk. “x” corresponds to a wall, and “o” corresponds to a floor.The
items
sheet contains a list of items that will be placed on the maze. The x column is the distance from the left side of the screen, and the y column is the distance from the top of the screen. You will need to edit this sheet to put items in the appropriate places. Check out the images in this folder.Note 1: Positions are zero-indexed; the row
being placed in the 2nd column and the 8th row.
"Cowboy hat", 1, 7, "cowboy-hat.png"
corresponds to anNote 2: You do not need to do any error checking for items being placed in invalid locations (like on walls or off the maze).
Using Reactors to Create Games
Remember the lasso and bull program? The position of the lasso was captured with a
Number
: on each tick of time, theon-tick
function produced a newNumber
value. It also had a function to draw the lasso based on the currentNumber
.All animations in Pyret have a similar structure: there is a datatype that captures the current information for the animation (positions, colors, attributes of characters, etc), and a separate function that takes a value of that datatype and draws it. Pyret calls these functions alternately: draw the current information, update it, draw it, update it, draw it, and so on.
For games based on user interaction, we want the information to update when the user presses keys (or the mouse). Rather than using the
on-tick
from the ghost program, we use a different reactor function calledon-key
, which takes the game information and name of key pressed (as a string) and produces new game information. In this case, Pyret alternates between callingon-key
,to-draw
,on-key
,to-draw
, etc.Thus, to make a game, you need a datatype that captures the information that changes as the game runs, you need a function that computes a revised datatype based on keys pressed, and you need a function that draws the current information. Pyret does the work of calling these functions to run your game.
You have seen the reactor properties
init
,to-draw
,on-key
,stop-when
, andclose-on-stop
before. If you are implementing portals, you will also need to use theon-mouse
function, which works similar to theon-key
function; check out theon-mouse
documentation for more. (for this, Pyret addson-mouse
to the rotation of functions it calls.)You can refresh yourself on reactor properties here. You may also want to look at your code or the solutions to Lab 2, which was on reactors.
Support Code
You will be given support code that provides four functions:
load-texture :: (String -> Image)
This function will load the images you can use to build up your game. Given a name of a file from the project 2 image directory,
load-texture
will return that image. The textures (walls, empty sqaures) are 30x30 (pixels), and everything else (players, cowboy boots, portals, etc.) are 24x24 (pixels).Examples:
-
load-texture("cowboy-hat.png")
returns thisImage
:-
load-texture("horse.png")
returnsload-items :: (String -> Table)
This function, given the ID of a spreadsheet, loads the
"items"
sheet into a table with columns"name"
,"x"
,"y"
, and"url"
.load-maze :: (String -> List<List<String>>)
and
load-maze-n :: (String, Number -> List<List<String>>)
This function, given the ID of a spreadsheet, loads the maze into a list of lists of the letters in the
maze-layout
sheet. If you change the number of columns, you must useload-maze-n
which is the same asload-maze
except it has a second parameter which is the number of columns to load.The resulting list looks like this:
get-maze-index :: (Number -> Number)
(portals only)The reactor
on-mouse
function takes in x and y coordinates that do not directly correspond to which row and column in the maze the user clicked on. Because of this,get-maze-index
is a function that takes in the coordinate that is input toon-mouse
and converts it to a game grid coordinate. For example, if thex
-coordinate clicked is 334,get-maze-index(334)
returns 11 (for the 12th column).Deadline 1: Design Check
Signup
Once you have a partner, sign up for a design check on this Google form by the signup deadline. You may not work with your Project 1 partner. Only one partner should fill out the form, and it is very important that your login information is correct.
Once you fill out the form, you will get two confirmation emails:
If you need help finding a partner, check out this Campuswire post, which was released earlier this week. If you are still having trouble finding a partner by Sunday, email the HTAs, and people will be paired up in the order that they email. This will ensure that you can fill out the form on time.
Requirements
For design check, you will hand in your
tumbleweed-town.arr
andpen-and-paper.pdf
file.There are two types of questions for the Design Check: “Pen & Paper” and “Coding”. For the Pen & Paper questions, come to your design check prepared to talk about your answers in the
pen-and-paper.pdf
file. For the Coding questions, write the code intumbleweed-town.arr
. Submit both files to Gradescope at least two hours before your design check starts.We recommend doing the questions in order as much as possible, as each question will help you think about the next.
Basic Project Setup
If you want to implement another item, it must be approved by an HTA before the design check.
File > Make a Copy...
b) Share the spreadsheet with your partner, and
c) Give “Anyone with the link” the ability to view the spreadsheet (you have to go into the Advanced sharing menu to do this).
tumbleweed-town.arr
.a) Copy and paste the code in the block below.
b) Replace “INSERT SPREADSHEET ID HERE” in the code you just pasted in with the ID of your spreadsheet
c) Make sure it runs. Then look at
maze-data
anditem-data
to get an idea of what they look like.To find the ID of your spreadsheet, open the sheet and check the URL for the highlighted portion below:

Design Check Questions!
Pen & Paper: In Lab 2, you made a reactor where a lasso moved towards a bull. We can divide the components of the animation into two categories: “Dynamic” (changing throughout the animation) and “Static” (does not change throughout the animation). The position of the bull, the shape and color of the background, and the shape and the color of the bull and lasso were things that did not change as we interacted with our reactor. The lasso position did change, both on-tick (over time) and on-key (when we pressed certain keys on our keyboard). This is represented in the chart below:
Look at the gameplay video at the top of the document. Make a similar list or chart of the static and dynamic components of the maze game.
Coding: Create the datatypes you will need for this game. Keep in mind what you wrote for question 1: only “dynamic” information should be components of the shifting game state.(For reference, you can look at how we created the Posn datatype in Lab 2.)
GameState
for the game state. Consider what you want to keep track of in the game: position, items, stamina, or whatever you need.GameState
. Define those as well. We’ll be expecting you to provide at least one more additional datatype (other than the Posn, if you choose to use it), but most implementations make use of more than this. This is also good style!Put the data types and examples into
tumbleweed-town.arr
.Pen & Paper: The reactor
to-draw
function takes in the currentGameState
value and outputs anImage
showing the corresponding frame of the game. Make a list of the tasks required to generate the maze image (3-5 one sentence bullet points). We suggest you spend a decent amount of time on this question: the more thorough you are, the more your TA will be able to help you at design check and the easier your jobs will be for week 2 of the project.Pen & Paper:
(1) If you are working on the gadgets version of the game: The reactor
on-key
function takes in the currentGameState
value and outputs a newGameState
value that reflects changes based on which key has been pressed. Pick one of the four keys (W
,A
,S
,D
) and write out the tasks (3-5 one sentence bullet points) needed to compute the newGameState
based on the currentGameState
.(2) If you are working on the portals version of the game: The reactor
on-mouse
function takes in the currentGameState
value, thex
andy
value of the position of the mouse event (in the reactor), and aString
that represents the name of the mouse event. Write out the tasks needed to compute the newGameState
based on the currentGameState
.Again, work here should save you considerable time in week 2.
Pen & Paper:
(1) If you are working on the gadgets version of the game: Write one simple example of the function named
key-pressed
you will use ason-key
in the reactor. Consider how the game state will change.(2) If you are working on the portals version of the game: Write one simple example of the function named
use-portal
you will use ason-mouse
in the reactor.Be sure to provide (a) what are the inputs to this function, (b) what is the output of the function, (c) what the function is doing, and (d) an example, something along the lines of:
or
Coding: Write a function
get-item(l :: List<List<String>>, x :: Number, y :: Number) -> String
in Pyret that takes in a List of Lists, and get the xth element from the yth list.Pen & Paper: Write down a list of 3-5 edge cases that you can think of in the program and how you plan to tackle these edge cases. Some questions to think about: What are the edge cases of the keys being pressed/communicated to the program? What are the edge cases that could happen with the stamina logic?
NEW (3/20/20): The design check solutions are posted in Canvas. Please don’t look at them until after you have completed your design check (we can see who is accessing Canvas files). If you are done with the design check, you are welcome to look.
In this file, we have also provided our implementation of the function
update-stamina
. Feel free to use this function in your code, or use it as a template for your other functions.Deadline 2: Final Handin
Work on One Feature at a Time
The key to tackling a larger assignment like this is to build the features up gradually. For example, get the game working on just basic functionality before adding more advanced features. What might that mean here?
These phases correspond to the grading levels for the project (see the Grading section below). You can pass the project even if you don’t get beyond phase 1.
We strongly recommend saving a copy of your file after you get each stage working, just in case you need to get back to your last stable version (practicing programmers do this all the time).
Grading
Testing
Since there are many functions that will be written for this project, we will only be requiring minimal testing for all functions that are not critical functions. In an ideal world, you would thoroughly test all functions; however, we would rather see you test one or two functions really well (to show you understand the concept) and others partially, rather than only doing partial testing or no testing at all for all functions, especially critical functions.
Because of this, we will look for the following:
(1) minimal testing (>= 2 test cases) for helper functions that output things other than an Image, and
(2) your thorough testing (try to hit as many tests of edge cases as possible!) for the function that moves the character (the one you use as
on-key
, oron-mouse
). Test this function thoroughly, covering as many cases as you can.Regarding your other functions, write as many tests as you think you need to be comfortable with it working - but include at least 2.
Your design, testing, and clarity grades will not be based on your functionality grade. Functionality will be weighted more heavily in your final grade for this project in particular. Here is what we’re looking at when grading functionality:
Minimum Functionality:
Mid-tier functionality:
For full functionality:
You can use the categories above as a roadmap as you’re implementing, or as a gauge of how far along you are!
NEW (3/30/2020): Correct implementation of minimum functionality will constitute a passing grade on this project.
Reflection
This project was designed to give you practice with organizing and manipulating structured data. Answer the following questions about these topics in a block comment at the bottom of
tumbleweed-town.arr
.Note on Late Days
If you plan on using one or more of your late days on this project, note that you are only allowed to use as many late days as the partner with the least number of late days remaining has. In other words, if one partner has 1 late day left and the other has two late days left, you are only allowed to use 1 late day on this project. As always, the 3-day maximum policy applies.
Campuswire and Feedback