On this page:
Readings
Supplemental Materials
Pre-lecture
Goals
In-class
In-class exercise solution:
Homework

Module 01

Last updated: Fri, 16 Jan 2015 13:29:20 -0500

Readings

Notes:

Supplemental Materials

  • The Program Design Recipe

    Not all parts are relevant this week. Focus on:

  • Some presentation slides.
    • Writing "arithmetic" functions, i.e. function composition. [pptx] [pdf]

    • Working with Images [pptx] [pdf]

    Note: These slides are from a previous semester. We provide them because learning is often aided by looking at different presentations of the same material. However, previous iterations of the course had a different structure and different style requirements. Read these slides for their ideas only. When there are discrepancies, follow the rules of our current course.

  • Style Guide (not all the style rules will apply yet)

You also may need to refer to the following documentation:

Pre-lecture
  1. Make sure you have completed all the pre-course requirements.

  2. Read and understand the course policy on Please Don’t Cheat.

  3. Read the Readings and Supplemental Materials.

Goals
In-class

In-class exercise: Moving Ball
Write a big-bang program that simulates and displays a horizontally moving ball in a 400x400 (width x height) pixel scene. The ball starts at the center of the canvas and moves to the right at a constant velocity of 5 pixels/tick.
Use the Beginning Student Language and make sure you import the required libraries by adding the following lines at the top of your file:
(require 2htdp/universe)
(require 2htdp/image)
(require rackunit)
(require "extras.rkt")
In addition:
  • Create a data definition that explains how you chose to represent your "World".

  • Use a solid red circle of radius 40 to represent the ball.

  • If the ball would move off the canvas, stop the simulation (see stop-when). The final scene should add "THE END" in the middle of the canvas.

  • If the "left" key is pressed, move the ball to the left 10 pixels. Ignore all other keys.

Part 2:
Modify your program to simulate an inflating balloon, centered on the canvas. The balloon is a solid red circle with initial radius 40, and every tick increases the radius by 5 pixels. Pressing "left" should decrease the radius by 10 pixels (a radius cannot be negative, obviously). If you’ve designed your program properly, you should only have to change a few places in the code.

In-class exercise solution:

Commentary:
  • It’s debatable whether separate World and Ball data definitions and functions are required, but which one is more readable and extensible?

  • The examples don’t add much for this very small example, but we don’t omit any steps for pedagogical purposes.

Part 1:
(require 2htdp/universe)
(require 2htdp/image)
(require rackunit)
(require "extras.rkt")
 
; canvas constants
(define CENTER-X 200) ; Coordinate
(define CENTER-Y 200) ; Coordinate
(define WIDTH (* 2 CENTER-X))  ; pixels
(define HEIGHT (* 2 CENTER-Y)) ; pixels
(define EMPTY-SCENE (empty-scene WIDTH HEIGHT))   ; Image
(define END-TEXT-IMG (text "THE END" 24 "black")) ; Image
 
; A Pixels is a NonNegReal, representing a canvas distance.
 
; Ball definition
; A Ball is a Coordinate, representing the Ball's x position.
 
(define INIT-BALL CENTER-X)
(define BALL-RADIUS 40) ; pixels
(define BALL-AT-RIGHT-EDGE (- WIDTH BALL-RADIUS)) ; Coordinate
(define BALL-IMG (circle BALL-RADIUS "solid" "red")) ; Image
(define VELOCITY 5) ; pixels/tick
(define BALL-LEFT-STEP-SIZE 10) ; pixels
 
; World definition -
; A World is a Ball
; INTERP: represents a Ball on a canvas.
(define INIT-WORLD INIT-BALL)
 
; World functions
 
; run : World -> World
; Starts the simulation.
(define (run init-world)
  (big-bang
   init-world
   (on-tick next-world)
   (on-draw draw-world)
   (on-key key-handler)
   (stop-when last-world? draw-last-world)))
 
; next-world : World -> World
; Computes the next World state after w.
(begin-for-test
  (check-equal? (next-world INIT-WORLD) (+ INIT-WORLD VELOCITY)
                "compute 2nd world state"))
(define (next-world w)
  (next-ball w))
 
