;; The first three lines of this file were inserted by DrScheme. They record metadata ;; about the language level of this file in a form that our tools can easily process. #reader(lib "htdp-intermediate-reader.ss" "lang")((modname rat-race) (read-case-sensitive #t) (teachpacks ((lib "world.ss" "teachpack" "htdp") (lib "testing.ss" "teachpack" "htdp"))) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ((lib "world.ss" "teachpack" "htdp") (lib "testing.ss" "teachpack" "htdp"))))) ;; THE RAT RACE ;; The rat world background (define WIDTH 300) (define HEIGHT 200) ;; the basic scene (define IMAGE0 (place-image (rectangle WIDTH HEIGHT 'solid 'blue) (/ WIDTH 2) (/ HEIGHT 2) (empty-scene WIDTH HEIGHT))) ;; A Rat is (make-rat Posn Number) (define-struct rat (loc size)) ;; Example of a rat (define rattie (make-rat (make-posn 30 50) 5)) ;; A food is (make-food Posn Number Number) (define-struct food (loc w h)) ;; Examples of foods (define cheese (make-food (make-posn 25 45) 10 10)) (define cheese2 (make-food (make-posn 50 40) 20 10)) ;; draw a rat onto the given scene ;; draw-rat: Rat Image -> Image (define (draw-rat arat scene) (place-image (circle (rat-size arat) 'solid 'black) (posn-x (rat-loc arat)) (posn-y (rat-loc arat)) scene)) ;; A visual test that the rat is drawn correctly (draw-rat rattie IMAGE0) ;; draw the food onto the given scene ;; draw-food: Food Image -> Image (define (draw-food afood scene) (place-image (rectangle (food-w afood) (food-h afood) 'solid 'green) (posn-x (food-loc afood)) (posn-y (food-loc afood)) scene)) ;; A visual test that the food is drawn correctly (draw-food cheese IMAGE0) ;; A visual test that the food and rat is drawn correctly (draw-food cheese (draw-rat rattie IMAGE0)) ;; A RatWorld is (make-world Rat Food) (define-struct rat-world (rat food)) ;; Example of a world (define world0 (make-rat-world rattie cheese)) ;; draw the rat world onto the initial image ;; draw-rat-world: RatWorld -> Scene (define (draw-rat-world rw) (draw-food (rat-world-food rw) (draw-rat (rat-world-rat rw) IMAGE0))) ;; A visual test that the world is drawn correctly (draw-rat-world world0) ;; produce a posn moved by the given distance (define (posn-move p dx dy) (make-posn (+ (posn-x p) dx) (+ (posn-y p) dy))) ;; Tests for posn-move ;; posn-move: Posn Number Number -> Posn (check-expect (posn-move (make-posn 20 40) 10 30) (make-posn 30 70)) ;; move the rat 3 pixels in the direction given by the ke ;; rat-move: Rat Symbol -> Rat (define (rat-move arat ke) (cond [(char? ke) arat] [(symbol=? ke 'up) (make-rat (posn-move (rat-loc arat) 0 -3) (rat-size arat))] [(symbol=? ke 'down) (make-rat (posn-move (rat-loc arat) 0 3) (rat-size arat))] [(symbol=? ke 'left) (make-rat (posn-move (rat-loc arat) -3 0) (rat-size arat))] [(symbol=? ke 'right) (make-rat (posn-move (rat-loc arat) 3 0) (rat-size arat))] [else arat])) ;; Test rat-move: (check-expect (rat-move rattie #\2) rattie) (check-expect (rat-move rattie 'in) rattie) (check-expect (rat-move rattie 'up) (make-rat (make-posn 30 47) 5)) (check-expect (rat-move rattie 'down) (make-rat (make-posn 30 53) 5)) (check-expect (rat-move rattie 'left) (make-rat (make-posn 27 50) 5)) (check-expect (rat-move rattie 'right) (make-rat (make-posn 33 50) 5)) ;; produce a rat world with the rat moved as given by the key event ;; rat-world-change: RatWorld KeyEvent -> RatWorld (define (rat-world-change w ke) (make-rat-world (rat-move (rat-world-rat w) ke) (rat-world-food w))) ;; Example of a world ;;(define world0 (make-rat-world rattie cheese)) ;; (define rattie (make-rat (make-posn 30 50) 5)) ;; Test rat-world-change: (check-expect (rat-world-change world0 #\2) world0) (check-expect (rat-world-change world0 'in) world0) (check-expect (rat-world-change world0 'up) (make-rat-world (make-rat (make-posn 30 47) 5) cheese)) (check-expect (rat-world-change world0 'down) (make-rat-world (make-rat (make-posn 30 53) 5) cheese)) (check-expect (rat-world-change world0 'left) (make-rat-world (make-rat (make-posn 27 50) 5) cheese)) (check-expect (rat-world-change world0 'right) (make-rat-world (make-rat (make-posn 33 50) 5) cheese)) ;; produce a rat hungrier than before ;; rat-starve: Rat -> Rat (define (rat-starve arat) (make-rat (rat-loc arat) (- (rat-size arat) 1))) ;; Test rat-starve: (check-expect (rat-starve rattie) (make-rat (make-posn 30 50) 4)) ;; did the rat find the food? ;; found-food?: Rat Food -> Boolean (define (found-food? arat munch) (and (<= (posn-x (food-loc munch)) (posn-x (rat-loc arat)) (+ (posn-x (food-loc munch)) (food-w munch))) (<= (posn-y (food-loc munch)) (posn-y (rat-loc arat)) (+ (posn-y (food-loc munch)) (food-h munch))))) ;; Tests for found-food: (check-expect (found-food? rattie cheese) true) (check-expect (found-food? (make-rat (make-posn 30 45) 5) cheese2) false) (check-expect (found-food? (make-rat (make-posn 60 30) 5) cheese2) false) (check-expect (found-food? (make-rat (make-posn 60 60) 5) cheese2) false) (check-expect (found-food? (make-rat (make-posn 80 45) 5) cheese2) false) ;; produce a fat rat that ate the food ;; rat-eat: Rat Food -> Rat (define (rat-eat arat munch) (make-rat (rat-loc arat) (+ (rat-size arat) (quotient (* (food-w munch) (food-h munch)) 10)))) ;; Tests for rat-eat: (check-expect (rat-eat rattie cheese) (make-rat (make-posn 30 50) 15)) ;; produce a new food at random within the given bounds ;; inset by at least 30 on all sides ;; new-cheese: Number Number -> Food (define (new-cheese w h) (make-food (make-posn (+ 30 (random (- w 60))) (+ 30 (random (- h 60)))) (+ 5 (random 10)) (+ 5 (random 10)))) ;; Test for new-cheese: only checks that the values are within bounds (define new-cheese-sample (new-cheese 100 200)) (and (< 30 (posn-x (food-loc new-cheese-sample)) 70) (< 30 (posn-y (food-loc new-cheese-sample)) 170)) ;; is the rat dead? ;; rat-dead?: Rat -> Boolean (define (rat-dead? arat) (<= (rat-size arat) 1)) ;; Test rat-dead? (check-expect (rat-dead? rattie) false) (check-expect (rat-dead? (make-rat (make-posn 20 20) 1)) true) (check-expect (rat-dead? (make-rat (make-posn 20 20) -2)) true) ;; Did the world end -- is the rat dead? ;; world-ends?: World -> Boolean (define (world-ends? aworld) (rat-dead? (rat-world-rat aworld))) (check-expect (world-ends? world3) true) ;; produce a rat world after one tick has passed ;; rat-world-tick: RatWorld -> RatWorld ;; check if rat found food: produce a new world in response ;; check if rat is dead: end the world in response ;; otherwise, starve the rat a bit (define (rat-world-tick w) (cond [(found-food? (rat-world-rat w) (rat-world-food w)) (make-rat-world (rat-eat (rat-world-rat w) (rat-world-food w)) (new-cheese WIDTH HEIGHT))] [else (make-rat-world (rat-starve (rat-world-rat w)) (rat-world-food w))])) ;; rats for the tests (define lab-rat (make-rat (make-posn 50 50) 10)) (define hungry-rat (make-rat (make-posn 50 50) 9)) (define fat-rat (make-rat (make-posn 50 50) 20)) (define dead-rat (make-rat (make-posn 50 50) 0)) ;; on tick the rat will starve (define world1 (make-rat-world lab-rat (make-food (make-posn 25 45) 10 10))) (define world1-on-tick (make-rat-world hungry-rat (make-food (make-posn 25 45) 10 10))) (check-expect (rat-world-tick world1) world1-on-tick) ;; on tick the rat will eat (define world2 (make-rat-world lab-rat (make-food (make-posn 45 45) 10 10))) ;; test only if the rat got fat - cannot test new cheese (check-expect (rat-world-rat (rat-world-tick world2)) fat-rat) ;; on tick the world will end (define world3 (make-rat-world dead-rat (make-food (make-posn 15 45) 10 10))) (generate-report) ;;------------------------------------------------------------------------- ;; run program run (big-bang WIDTH HEIGHT 2 world0) (on-redraw draw-rat-world) (on-tick-event rat-world-tick) (on-key-event rat-world-change) (stop-when world-ends?)