(define title "The Snake, Moving") ;; GOAL: get them to see the connectivity among the data definitions for design. ;; SHOW: the data definitions, the constant definitions ;; THEN: design the world-image function; time permitting also show world-move (define-struct world (snake food)) (define-struct snake (dir segs)) ;;; SNAKE WORLD ;;; ;;; World is: (make-world Snake Food) ;;; Food is: Posn ;;; Snake is: (make-snake Direction Segs) ;;; A snake's Segs may not be empty. ;;; Direction is one of: 'up 'down 'left 'right ;;; Segs is one of: ;;; -- empty ;;; -- (cons Posn Segs) ;;; Start with the world, when you make up a wish list, the others will fall ;;; out. This should suggest something like that. ;;; world->scene : World -> Scene ;;; food+image : Food Image -> Image ;;; snake+image : Snake Image -> Image ;;; world-step : World -> World ;;; snake-slither : Snake -> Snake ;;; snake-change-direction : Snake Direction -> Snake ;;; snake-eat : Snake Food -> Snake ;;; snake-grow : Snake -> Snake ;;; snake-self-collide? : Snake -> Boolean ;;; snake-eating? : World -> Boolean ;;; snake-wall-collide? : World -> Boolean ;; Add purpose statements. Then pick a place and start. ;; --- CONSTANTS : DESCRIBE PROPERTIES THAT ARE ALWAYS THE SAME (define HEIGHT 400) (define WIDTH 600) (define BACKGROUND (empty-scene WIDTH HEIGHT)) (define SEGMENT-SIZE 10) (define SEGMENT-IMAGE (circle SEGMENT-SIZE 'solid 'red)) (define FOOD-SIZE 8) (define FOOD-IMAGE (circle FOOD-SIZE 'solid 'green)) (define Snake1 (make-snake 'right (list (make-posn 10 10)))) (define Food1 (make-posn 10 50)) (define World1 (make-world Snake1 Food1)) (define World2 (make-world Snake1 (make-posn 20 10))) ; An eating scenario ;; --- FUNCTIONS ;;; Image-painting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; world->scene : World -> Scene ;;; Build an image of the given world. (define (world->scene w) (snake+image (world-snake w) (food+image (world-food w) BACKGROUND))) ;;; food+image : Food Image -> Image ;;; Add image of food to the given image. (define (food+image f img) (place-image FOOD-IMAGE (posn-x f) (posn-y f) img)) ;;; snake+image : Snake Image -> Image ;;; Add an image of the snake to the image. (define (snake+image wrm img) (segments+image (snake-segs wrm) img)) ;;; segments+image : Segments Image -> Image ;;; Add an image of the snake segments to the image. (define (segments+image segs img) (cond [(empty? segs) img] [else (place-image SEGMENT-IMAGE (posn-x (first segs)) (posn-y (first segs)) (segments+image (rest segs) img))])) "Examples/tests: Image-painting functions" (equal? (segments+image empty BACKGROUND) BACKGROUND) (equal? (segments+image (snake-segs Snake1) BACKGROUND) (place-image SEGMENT-IMAGE 10 10 BACKGROUND)) (equal? (snake+image Snake1 BACKGROUND) (place-image SEGMENT-IMAGE 10 10 BACKGROUND)) (equal? (food+image Food1 BACKGROUND) (place-image FOOD-IMAGE 10 50 BACKGROUND)) (equal? (world->scene World1) (place-image FOOD-IMAGE 10 50 (place-image SEGMENT-IMAGE 10 10 BACKGROUND))) ;;; Snake motion & growth ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; snake-slither : Snake -> Snake ;;; Move the snake by one step in the appropriate direction. (define (snake-slither s) (make-snake (snake-dir s) (segments-move (snake-segs s) (snake-dir s)))) ;;; segments-move : Segs Direction -> Segs ;;; Slither the list of segments one unit in the given direction. ;;; Everything moves up one segment, and we add a new head. (define (segments-move seg dir) (cons (move-posn (first seg) dir) ; New head: move old head 1 step. (segments-all-but-last seg))) ; New tail: all segs minus last one. ;;; Segs -> Segs ;;; Remove the last segment from a NON-EMPTY list. (define (segments-all-but-last seg) (cond [(empty? (rest seg)) empty] [else (cons (first seg) (segments-all-but-last (rest seg)))])) ;;; Posn Direction -> Posn ;;; Move posn one step in given direction. (define (move-posn p dir) (cond [(symbol=? 'up dir) (make-posn (posn-x p) (- (posn-y p) SEGMENT-SIZE))] [(symbol=? 'down dir) (make-posn (posn-x p) (+ (posn-y p) SEGMENT-SIZE))] [(symbol=? 'left dir) (make-posn (- (posn-x p) SEGMENT-SIZE) (posn-y p))] [(symbol=? 'right dir) (make-posn (+ (posn-x p) SEGMENT-SIZE) (posn-y p))])) ;;; snake-grow : Snake -> Snake ;;; Grow snake one step. ;;; This is just like SNAKE-SLITHER, but we don't drop the last segment. ;; (define (snake-grow s) ...) ;; To be completed. ;;; eat&grow : World -> World ;;; Eat the current food and grow the snake one segment. ;;; The new world has food at some random coordinate. ;;; The new food is aligned on a coordinate that the snake can reach. ;; To be completed. "Examples/tests: Snake motion & growth" (equal? (move-posn (make-posn 10 10) 'up) (make-posn 10 0)) (equal? (segments-all-but-last (snake-segs Snake1)) empty) (equal? (segments-all-but-last (list (make-posn 10 20) (make-posn 10 10))) (list (make-posn 10 20))) (equal? (move-posn (make-posn 10 20) 'up) (make-posn 10 10)) (equal? (segments-move (list (make-posn 10 20) (make-posn 10 10)) 'right) (list (make-posn 20 20) (make-posn 10 20))) (equal? (snake-slither Snake1) (make-snake 'right (list (make-posn 20 10)))) ;; Uncomment after completing code above. ;; (equal? (snake-grow Snake1) ;; (make-snake 'right (list (make-posn 20 10) ;; (make-posn 10 10)))) ;;; We can't test the new world's food, since it's randomly placed. ;; (equal? (world-snake (eat&grow (make-world Snake1 (make-posn 20 10)))) ;; (make-snake 'right (list (make-posn 20 10) ;; (make-posn 10 10)))) ;;; Collisions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; To be completed. ;;; Movie handlers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; world-step : World -> World ;;; Step the world one tick. (define (world-step.v1 w) ; This version is for early testing. (make-world (snake-slither (world-snake w)) (world-food w))) ;; --- RUN PROGRAM RUN (big-bang WIDTH HEIGHT .15 World1) (on-redraw world->scene) (on-tick-event world-step.v1)