Module 3

World Programs

Using big-bang to make universal long-lived functions.

  • State: how do we represent data that changes during the lifetime of a program?
  • Event handler: respond to events such as clicking, typing, and the passage of time

big-bang

(big-bang initial-world-state
  [to-draw draw-world] ; required
  [on-tick tick-world] ; optional
  [on-key handle-key ; optional
  [on-mouse handle-mouse] ; optional
  [on-receive handle-network] ; optional
  [stop-when should-i-stop?] ; optional
  ...)

to-draw is the only required functions.

Common event types and their function signatures include:

  • to-draw: Draw based on the current state, or World (World -> Image)
  • on-tick: passage of time (World -> World)
    • does not draw images, only produces new state
  • on-key: keyboard buttons pressed (World KeyEvent -> World)
  • handle-key: produces the next state after the user presses a key (World KeyEvent -> World)
    • KeyEvent is a String that represents a key
    • does not draw images, only returns new state
  • handle-mouse: produces the next state of the world after the user presses a mouse button (World Number Number MouseEvent -> World)
    • the numbers are x- and y-coordinates
    • MouseEvent is one of button-down, button-up, drag, move, or enter
    • does not draw images, only produces new state
  • stop-when: end the program based on world state (World -> Boolean)

Questions to ask while designing a world program include:

  1. As the program executes, what data is changing and how do we represent it?
  2. What events are we responding to?

Note that we typically do not test big-bang functions because we are not able to predict many things, such as when the user closes the program.

Example: moving moon

Design a program big-bang-moon that moves a moon across the sky.

Answer the questions.

  1. The moon is moving and we have to keep track if its x-position
  2. The event we are listening to is time (on-tick)

First, create the big-bang function, following the design recipe (except no testing).

;; big-bang-moon : RealNumber -> RealNumber
;; Moves the moon across the sky

(define (big-bang-moon initial-x)
  (big-bang initial-x
    [to-draw draw-moon]
    [on-tick move-moon]))

We have created a wish-list for functions, draw-moon and move-moon.

;; draw-moon : RealNumber -> Image
;; Draws the moon on the sky at the supplied x-position

(check-expect (draw-moon 10) (place-image MOON 10 (/ SKY-HEIGHT 2) SKY))

(define (draw-moon x)
  (place-image MOON x (/ SKY-HEIGHT 2) SKY))


;; move-moon : RealNumber -> RealNumber
;; Moves the moon in the sky (2 units/tick)

(check-expect (move-moon 10) 12)

(define (move-moon x)
  (+ x 2))

Call with (big-bang-moon 0).

No we want to add an event: reset the moon’s position when the user presses a key.

(define (big-bang-moon initial-x)
  (big-bang initial-x
    [to-draw draw-moon]
    [on-tick move-moon]
    [on-key restart-moon])) ;; New function

Create the function:

;; restart-moon : RealNumber KeyEvent -> RealNumber
;; Puts the moon back at the start of the screen.

(check-expect (restart-moon 10 "t") (- RADIUS))

(define (restart-moon x key)
  (- RADIUS))

Note that we do not care about what key the user pressed. Any key should reset.

We now want to end the program when the moon moves off the screen, so we use the stop-when handler:

(define (big-bang-moon initial-x)
  (big-bang initial-x
    [to-draw draw-moon]
    [on-tick move-moon]
    [on-key restart-moon]
    [stop-when stop-moon])) ;; New handler and function
;; stop-moon : RealNumber -> Boolean
;; Determines if the moon is off screen.

(check-expect (stop-moon 10) #false)
(check-expect (stop-moon 1000) #true)

(define (stop-moon x)
  (> x (+ SKY-WIDTH RADIUS)))

Example: traffic lights

Design a program loop-light that shows a traffic light and changes red to green to yellow …

  1. The data changing is the color of the lights
  2. The events we respond to is just time (on-tick)

First, right the signature of the function:

;; loop-light : TrafficLight -> TrafficLight

We realize that there is no data type for the traffic light state, we create it:

;; A TrafficLight is one of
;; - "red"
;; - "yellow"
;; - "green"
;; Interpretation: colors of a traffic light.

(define TL-RED "red")
(define TL-YELLOW "yellow")
(define TL-GREEN "green")

(define (tl-template tl)
  (...
   (cond
     [(string=? tl TL-RED) ...]
     [(string=? tl TL-YELLOW) ...]
     [(string=? tl TL-GREEN) ...])))

Now start writing the big bang:

;; loop-light : TrafficLight -> TrafficLight
;; Visualize a looping traffic light.

(define (loop-light initial-tl)
  (big-bang initial-tl
    [to-draw draw-tl]
    [on-tick cycle-traffic-light]))

;; draw-tl : TrafficLight -> Image
;; Visualizes a traffic light

(define RADIUS 50)

(check-expect (draw-tl TL-RED) (circle RADIUS "solid" "red"))
(check-expect (draw-tl TL-YELLOW) (circle RADIUS "solid" "yellow"))
(check-expect (draw-tl TL-GREEN) (circle RADIUS "solid" "green"))


(define (draw-tl tl)
  (cond
    [(string=? tl TL-RED) (circle RADIUS "solid" "red")]
    [(string=? tl TL-YELLOW) (circle RADIUS "solid" "yellow")]
    [(string=? tl TL-GREEN) (circle RADIUS "solid" "green")]))

We can improve this because we’re drawing a circle of the same radius, different color. All colors are already there in the TL-* variables, so we can shorten the function:

(define (draw-tl tl)
  (circle RADIUS "solid" tl))

Now implement the final function that changes the color based on the current color (the on-tick function):

;; cycle-traffic-light : TrafficLight -> TrafficLight
;; Changes the color of the light.

(check-expect (cycle-tl TL-RED) TL-GREEN)
(check-expect (cycle-tl TL-GREEN) TL-YELLOW)
(check-expect (cycle-tl TL-YELLOW) TL-RED)

(define (cycle-tl tl)
  (cond
    [(string=? tl TL-RED) TL-GREEN]
    [(string=? tl TL-YELLOW) TL-RED]
    [(string=? tl TL-GREEN) TL-YELLOW]))

This works, but the lights change color very fast. We can change the big bang function to reduce the tick frequency:

(define (loop-light initial-tl)
  (big-bang initial-tl
    [to-draw draw-tl]
    [on-tick cycle-tl 1])) ;; 1 tick per second