;; CMPU 101 Spring 2013 ;; Lecture 18 (require 2htdp/image) (require 2htdp/universe) ;; NOTE THE USE OF LARGE LETTERS, AVAILABLE IN THE INSERT MENU. THESE ARE ;; NICE TO USE WHEN VIEWING THE PROGRAM CONTOUR (CTRL-u). ; ; Example: Writing a function to direct a moving circle (dot) around a ; grid using input from the keyboard. ; ; This problem will involve using a new clause in big-bang: on-key. ; ; We will also use a stop-when clause that displays a final scene when ; the dot hits a wall. ; ; Lastly, we will experiment with creating and mutating global ; variables to keep track of the number of events in a simulation. ; For this purpose, we will use set!. ; ; ; ; ; ;; ;; ; ; ; ; ;;; ;;; ; ;; ;;; ;;;; ;;; ; ;; ;;;; ;;; ; ; ; ; ; ;;; ; ; ; ;; ; ;;; ; ; ; ; ; ; ;; ; ;; ; ; ; ; ; ;; ; ; ; ; ; ; ; ;; ; ;; ; ; ; ;; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ;; ; ; ; ; ;;; ; ; ; ; ; ;; ; ; ;;; ;;; ; ;; ;;;; ;; ;; ;; ; ;; ;; ;;;; ; ; ; Constants (define RADIUS 10) ;\ (define DIAMETER (* RADIUS 2)) ; dot-related (define SEGMENT (circle RADIUS 'solid 'red)) ;/ (define WIDTH 400) ;\ (define HEIGHT 400) ; scene-related (define MT (empty-scene WIDTH HEIGHT)) ; (define COLOR 'black) ;/ ;; Contract: (make-grid-hor-lines-v2) -> list of posn pairs ;; Header: (define make-grid-hor-lines-v2 (lambda () ...)) ;; Purpose: To make horizontal lines at every DIAMETER position in ;; grid (uses build-list for recursion). (define make-grid-hor-lines-v2 (lambda () (build-list ;; NOTE: using higher-order function (sub1 (/ WIDTH DIAMETER)) (lambda (c) (list (make-posn 0 (* (add1 c) DIAMETER)) (make-posn WIDTH (* (add1 c) DIAMETER))))))) ;; Contract: (make-grid-vert-lines-v2) -> list of posn pairs ;; Header: (define make-grid-vert-lines-v2 (lambda () ...)) ;; Purpose: To make vertical lines at every DIAMETER position in ;; grid (uses build-list). (define make-grid-ver-lines-v2 (lambda () (build-list ;; NOTE: using higher-order function (sub1 (/ HEIGHT DIAMETER)) (lambda (c) (list (make-posn (* (add1 c) DIAMETER) 0) (make-posn (* (add1 c) DIAMETER) HEIGHT)))))) ;; Contract: (draw-lines list-of-posn-pairs) -> image ;; Header: (define draw-lines (lambda (lopp) ...)) ;; Purpose: To draw lines between endpoints specified by each posn ;; pair in input list (define draw-lines (lambda (lopp) (cond [(empty? lopp) MT] [else (scene+line (draw-lines (rest lopp)) (posn-x (first (first lopp))) (posn-y (first (first lopp))) (posn-x (first (rest (first lopp)))) (posn-y (first (rest (first lopp)))) COLOR)]))) ;; Make the endpoints of all the lines to be in grid (define LOPS (append (make-grid-hor-lines-v2) (make-grid-ver-lines-v2))) ;; The game board: the equivalent of the empty-scene (define GRID (draw-lines LOPS)) ; ; For the simulation in homework 6, the world state w was the ; x coordinate of a red circle on a grid. The big-bang function ; had 2 clauses: on-draw and on-tick. ; ; Run this program now to find out how it works. ; ; ; ; ; NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW ; ; ; ; How should we change the world state so that we can move the ; ; dot in different directions on the grid? Specifically, we ; ; want to move the dot in four directions: up, down, left, and ; ; right, but not diagonally. ; ; ; ; What parts of the dot do we need to keep track of with ; ; these new requirements? ; ; ; ; 1. We need to know where to draw the dot (x and y coordinate). ; ; ; ; 2. We need to know the direction the dot should move when ; ; the clock ticks. ; ; ; ; We need to be able to change the movement of the dot ; ; between up, down, left and right. ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ;;; ;; ; ;;; ; ; ; ;; ; ; ;;; ; ;; ; ; ; ; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ;;;; ;;; ; ; ; ; ;;; ;; ;; ;; ;; ; (define-struct dotpos (x y dx dy)) ;; The state of the world is a (make-dotpos num num num num), ;; where the first 2 numbers are the x and y coordinate ;; and the last 2 are the change in direction, dx and dy ;; Contracts of all functions created by the definition of dotpos: ; (make-dotpos num num num num) -> dotpos ; (dotpos-x dotpos) -> num ; (dotpos-y dotpos) -> num ; (dotpos-dx dotpos) -> num ; (dotpos-dy dotpos) -> num ; (set-dotpos-x! dotpos number) -> void ; (set-dotpos-y! dotpos number) -> void ; (set-dotpos-dx! dotpos number) -> void ; (set-dotpos-dy! dotpos number) -> void ; (dotpos? anything) -> boolean (define INIT-W (make-dotpos (/ WIDTH 2) (/ HEIGHT 2) 0 DIAMETER)) ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ; ;;;; ; ;;; ;;; ; ;; ;;;; ; ; ; ; ; ; ; ; ; ; ;; ; ;;; ; ; ; ; ;; ; ; ; ; ;; ; ; ; ;; ; ; ; ; ; ; ; ; ; ;;; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;; ;; ; ; ; ;;; ; ; ; ; ; ;; ; ;; ;; ;;; ; ; ;; ;; ;; ; ;; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ;; ; ; Contract: (main dotpos) -> dotpos ; Header: (define main (lambda (w) ... )) ; Purpose: starts big-bang with initial world. (define main (lambda (w) (big-bang w (on-draw draw-segment) (on-tick move 1/4)))) ;; The clock will tick every quarter sec. ; ; For the on-draw clause in HW 6, you created a function called ; draw-segment that consumed a positive natural number called ; w. This function placed the SEGMENT image at (w, DIAMETER) ; on top of the GRID scene created above. How should we change ; draw-segment to comply with our new world model? ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ;;; ; ;; ;;; ;; ;;; ; ; ; ; ; ; ;;; ; ;; ; ;;; ;; ; ; ; ;; ; ;; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ;;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ;;; ; ; ; ; ;;; ; ; ; ;; ; ;;; ; ;; ;; ;; ;; ;; ;; ; ; ;;Contract: (draw-segment dotpos) -> scene (image) ;;Header: (define draw-segment (lambda (w) ...)) ;;Purpose: Render result of placing SEGMENT at position (dotpos-x w) ;; (dotpos-y w) on the background GRID. ;;Function definition: (define draw-segment (lambda (w) (place-image SEGMENT (dotpos-x w) (dotpos-y w) GRID))) ; ; For the on-tick clause, we wrote a function called go-right ; to consume a positive natural number w and increase w by ; DIAMETER each time it is called by on-tick. ; ; How should we change go-right to comply with our new world ; model? CHANGE THE NAME OF THE FUNCTION AND MAKE MODIFICA- ; TIONS SHOWN BELOW. ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;; ; ;; ;;;; ; ;;; ; ;; ; ; ; ;;; ; ; ; ; ; ; ;; ; ;; ; ;; ; ; ; ; ; ;; ; ; ; ;; ; ;;; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;;; ;;; ; ; ; ;;; ; ;; ;; ;; ;;; ; ;; ; ;;Contract: (move dotpos) -> dotpos ;;Header: (define move (lambda (w) ...)) ;;Purpose: change the position of the dot. ;; NOTE: WE CAN SHORTEN THIS FUNCTION BY COMBINING THE FIRST 2 CLAUSES. ;;Function definition: (define move (lambda (w) (cond [(or (and (<= (dotpos-x w) RADIUS) (< (dotpos-dx w) 0)) (and (>= (dotpos-x w) WIDTH) (> (dotpos-dx w) 0))) (make-dotpos (dotpos-x w) (dotpos-y w) (* -1 (dotpos-dx w)) (dotpos-dy w))] [(or (and (<= (dotpos-y w) RADIUS) (< (dotpos-dy w) 0)) (and (>= (dotpos-y w) HEIGHT) (> (dotpos-dy w) 0))) (make-dotpos (dotpos-x w) (dotpos-y w) (dotpos-dx w) (* -1 (dotpos-dy w)))] [else (make-dotpos (+ (dotpos-x w) (dotpos-dx w)) (+ (dotpos-y w) (dotpos-dy w)) (dotpos-dx w) (dotpos-dy w))]))) ; ; ; ; ; ; ; ; ; ; ; ;;; ; ;; ; ;; ;;; ; ; ; ; ; ;;; ; ; ;; ; ; ; ; ; ;; ; ;; ; ;; ; ;; ; ; ; ; ; ;; ; ;;; ;; ; ; ;; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ; ;;; ; ;; ; ;; ;;; ;; ;; ; ; ; ; ; ; ; ; ; ; ;;; ; ; CONTRACT: (worm-key dotpos string) -> dotpos ; HEADER: (define worm-key (lambda (w k) ... )) ; PURPOSE: To respond to key presses by changing the direction of ; the dot. ; Function definition: ;(define worm-key ; (lambda (w k) ; ; NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW ; ; ON-KEY: ; ; The function we will write above is used as an argument to the ; on-key clause of big-bang and takes in 2 arguments: ; ; 1. the current world state and ; ; 2. a string representation of the key that was pressed (this ; string can be compared using the key=? function). Possible ; values of the key-event are: "up", "down", "right", "left", ; a single character string representing any letter key, etc. ; ; The result of a key-event is a new world state. ; ; ; ; ; ;; ; ; ; ; ; ;;; ;;;; ;;; ; ;;; ; ; ; ; ;; ;;; ; ;; ; ; ; ; ; ;; ; ; ; ;;; ;; ; ; ; ;;; ; ; ; ; ;; ; ;; ; ; ; ;;; ; ; ;; ;; ; ; ;; ; ; ; ; ; ;;; ; ; ; ;; ; ; ;; ;; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ; ;; ; ; ; ; ; ;;; ;; ;;; ; ;; ; ; ; ;; ;;; ; ;; ; ; ; ; ; ; ; Contract: (at-wall? dotpos) -> boolean ; Header: (define at-wall? (lambda (w) ... )) ; Purpose: Return true when dot is embedded in wall ; Function definition: ;(define at-wall? ; (lambda (w) ; Contract: (final-scene dotpos) -> scene (image) ; Header: (define final-scene (lambda (w) ... )) ; Purpose: Write text on last scene to indicate end ; ; NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW--NEW ; ; STOP-WHEN: ; ; Recall that the function used as an argument to the stop-when ; clause of big-bang has the contract: ; ; ;; Contract: (stop-now? any) -> boolean ; ; where function stop-now? is checked every time the clock ticks. ; When it returns true, the simulation ends. ; ; It is possible for us to add a final scene to the simulation by ; adding an additional parameter to the stop-when? clause, a ; function with the following contract: ; ; ;; Contract: (final-scene any) -> image ; ; The final-scene function draws the final scene with some change ; indicating the end of the simulation. ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;;; ;;; ; ; ; ;; ;; ;;; ; ; ;; ; ; ; ;; ; ; ; ;;; ; ; ; ;; ; ; ;;; ; ; ; ; ; ; ; ; ;; ;; ; ; ; ; ;; ; ; ; ; ; ; ; ;; ;; ; ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;; ; ;;; ; ;;;;;; ; ; ; ;; ;;; ; ;;;; ; ; ; ;;; ;; ;; ;; ;; ; ; ;; ;; ;; ;; ; ;; ; ;; Call the main function, passing in the initial state of the world. (printf "(main DIAMETER)~%") ;(main DIAMETER) (main INIT-W) ; ; ; ;; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ; ; ;;; ;;; ;;;; ; ; ; ; ; ; ; ; ; ; ;; ; ; ; ;; ; ;; ; ; ; ; ;; ; ; ; ; ; ; ; ;; ; ;;; ;;; ;; ;; ; ; ; ; Suppose we wanted to keep track of how many times the user chooses ; to move the dot down during the simulation. One way to do this is ; to define a global variable (and this really will be a variable), ; and mutate the variable as the simulation proceeds. The procedure ; to do this is called set! and it has the contract: ; ; (set! variable-name new-value) ;