; draw-world : World -> Image
; Renders the current World state w.
(begin-for-test
  (check-equal? (draw-world INIT-WORLD)
                (place-image BALL-IMG CENTER-X CENTER-Y EMPTY-SCENE)
                "draw initial world"))
(define (draw-world w)
  (draw-ball-on w EMPTY-SCENE))
 
; key-handler : World KeyEvent -> Worldm
; Computes the next World state in response to key press kev:
; - "left" moves ball left 10 pixels
; - other keys leave w unchanged
(begin-for-test
  (check-equal? (key-handler INIT-WORLD "right") INIT-WORLD "invalid key")
  (check-equal? (key-handler INIT-WORLD "left") (- INIT-WORLD (* 2 VELOCITY))
                "valid key (left)"))
(define (key-handler w kev)
  (cond
    [(key=? kev "left") (move-ball-left w BALL-LEFT-STEP-SIZE)]
    [else w]))
 
; draw-last-world : World -> Image
; Renders the last World state w.
(begin-for-test
  (check-equal?
   (draw-last-world INIT-WORLD)
   (place-image END-TEXT-IMG CENTER-X CENTER-Y (draw-world INIT-WORLD))
   "world with THE END text"))
(define (draw-last-world w)
  (place-image END-TEXT-IMG CENTER-X CENTER-Y (draw-world w)))
 
; last-world? : World -> Boolean
; Returns true if w should end the simulation.
(begin-for-test
  (check-false (last-world? INIT-WORLD) "simulation continues")
  (check-true (last-world? (add1 BALL-AT-RIGHT-EDGE)) "simulation ends"))
(define (last-world? w)
  (ball-off-canvas? w))
 
; Ball functions -
 
; next-ball : Ball -> Ball
; Computes the next Ball position.
(begin-for-test
  (check-equal? (next-ball -5) 0 "negative position")
  (check-equal? (next-ball 5) 10 "positive position"))
(define (next-ball b)
  (+ b VELOCITY))
 
; draw-ball-on : Ball Image -> Image
; Draws the Ball b horizontally-centered on Image img
(begin-for-test
  (check-equal? (draw-ball-on INIT-BALL EMPTY-SCENE)
                (place-image BALL-IMG INIT-BALL CENTER-Y EMPTY-SCENE)
                "draw initial ball (at center)"))
(define (draw-ball-on b img)
  (place-image BALL-IMG b CENTER-Y img))
 
; ball-off-canvas? : World -> Boolean
; Returns true if any part of b is off the canvas.
(begin-for-test
  (check-false (ball-off-canvas? CENTER-X) "fully in canvas")
  (check-false (ball-off-canvas? BALL-AT-RIGHT-EDGE) "exactly at edge: ok")
  (check-true (ball-off-canvas? (add1 BALL-AT-RIGHT-EDGE))
              "partially off canvas"))
(define (ball-off-canvas? b)
  (> b BALL-AT-RIGHT-EDGE))
 
; move-ball-left: Ball Pixels -> Ball
; Moves the given Ball b to the left by dist pixels.
(begin-for-test
  (check-equal? (move-ball-left INIT-BALL BALL-LEFT-STEP-SIZE)
                (- INIT-BALL BALL-LEFT-STEP-SIZE)
                "move left from center"))
(define (move-ball-left b dist)
  (- b dist))
Part 2:
At first glance, one might make the following changes:
; (define INIT-BALL CENTER-X)
(define INIT-BALL BALL-RADIUS)
(define (draw-ball-on b img)
  ; (place-image BALL-IMG b CENTER-Y img)
  (place-image (circle b "solid" "red") CENTER-X CENTER-Y img))
After running the program, we realize that negative Ball values are no longer valid:
; A Ball is a Pixels, representing the Ball's radius.
(define (move-ball-left b dist)
  ; (- b dist)
  (if (< (- b dist) 0)
      0
      (- b dist)))
This produces a program that "works" (after updating tests), but since we want clear, readable programs, we should also change many of the names so they make more sense in the new context, e.g. INFLATE-STEP, DEFLATE-STEP, deflate-ball, ... instead of VELOCITY, BALL-LEFT-STEP-SIZE, move-ball-left, ...

Homework

Problem Set 01