Pipe Fantasy
This homework assignment is the first of a multi-part assignment where you will build Pipe Fantasy, a simplified version of the game Pipe Dream. You should play 1-2 levels of Pipe Dream to understand what you’re trying to build.
For this particular assignment, the program will be very simple: there is neither scoring nor an “goo” flowing through the pipes. The only behavior your program will support is allowing the player to click to place or replace a pipe on the tile, as shown below:
In later assignments, you’ll add the goo and scoring to turn it into a real game.
Before You Start …
Here are a few things to keep in mind before you start working on it:
- This assignment will take significantly more time to complete than earlier assignments. So, start immediately.
- We strongly recommend that you read the entire document before you start.
- We have provided signatures for many of the functions that you need to write. You must use them as provided and cannot change them.
- You must follow the full design recipe for every function and data design, unless indicated otherwise.
- You do not have to make your game look identical to our demos. Feel free to be more creative.
Part 1: Pipes as Data and How to Draw Them
Your first task is to complete the data design for pipes.
To make it easier to talk about pipes, we will use the directions of the
opening of pipe as the name for them. The directions are named top
, bottom
,
left
, right
, and they will be listed in this order. For example, the pipe
that has openings on the left and the top is the top-left
pipe. Pipe Fantasy has
seven kinds of pipes in three categories:
- Straight pipes:
top-bottom
andleft-right
- Corner pipes:
top-right
,top-left
,bottom-right
, andbottom-left
. - Cross pipe:
top-bottom-left-right
The code below is a partial data design for pipes:
(define-struct pipe [top bot left right])
;; A Pipe is a (make-pipe Boolean Boolean Boolean Boolean)
;; Interpretation: a pipe with openings in the given directions. A #true for
;; one of top, bot, left, right indicates an opening in that direction.
(define PIPE-TL (make-pipe #true #false #true #false))
Task 1: Copy the code above into your program and complete the data design. Do not modify the existing code.
Task 2: Create a list called ALL-PIPES
that contains every kind of pipe.
Task 3: During the game, the player places pipes onto square tiles on the board. Design a function with the following signature and purpose statement:
;; pipe->image: Pipe Integer Integer -> Image
;; Draws the given pipe on a square tile with length tile-side-length. The width
;; of the pipe is pipe-width. Pipe-width should be less than tile-side-length
(define (pipe->image pipe tile-side-length pipe-width)
...)
Here is a simple example of what a top-left
pipe might look like:
You do not need to write check-expects for pipe->image
.
Part 2: Designing the Grid and Placing Pipes
In the game, pipes are placed on square grid. The grid is indexed by the column
and row number, starting from 0. For example, the image below shows
a 7 × 7 grid, with only a top-bottom
pipe placed at
row 1 and column 2:
You will design your program to support any n × n grid.
Task 4: Complete a data design called Grid
that represents a grid. You
should construct several examples of varying sizes and with different pipes
places on them.
Recommendation 1: You clearly need a list to represent grids of varying sizes. The obvious approach is to have a list of length n × n, and that has a value at each position that represents a pipe or a blank tile. We strongly recommend not using this approach. Your examples will be very unwieldy and converting list positions to row and column numbers will be needlessly complicated.
Recommendation 2: A better approach is to only represent the list of placed pipes. So, a blank grid would be represented by the empty list. Thus each item in the list must represent both a pipe and its coordinates on the grid. (This requires an auxiliary data definition for a pipe with its coordinates.)
Task 5: Create an example that represents an empty 7 × 7 grid called
STARTING-GRID
.
Task 6: Complete the following function designs (do not modify the signatures):
;; place-pipe: Grid Pipe Integer Integer -> Grid
;; Places the pipe on the grid at the given row and column. We assume that the
;; row and column are valid positions on the grid.
(define (place-pipe grid pipe row col)
...)
;; pipe-at: Grid Integer Integer -> [Optional Pipe]
;; Produces the pipe at the given row and column, or #false if that position is
;; is blank. We assume that the row and column are valid positions on the grid.
(define (pipe-at grid row col)
...)
You can assume that [Optional X]
is defined as follows (you don’t need to
copy this into your program):
;; An [Optional X] is one of:
;; - #false
;; - X
;; Interpretation: Either #false or an X
(define (optional-template x)
(cond
[(false? x) ...]
[else ...]))
Task 7: Complete the following function design. Do not modify its signature, and you do not need to write check-expects for it:
;; grid->image: Grid Integer Integer -> Image
;; Draws the grid of pipes. Every tile should be a square with side length
;; tile-side-length and every pipe should have width pipe-width.
(define (grid->image grid tile-side-length pipe-width)
...)
Part 3: Game Interactions
Task 8: Complete a data design called GameState
. For now, a game should
have a grid and a list of “incoming pipes” that may appear when the player clicks
on a grid cell. In a complete game, the pipes are generated randomly and there
is an infinite supply of pipes. However, for now, you’ll have a fixed list
of incoming pipes.
Task 9: Complete the following function design:
;; place-pipe-on-click : GameState Integer Integer MouseEvent -> GameState`
;; If the user clicks on a tile and there are incoming pipes available, places
;; the next incoming pipe on that tile. If no pipes are available, does nothing.
You do need to write check-expects for it.
Task 10: Write a function that starts a game of Pipe Fantasy that simply uses the helpers you write above. You cannot write check-expects, but you can play the game.
;; pipe-fantasy: GameState -> GameState
(define (pipe-fantasy initial-game-state)
(big-bang ...))
Optional: You may wish to draw the list of incoming pipes on the screen, but it is not required at this point.