On this page:
1 Directions for submitting
2 Oveview
3 Design of a Chip
6.12

Exercise 3

Due: Thursday, July 11, 11:59:59 PM EST.

NOTE: this due date has been pushed back 24 hours.

Implement these exercises with the Beginning Student Language. Require the HtDP2e image and universe libraries at the top of your definitions:

(require 2htdp/image)
(require 2htdp/universe)
1 Directions for submitting

Please read and follow these intructions carefully. You should submit a single file named ex3.rkt on ELMS. You may submit many times, but each submission should be one file called ex3.rkt. You may lose points for not following these instructions.

Make sure the top of the file contains the following, with your name filled in:
;; Exercise 3
;; Name: ...your name here...

2 Oveview

The goal of this exercise is to practice
  • (1) making an interactive, event-driven program,

  • (2) using compound data (i.e. structures),

  • (3) using the “design recipe” for systematic problem solving.

The goal of this exercise set is to make an interactive program that let’s the user control a small avatar that can move horizontally on a screen. The avatar, which we will affectionately refer to as "Chip, the Cheap Sheep," or simply "Chip," moves horizontally, either left or right. Chip’s direction is controlled by the left and right arrow keys on the keyboard. Chip can also go faster or slower (in the current direction of travel) with the up and down arrow keys. Conceptually, Chip can run arbitrarily far in either direction, but only a small window is shown, which Chip can enter or exit.

3 Design of a Chip

You should now have seen the basic elements of the design recipe, both from your readings in HtDP, and from lecture.

In this exercise, we’re going to be following the design recipe process. Many of the most important steps of the process are going to be done for you. As we progress through the course, you will be responsible for doing more of these steps yourself, so it’s important to follow along in the design process that’s given to you.

When designing an interactive program such as this, we should take an inventory of the information that we will need to model.

Take a look at the GIF recording of Chip above. Think about what information is needed to reconstruct any moment of the Chip program. At any given moment, we need to know:
  • Chip’s horizontal position,

  • Chip’s direction, and

  • Chip’s speed.

We will need to represent this information as BSL data, which brings us to the design of our data definition:

;; A Chip is a (make-chip Integer Boolean PositiveInteger)
(define-struct chip (x right? speed))
;; Interpretation:
;; x: distance from center of Chip to left side of screen (in px)
;; right?: whether Chip is going right
;; speed: rate of Chip's speed in px/tick

