;; CMPU101, Spring 2013 ;; Lecture #16 (require 2htdp/image) (require 2htdp/universe) ; * Lab 8 and homework 6 are posted. ; * Review basics of using structs. ; * Review introduction to writing animated programs. ; - Go over big-bang clauses and steps in writing a big-bang ; simulation. (THESE ARE POSTED IN THE LECTURE 15 NOTES) ; ; In the last lecture, we looked at a simple animation of a rocket ; image moving from top to bottom on a scene. The first example ; below repeats that program, including the clause to stop the ; rocket image from moving when it gets to the bottom of the scene. ; ; Whenever you have to check for an image being in collision with ; the edge of the scene or with another image in the scene, you ; must keep in mind that the x y coordinates of the image are at ; the pinhole, the exact center, of the image. For this reason, ; you will often need to use a function that returns the width ; (image-width) or the height (image-height) of an image and divide ; that value by 2. This makes the code a little messier (and, in ; general, animation programs are longer than any programs you've ; written yet.) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Version 1 of rocket landing scene: ;; Create a scene in which a rocket image is moving downward toward ;; the bottom of the scene. When the bottom of the rocket image touches ;; the bottom, the rocket (and the simulation) should stop. ;; THE STATE OF THE WORLD IS A NUMBER, THE Y-COORDINATE OF THE ;; ROCKET. ;------------BEGIN ROCKET SCENE CONSTANTS------------------ (define ROCKET .) ;; images do not show in text files (define ROCKET-HEIGHT/2 (/ (image-height ROCKET) 2)) (define COORD-X 100) (define HEIGHT 400) ; Height and (define WIDTH 200) ; width of scene (define MT (empty-scene WIDTH HEIGHT)) ;------------END ROCKET SCENE CONSTANTS-------------------- ;Contract: (create-rocket-scene-v1 number) -> image ;Header: (define create-rocket-scene-v1 (lambda (y) ... )) ;Purpose: display rocket at given coordinates. This function ; is called by the big-bang function, which passes ; in the current y coordinate: the state of the world. (define create-rocket-scene-v1 (lambda (y) ;; note that the x coordinate remains fixed, but the y coordinate changes ;; in the following line (place-image ROCKET COORD-X y MT))) ;Contract: (on-ground? number) -> number ;Header: (define on-ground? (lambda (y) ... )) ;Purpose: return true when the y coordinate of the rocket pinhole ; is >= the height of the scene minus half the image-height ; of the rocket. (define on-ground? (lambda (y) (>= y (- HEIGHT ROCKET-HEIGHT/2)))) ;Contract: (mission-control number) -> animated scene (image) ;Header: (define mission-control (lambda (w-s) ...)) ;Purpose: Call big-bang with proper initial world state. ;NOTE: The reason for defining a function like mission control ; is so that the animation will only run when we call this ; function (and not every time we press Run). (define mission-control (lambda (w-s) (big-bang w-s ;; on-draw clause passes the world state (a number) ;; into the create-rocket-scene-v1 function 28 times per second (on-draw create-rocket-scene-v1) ;; on-tick clause increments the world state (a number) ;; 28 times per second and passes it as an argument to add1 (on-tick add1) ;; stop-when clause passes the world state (a number) in to ;; the on-ground? function and stops the animation when the ;; state of the world has the rocket on the bottom of the scene. (stop-when on-ground?)))) ;; The first argument to mission-control is the negative value of ;; 1/2 the rocket height so that, at the start of the animation, ;; the rocket is completely above the scene. ;; UNCOMMENT THE LINE BELOW TO RUN BIG-BANG ;(mission-control (* ROCKET-HEIGHT/2 -1)) ;; In the simulation above, the rocket is very slow. How can we make it ;; faster using an unnamed lambda? ANSWER: Change the add1 function ;; given as argument to on-tick to an unnamed lambda like this: ;; (lambda (x) (+ x 5)) ; ADDING A LARGER NUMBER TO THE Y COORDINATE ; WILL MAKE IT MOVE FURTHER ON EACH CLOCK TICK ;; This will cause the rocket to move 5 pixels on each time step instead ;; of just 1 pixel on each time step. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Version 2 of rocket landing scene: ;; Create a scene in which a rocket image is moving downward toward ;; the bottom of the scene. When the bottom of the rocket image touches ;; the bottom of the scene, the rocket should begin to move upward. ;; ;; We should be able to change the simulation above to make the rocket ;; take off again. What information will we need to keep track of if ;; the rocket should start going downward, landing, and then taking off ;; again? ;; ;; We can still use a number to keep track of the y-coordinate of the ;; rocket, but we also need a way to determine if the y-coordinate ;; should be growing or shrinking. This calls for a world state that ;; contains 2 numbers. ;; To represent 2 numbers, we could use a posn struct. But we would be ;; using posns in a way that they were not intended to be used. ;; What to do? Define a new struct to hold the two values! I've called this ;; struct posdir below because it will hold the current value of the y ;; coordinate and also the change in the y coordinate each time the rocket ;; needs to be repositioned (a positive number for up and a negative number ;; for down). ;; The WORLD STATE is a (make-posdir y dy), where y is a number representing ;; the y coordinate of the rocket and dy is the change in elevation (either ;; positive or negative) (define-struct posdir (y dy)) ;;Contracts of functions created by this define-struct: ;;(make-posdir number number) -> posdir ;;(posdir-y posdir) -> number representing the y coordinate of an image ;;(posdir-dy posdir) -> number representing the change in y coordinate ;;(set-posdir-y! posdir number) -> void ;;(set-posdir-dy! posdir number) -> void ;;(posdir? anything) -> boolean ;; Creating a new world state requires small changes to some functions ;; and bigger changes to others. ;Contract: (create-rocket-scene-v2 posdir) -> image ;Header: (define create-rocket-scene-v2 (lambda (w) ... )) ;Purpose: display rocket at given posdir-y. This function ; is called by the big-bang function which passes ; in the current posdir struct w - the state of the world. (define create-rocket-scene-v2 (lambda (w) (place-image ROCKET COORD-X (posdir-y w) MT))) ;; In this version of the rocket landing, we will need to draw the ;; scene on every clock tick. We'll also still need to move the ;; rocket every time the clock ticks. ;; Since we haven't specified that the simulation needs to end, what ;; big-bang clause can we remove? ANSWER: stop-when ;Contract: (move-rocket posdir) -> posdir ;Header: (define move-rocket (lambda (w) ... )) ;Purpose: Change the world state with each clock tick ;(define move-rocket-v1 ;; version 1 ; (λ (w) ; (cond ; ;; rocket at scene bottom and moving downward ; ;; change direction to moving up by multiplying ; ;; the dy field by -1 ; [(and (>= (posdir-y w) (- HEIGHT ROCKET-HEIGHT/2)) (> (posdir-dy w) 0)) ; (make-posdir (posdir-y w) (* -1 (posdir-dy w)))] ; ;; if the rocket y coordinate is not at the bottom ; ;; of the scene, just move the rocket by returning ; ;; a new world state in which the dy field has been ; ;; added to the y field to make a new y field and ; ;; the dy field is the same as it was in the old world ; ;; state. ; [else ; (make-posdir (+ (posdir-y w) (posdir-dy w)) (posdir-dy w))]))) ;(define move-rocket-v2 ;; SECOND version ; (λ (w) ; ;; make a new posdir that adds the dy onto the y-coordinate ; ;; but put the cond inside the make-posn function call ; (make-posdir (+ (posdir-y w) (posdir-dy w)) ; (cond ; ;; rocket at scene bottom and moving downward, change direction so ; ;; that it starts to move up ; [(and (>= (posdir-y w) (- HEIGHT ROCKET-HEIGHT/2)) (> (posdir-dy w) 0)) ; (* -1 (posdir-dy w))] ; ;; leave direction in same state ; [else (posdir-dy w)])))) ;; Another way to change the world state is to reset the fields ;; in the given posdir struct w using the set-posdir-y! and ;; set-posdir-dy! functions (define move-rocket-v3 ;; THIRD version (λ (w) (cond ;; rocket at scene bottom and moving downward, change direction so ;; that it starts to move up [(and (>= (posdir-y w) (- HEIGHT ROCKET-HEIGHT/2)) (> (posdir-dy w) 0)) (begin (set-posdir-dy! w (* -1 (posdir-dy w))) w)] ;; leave direction in same state [else (begin (set-posdir-y! w (+ (posdir-y w) (posdir-dy w))) w)]))) ;Contract: (mission-control-v2 number) -> animated scene (image) ;Header: (define mission-control-v2 (lambda (w) ...)) ;Purpose: Call big-bang with proper initial world state. (define mission-control-v2 (lambda (w) (big-bang w ;; on-draw clause passes the world state (a posdir struct) ;; into the create-rocket-scene function 28 times per second (on-draw create-rocket-scene-v2) ;; on-tick clause increments the world state y coordinate ;; until rocket touches bottom, then moves rocket upward ;; NOTE THE 3 VERSIONS OF MOVE-ROCKET GIVEN ABOVE. (on-tick move-rocket-v3)))) ;; Call mission-control-v2, passing the initial state of the ;; world in a posdir struct with y coordinate set so that the ;; rocket is above the top of the scene and moving downward ;; (the dy field is a positive number). (mission-control-v2 (make-posdir (* -1 ROCKET-HEIGHT/2) 4))