Design Check Signup: Saturday Oct 27 11:59PM
Design Checks: Tuesday Oct 29, Wednesday Oct 30
Final handin deadline: Tuesday November 6, 9:00PM
Final handin late deadline: Wednesday November 7, 5:00PM
You will be creating a maze game. The user will navigate a character around the maze using key presses and mouse clicks. The maze will contain some magical items, such as golden apples to completely restore the character’s strength (shown here as the yellow bar on the right), bananas to give them a small boost, tomatoes to deplete their strength, or portals to allow the character to teleport. The goal: help the character escape the confines of the maze.
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. A lot of the work for this project will happen for design check.
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.
You will make a maze. The maze we provide 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 character to the exit.
The maze will also have some number of magical items scattered throughout that the player 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 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 current position of the player in the maze is represented with an image of a character.
The player moves the character 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. For the character image, choose from the options in this directory (see Implementation Details for more).
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).
You will implement either fruits 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.
There can be an arbitrary number of these items placed on the maze. Items are picked up when the player moves into their cells; fruits are immediately consumed, and portals are “held on to” until use.
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.
Fruits: If you implement fruits, your character needs to have stamina. Stamina is a measure of energy often used in video games. In the mystical magical maze, your character’s stamina depletes by moving. If your character runs out of stamina, it’s game over.
There are three types of fruits. Eating a fruit (which amounts to the character moving onto it) changes the character’s stamina.
For example, if the character’s stamina is 20 and they move onto a tomato, they may move down to 7 stamina. Then moving onto a banana may heal them to 7 + 8 = 15 stamina, and eating a golden apple 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 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 -coordinate and the change in the -coordinate :
For example, if the player is at and wants to move to , . 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).
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 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. 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 the fruits, the player doesn’t have to worry about losing.
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.
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
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 column is the distance from the left side of the screen, and the 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: https://cs.brown.edu/courses/csci0111/data/project-2/
Note 1: Positions are one-indexed; the row "Apple", 1, 7, "apple.png"
corresponds to an being placed in the 1st column and the 7th 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).
Maze Sheet | Items Sheet |
---|---|
![]() |
![]() |
Remember the sunset program? The position of the sun was captured with a Number
: on each tick of time, the on-tick
function produced a new Number
value. The sunset program also had a function to draw the sun 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 sunset 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.
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 squres) are 30x30 (pixels), and everything else (players, apples, portals, etc.) are 24x24 (pixels).
Examples:
- load-texture("apple.png")
returns this Image
:
- load-texture("portal.png")
returns
You are also free to use your own images: see this link if you want to do so!
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 and 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).
For design check, you will hand in your magicmaze.arr
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 answer the questions; you do not need to submit anything. For the Coding questions, write the code in magicmaze.arr
and submit to the Google Form some time 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.
Find a partner for your project. You may not work with your Project 1 partner. If you need help finding a partner, use this Piazza post.
Sign up for a design check appointment here. Make sure to invite your project partner to the event! (we need the information for grading purposes)
Pen & Paper: Decide which item you will be implementing (fruits or portals).
If you want to implement another item, it must be approved by an HTA before the design check.
Setup & Coding:
File > Make a Copy...
magicmaze.arr
.maze-data
and item-data
to get an idea of what they look like.# load the project support code
include shared-gdrive(
"project-2-support.arr",
"1J04p2lnp_FYTXWQbYUfcYQtddjHqUgJs")
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 : "Magical Mystical Maze!"
end
|#
Pen & Paper: In Homework 3, you made a reactor of a sunset. Go back to that animation and look it over. Here is a table of the “Dynamic” (changing throughout the animation) and “Static” (does not change throughout the animation) components of the animation.
Static | Dynamic |
---|---|
Starry Background | Sun Position |
Sun Size | Sun Color |
Sun Shape | Sun Speed |
Look at the gameplay video at the top of the document. Make a similar list of the static and dynamic components of the maze game.
Coding: Figure out the datatypes you will need for this game. Keep in mind what you wrote for question 3: only “dynamic” information should be components of the game state.
GameState
) for the game state.GameState
, define those as well.GameState
type.magicmaze.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: 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
. Again, work here should save you considerable time in week 2.
Coding: Write one simple example of the function you will use as on-key
in the reactor. For example:
key-pressed(..., "w") is ...
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).
Since there are many functions that will be written for this project, we will not be looking for thorough testing on all 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) rather than test all functions only partly.
Because of this, we will be only grade your testing for the function that moves the character (the one you use as on-key
). 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. We will not deduct if you do not have any/enough tests for other functions.
Only implement these after you have finished everything else for the project, including testing, docstrings, etc.
"S"
, the character “turns” down. You can accomplish this by using char-2/3-up/down/left/right.png
or by another method using char-1.png
or whichever image you use for your character.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.
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 magicmaze.arr
.
Block comments are written with #|
and |#
:
#|
this is a block comment.
with multiple lines!
yay!!
|#
Hand in magicmaze.arr
to the Google Form.
As always, make sure you receive your confirmation email. Simply submitting the Google Form does not mean you have successfully submitted your project.