(require 2htdp/image) (require 2htdp/universe) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SNAKE (define-struct world (food snake)) ;; A World is a (make-world Food Snake) ;; A Food is a Posn (in grid square coords) (define-struct snake (segs direction)) ;; A Snake is a (make-snake Segs Direction) ;; A Segs is one of: ;; - empty ;; - (cons Posn Segs) ;; where posn is in grid square coords ;; A Snake must contain at least one segment ;; The head is the first element in Segs ;; A Direction is one of: ;; - "up" ;; - "down" ;; - "left" ;; - "right" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; TEMPLATES #;(define (world-temp w) ... (world-food w) ... (world-snake w) ... ) #;(define (food-temp f) ... (posn-x f) ... (posn-y f) ...) #;(define (snake-temp snk) ... (snake-segs snk) ... (snake-direction snk) ...) #;(define (segs-temp segs) (cond [(empty? segs) ...] [else ... (first segs) ... (segs-temp (rest segs)) ... ])) #;(define (dir-temp d) (cond [(string=? d "up") ...] [(string=? d "down") ...] [(string=? d "left") ...] [(string=? d "right") ...])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CONSTANTS (define GRIDSQ-SIZE 10) ;; in pixels (define BOARD-HEIGHT 20) ;; in grid squares (define BOARD-WIDTH 30) ;; in grid squares (define BACKGROUND (empty-scene (* GRIDSQ-SIZE BOARD-WIDTH) (* GRIDSQ-SIZE BOARD-HEIGHT))) (define TICK-RATE 0.3) (define SEG-RADIUS (quotient GRIDSQ-SIZE 2)) (define SEG-IMAGE (circle SEG-RADIUS "solid" "red")) (define FOOD-RADIUS (floor (* 0.9 SEG-RADIUS))) (define FOOD-IMAGE (circle FOOD-RADIUS "solid" "green")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; EXAMPLES (define food1 (make-posn 5 3)) (define snake1 (make-snake (cons (make-posn 6 10) empty) "left")) (define world1 (make-world food1 snake1)) (define snake2 (make-snake (cons (make-posn 5 3) empty) "left")) (define world2 (make-world food1 snake2)) ; an eating scenario (define food3 (make-posn 10 19)) (define snake3 (make-snake (cons (make-posn 5 3) (cons (make-posn 6 3) empty)) "left")) ; 2-segment snake (define world3 (make-world food3 snake3)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Wish List ;; world->world : World -> World ;; produce the next world ;; snake-slither : Snake -> Snake ;; move snake in direction it's headed in ;; snake-grow : Snake -> Snake ;; grow the snake ;; eating? : World -> Boolean ;; is this an eating scenario? ;; key-handler : World KeyEvt -> World ;; place-image-on-grid : Image Number Number ;; game-over? : World -> Boolean ;; wall-collide? : Snake -> Boolean ;; self-collide? : Snake -> Boolean ;; create-food : World -> World ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; IMAGE RENDERING FUNCTIONS ;; world->scene : World -> Image ;; render the current world(define (world->scene w) (define (world->scene w) (snake+scene (world-snake w) (food+scene (world-food w) BACKGROUND))) ;; food+scene : Food Image -> Image ;; adds the food to a scene (define (food+scene f scn) (place-image-on-grid FOOD-IMAGE (posn-x f) (posn-y f) scn)) ;; place-image-on-grid : Image Number Number Image -> Image ;; just like place image except take grid square coords (define (place-image-on-grid img1 x y img2) (place-image img1 (- (* GRIDSQ-SIZE x) (quotient GRIDSQ-SIZE 2)) (- (* GRIDSQ-SIZE y) (quotient GRIDSQ-SIZE 2)) img2)) (check-expect (place-image-on-grid FOOD-IMAGE 1 1 BACKGROUND) (place-image FOOD-IMAGE 5 5 BACKGROUND)) (check-expect (place-image-on-grid FOOD-IMAGE 5 10 BACKGROUND) (place-image FOOD-IMAGE 45 95 BACKGROUND)) ;; snake+scene: Snake Image -> Image ;; add the snake to the scene (define (snake+scene snk scn) (segments+scene (snake-segs snk) scn)) ;; segments+scene : Segs Image -> Image ;; draw the given segments on top of the given scene (define (segments+scene segs scn) (cond [(empty? segs) scn] [else (place-image-on-grid SEG-IMAGE (posn-x (first segs)) (posn-y (first segs)) (segments+scene (rest segs) scn))])) ; Examples/tests: Image rendering functions (check-expect (food+scene food1 BACKGROUND) (place-image FOOD-IMAGE 45 25 BACKGROUND)) (check-expect (segments+scene empty BACKGROUND) BACKGROUND) (check-expect (segments+scene (snake-segs snake1) BACKGROUND) (place-image SEG-IMAGE 55 95 BACKGROUND)) (check-expect (snake+scene snake1 BACKGROUND) (place-image SEG-IMAGE 55 95 BACKGROUND)) (check-expect (snake+scene snake3 BACKGROUND) ; 2-segment snake (place-image SEG-IMAGE 45 25 (place-image SEG-IMAGE 55 25 BACKGROUND))) (check-expect (world->scene world1) (place-image FOOD-IMAGE 45 25 (place-image SEG-IMAGE 55 95 BACKGROUND))) (check-expect (world->scene world2) ; eating scenario: food is hidden! (place-image SEG-IMAGE 45 25 BACKGROUND)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; SNAKE MOTION & GROWTH ;; snake-slither : Snake -> Snake ;; move snake by one step in direction it's headed in (define (snake-slither snk) (... (snake-segs snk) ... (snake-direction snk) ...)) #;(check-expect (snake-slither snake1) (make-snake (cons (make-posn 5 10) empty) "left")) #;(check-expect (snake-slither snake3) (make-snake (cons (make-posn 4 3) (cons (make-posn 5 3)) empty) "left")) ;; A NESegs is one of: ;; - (cons Posn empty) ;; - (cons Posn NESegs) ;; Template #;(define (nesegs-temp nesegs) (cond [(empty? (rest nesegs)) ... (first nesegs) ...] [else ... (first nesegs) ... (nesegs-temp (rest nesegs)) ... ])) ;; segments-all-but-last : NESegs -> Segs ;; remove the last segment from a NON-EMPTY list (define (segments-all-but-last nesegs) ...) #;(big-bang world0 (to-draw world->scene) (on-tick world->world) (on-key key-handler) (stop-when game-over?))