So, for example, (make-chip 10 #true 3) represents a Chip that is 10 pixels from the left side of the screen and moving right at a rate of 3px/tick.

Based on the big-bang event system, we will need functions to:

These tasks can help us generate a wishlist of function signatures:

;; chip-draw : Chip -> Image
;; Display a Chip on a scene
(define (chip-draw c) ...)
 
;; chip-move : Chip -> Chip
;; Move a Chip according to its direction and speed
(define (chip-move c) ...)
 
;; chip-key : Chip KeyEvent -> Chip
;; Respond to keyboard events by changing Chip’s direction or speed
(define (chip-key c ke) ...)

Which we can use to write a main function that launches a Chip program. Here, we’ve chosen to have it consume an x-coordinate for the initial placement of Chip.

;; main : Integer -> Chip
;; Launch Chip at given position, moving right slowly
(define (main x)
  (big-bang (make-chip x #true 1)
    [on-tick chip-move]
    [to-draw chip-draw]
    [on-key chip-key]))

Thinking more about the tasks involved in the above functions, we can identify a number of other tasks that will come up. We will need to:

From this list of tasks, we can generate another wishlist of function signatures:

;; chip-speed-down : Chip -> Chip
;; Decrease a Chip's speed
(define (chip-speed-down c) ...)
 
;; chip-speed-up : Chip -> Chip
;; Increase a Chip's speed
(define (chip-speed-up c) ...)
 
;; chip-go-right : Chip -> Chip
;; Change a Chip's direction to go right
(define (chip-go-right c) ...)
 
;; chip-go-left : Chip -> Chip
;; Change a Chip's direction to go left
(define (chip-go-left c) ...)

We now have a pretty good design sketch. To flesh it out, we can work through each of the functions above, applying the design recipe for functions to systematically solve for the code. Let’s work our way backwards.

Problem 1: Examples of chip-go-left and chip-go-right

Make four examples of using chip-go-left. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Apply the same steps as problem 1 to chip-go-right.

Problem 2: Solve for chip-go-left and chip-go-right

Using your examples to help guide you, fill out the ... in chip-go-left and chip-go-right with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

The chip-speed-up function should increase the given Chip’s speed by 4 px/tick. There’s no limit on how fast a Chip can go.

Problem 3: Examples of chip-speed-up

Make four examples of using chip-speed-up. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Now write the function.

Problem 4: Solve for chip-speed-up

Using your examples to help guide you, fill out the ... in chip-speed-up with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

The chip-slow-down function is slightly more involved compared to chip-speed-up. The chip-slow-down function should decrease the given Chip’s speed by 4 px/tick, but Chip’s speed should never drop below 1 px/tick.

Problem 5: Examples of chip-speed-down

Make four examples of using chip-speed-down. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Now write the function.

Problem 6: Solve for chip-speed-down

Using your examples to help guide you, fill out the ... in chip-speed-down with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

Now let’s move further up our wishlist.

The chip-key function is responsible for taking a Chip and a KeyEvent representing a key the user pressed and computing an appropriately changed Chip.

There are 5 cases to consider:
  • the user pressed "up",

  • the user pressed "down",

  • the user pressed "left",

  • the user pressed "right", or

  • the user pressed something else.

This suggests a function structure that uses cond to discriminate which case has occurred based on the ke input. (A helpful operation here is key=?, which takes two KeyEvents and determines if they are the same.)

Problem 7: Examples of chip-key

Make six examples of using chip-key. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Problem 8: Solve for chip-key

Using your examples to help guide you, fill out the ... in chip-key with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

Next, do chip-move, which should change a Chip’s position based on its direction and speed. The computed Chip’s position should reflect where Chip is after 1 tick of time.

Problem 9: Examples of chip-move

Make four examples of using chip-move. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Problem 10: Solve for chip-move

Using your examples to help guide you, fill out the ... in chip-move with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

The final task to consider is chip-draw. For this function, let’s first engage in what’s called iterative refinement, a fundamental engineering principle in which we first make a working solution for a simplified problem. Once in place, we can revise our problem description to be more sophisticated and refine our simple solution to the revised task. We can do this successively until arriving at a solution to our original problem.

There are a few aspects that complicate chip-draw. The first is that what to draw depends on the direction of Chip: if Chip is going left, the image of Chip should face left; if Chip is going right, the image should face right. The second is that, in order to make Chip appear animated, we actually choose different images of Chip in moments of movement based on Chip’s location.

Let’s start by simplifying away both of these complications and instead simply draw a circle to represent Chip.

Problem 11: Examples of chip-draw, version 1

Make four examples of using the simplified version of chip-draw. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Problem 12: Solve for chip-draw, version 1

Using your examples to help guide you, fill out the ... in chip-draw with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

You should now have a working version of (a simplified version of) the Chip program. Try it out by running (main 0).

Now let’s refine chip-draw slightly. Let’s re-introduce the first complication: the image of Chip should reflect Chip’s directionality. We will still ignore the complication of making Chip appear to run.

Copy/pasting will not produce good results here. You can download and insert the Chip image here: chip1.png.

Use the following image for Chip: image. In order to show Chip moving in the other direction, use flip-horizontal to compute a right-facing Chip.

Comment out your first version of chip-draw and redesign this more sophisticated version.

Problem 13: Examples of chip-draw, version 2

Make four examples of using the more sophisticated version of chip-draw. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Problem 14: Solve for chip-draw, version 2

Using your examples to help guide you, fill out the ... in chip-draw with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

Try out your refined program with (main 0).

Copy/pasting will not produce good results here. You can download and insert the Chip images here: chip1.png, chip2.png, chip3.png, and chip4.png.

Finally, let’s finish the exercise by removing our simplifying assumptions. In order to make Chip appear animated, use the following images:
  • image

  • image

  • image

  • image

If Chip is running right, we can again use flip-horizontal. In order to determine which image to use, you can use Chip’s position to select one of the four images. The basic idea is compute the position modulo 4. The modulo operation will help here. Try out some examples of modulo to get a sense of it.

We can now identify a couple small tasks that we can use to design functions:

;; chip-choose : Natural in [0,3] -> Image
;; Given either 0, 1, 2 or 3, pick a corresponding image of (leftward) Chip
(define (chip-choose c) ...)
 
;; chip-image : Integer -> Image
;; Pick one a (leftward) Chip image based on given position
(define (chip-image c) ...)

Problem 15: Design chip-choose and chip-image

Make four examples (each) of using of chip-choose and chip-image. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definitions and purpose statements.

Using your examples to help guide you, fill out the ... in chip-choose and chip-image with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

Now it should be pretty straightforward to finish the final version of chip-draw.

Problem 16: Examples of chip-draw, final version

Make four examples of using the full version of chip-draw. For each, write what answer the function should compute. Turn each example into a test using check-expect. Place these tests between the function definition and purpose statement.

Problem 17: Solve for chip-draw, final version

Using your examples to help guide you, fill out the ... in chip-draw with definitions that will work for the examples you wrote.

Run your program to confirm the correcntess of your function against the tests you’ve written.

Give Chip the Cheap Sheep a shot with (main 0).