;;; MADE BY GARY KATSEVMAN ;;; licensed under "Creative Commons Attribution-Noncommercial-Share Alike 3.0" ;;; http://creativecommons.org/licenses/by-nc-sa/3.0/us/ ;--------------------------SPACEWAR-------------------------------------------- ;Data Definitions: (define-struct world (ship1 ship2 bullets paused? dead? score)) ; a world is a (make-world Ship Ship [List of bullet] boolean dead? score) (define-struct ship (p v thrust theta)) ; a ship is a (make-ship Posn Posn Number Number) ; p is the position of the ship according to game coordinates ; v is the thurst where the posn represents x and y components of velocity ; thrust is how much the ship is accelerating, 0 means no thrust ; theta is the angle of the ship where it is facing, 0<=theta<=359 (define-struct dead? (ship1 ship2)) ; a dead? is a (make-dead? boolean boolean) ; where each boolean represents which ship is dead or a live ; true represents that a ship has died, false means it is still alive (define-struct bullet (p v)) ; bullet has a position and a velocity just like a ship (define-struct score (s1 s2)) ; a score has a score for ship1 and ship2 which are both numbers ;Constants--------------------------------------------------------------------- (define width 800) (define full-height 600) (define height (- full-height 100)) (define center (make-posn (/ width 2) (/ height 2))) (define sunr 20) (define shipr 10) (define bulletr 1) (define tipTOfin #i2.2252947962927703) (define topTOleftFIN #i4.057890510886816) (define G #i-150) (define thrust-off 0) (define thrust-on 1) (define ship1 (make-ship (make-posn -275 -225) (make-posn 0 0) 0 0)) (define ship2 (make-ship (make-posn 275 225) (make-posn 0 0) 0 pi)) (define ship3 (make-ship (make-posn 100 100) (make-posn 0 0) 0 (/ pi 2))) (define w1 (make-world ship1 ship2 (list) false (make-dead? false false) (make-score 0 0))) (define w2 (make-world ship3 ship2 (list) false (make-dead? false false) (make-score 1 1))) (define sun (overlay (star 20 5 20 'solid 'red) (star 10 5 15 'solid 'yellow))) (define bgcolor (rectangle width full-height 'solid 'black)) (define background (place-image sun (posn-x center) (posn-y center) (place-image bgcolor (posn-x center) (+ 50 (posn-y center)) (empty-scene width full-height)))) ;draw-score : score scene -> scene ;given a score and a scene, will add the score to the scene (define (draw-score s scn) (local [(define string (string-append (number->string (score-s1 s)) " " (number->string (score-s2 s)))) (define image (text string 10 'white))] (place-image image (- (posn-x center) (/ (image-width image) 2)) 20 scn))) ;update-score : dead?num score -> score ;given a number representing dead? and the current score ;will return the updated score (define (update-score d s) (cond [(boolean? d) s] [(= d 0) s] [(= d 1) (make-score (score-s1 s) (+ 1 (score-s2 s)))] [(= d -1) (make-score (+ 1 (score-s1 s)) (score-s2 s))] [else s])) ;draw-status : world scene -> scene ;given a world and a scene, adds the status to the scene (define (draw-status w s) (local [(define wp (- (/ width 2) 20)) (define charcolor 'white) (define (draw-status1 s) (let* ([ship (world-ship1 w)] [player (text "Ship 1:" 12 charcolor)] [wkey (text "W - Shoot" 10 charcolor)] [akey (text "A - Turn Left" 10 charcolor)] [dkey (text "D - Turn Right" 10 charcolor)] [skey (text "S - Turn Thrust On" 10 charcolor)] [t (text (if (< 0 (ship-thrust ship)) "Thrust: ON" "Thrust: OFF") 10 charcolor)] [posx (text (string-append "X: " (number->string (posn-x (ship-p ship)))) 10 charcolor)] [posy (text (string-append "Y: " (number->string (posn-y (ship-p ship)))) 10 charcolor)] [shipimage (draw-ship ship1 'blue (ship-thrust ship) (ship-theta ship))]) (place-image shipimage 150 (+ height 30) (place-image posx 30 (+ height 55) (place-image posy 30 (+ height 70) (place-image t 30 (+ height 35) (place-image player 30 (+ height 10) (place-image (rectangle (- wp 75) 80 'outline 'white) 175 (+ height 50) (place-image wkey 200 (+ height 25) (place-image akey 200 (+ height 40) (place-image dkey 200 (+ height 55) (place-image skey 200 (+ height 70) s)))))))))))) (define (draw-status2 s) (let* ([ship (world-ship2 w)] [player (text "Ship 2:" 12 charcolor)] [ikey (text "I - Shoot" 10 charcolor)] [jkey (text "J - Turn Left" 10 charcolor)] [kkey (text "K - Turn Right" 10 charcolor)] [lkey (text "L - Turn Thrust On" 10 charcolor)] [t (text (if (< 0 (ship-thrust ship)) "Thrust: ON" "Thrust: OFF") 10 charcolor)] [posx (text (string-append "X: " (number->string (posn-x (ship-p ship)))) 10 charcolor)] [posy (text (string-append "Y: " (number->string (posn-y (ship-p ship)))) 10 charcolor)] [shipimage (draw-ship ship1 'green (ship-thrust ship) (ship-theta ship))]) (place-image shipimage 600 (+ height 30) (place-image posx (+ wp 100) (+ height 55) (place-image posy (+ wp 100) (+ height 70) (place-image t (+ wp 100) (+ height 35) (place-image player (+ wp 100) (+ height 10) (place-image (rectangle (- wp 75) 80 'outline 'white) 625 (+ height 50) (place-image ikey 650 (+ height 25) (place-image jkey 650 (+ height 40) (place-image kkey 650 (+ height 55) (place-image lkey 650 (+ height 70) s)))))))))))) (define (draw-restart/pause s) (let ([bspace (text "BACKSPACE" 10 charcolor)] [restart (text "RESTART" 10 charcolor)] [space (text "SPACE" 10 charcolor)] [pause (text "PAUSE" 10 charcolor)]) (place-image bspace (- (posn-x center) (/ (image-width bspace) 2)) (+ height 20) (place-image restart (- (posn-x center) (/ (image-width restart) 2)) (+ height 35) (place-image space (- (posn-x center) (/ (image-width space) 2)) (+ height 55) (place-image pause (- (posn-x center) (/ (image-width pause) 2)) (+ height 70) s))))))] (draw-restart/pause (draw-status2 (draw-status1 (add-line (place-image (rectangle width 100 'solid 'black) (posn-x center) (+ 50 (* (posn-y center) 2)) s) 0 height width height charcolor)))))) ;gravity : posn -> posn ;givena posn of position, will return a posn for gravity (define (gravity p) (let ([px (posn-x p)] [py (posn-y p)]) (make-posn (* px (/ G (+ (* px px) (* py py)))) (* py (/ G (+ (* px px) (* py py))))))) ;who-is-dead?-text : number -> string ;given a number representing a dead?, will return the corresponding string (define (who-is-dead?-text d) (cond [(boolean? d) ""] [(= d 0) "You both lose"] [(= d 1) "The winner is Ship2"] [(= d -1) "The winner is Ship1"] [else ""])) ;who-is-dead? : dead? -> number or false ;given a dead?, will return 1 if ship1 died, -1 if ship2 died, ;0 if both died, or false if no one died yet (define (who-is-dead? d) (local [(define ship1 (dead?-ship1 d)) (define ship2 (dead?-ship2 d))] (cond [(and ship1 ship2) 0] [ship1 1] [ship2 -1] [else false]))) ;Bullets----------------------------------------------------------------------- ;move-bullet : bullet -> bullet ;given a bullet, will advance it from p to p' by v. ;bullet movement ;p = [px + vx * (px*G)/(px^2 + py^2), py + vy * (px*G)/(px^2 + py^2)] (define (move-bullet b) (local [(define p (bullet-p b)) (define v (bullet-v b)) (define g (gravity p)) (define px (posn-x p)) (define py (posn-y p)) (define vx (posn-x v)) (define vy (posn-y v)) (define ax (posn-x g)) (define ay (posn-y g)) (define vxf (+ (posn-x v) ax)) (define vyf (+ (posn-y v) ay)) (define pxf (+ px vxf)) (define pyf (+ py vyf))] (make-bullet (make-posn pxf pyf) (make-posn vxf vyf)))) ;move-bullets : [List of bullet] -> [List of bullet] ;given a list of bullets, will advance each one forward ;this also checks to see that the bullet is still on the screen (define (move-bullets l) (cond [(empty? l) empty] [else (local [(define bullet (first l)) (define brest (rest l))(define xcenter (posn-x center)) (define ycenter (posn-y center)) ;on-screen? : bullet -> boolean ;given a bullet, will return if it on screen (define (on-screen? b) (and (<= (- xcenter) (posn-x (bullet-p b)) xcenter) (<= (- ycenter) (posn-y (bullet-p b)) ycenter)))] (filter on-screen? (map move-bullet l)))])) ;fire-bullet : ship [List of bullet] -> ship ;given a ship, and a list of bullets, ;will add a newly fired bullet to the list (define (fire-bullet s lob) (let ([br (+ shipr 20)] [p (ship-p s)] [theta (ship-theta s)] [bp (+ shipr 1)]) (cons (make-bullet (make-posn (+ (posn-x p) (* bp (cos theta))) (+ (posn-y p) (* bp (sin theta)))) (make-posn (* br (cos (ship-theta s))) (* br (sin (ship-theta s))))) lob))) ;draw-bullet : bullet scene -> scene ;given a bullet and a scene, it will draw the bullet on the scene (define (draw-bullet b s) (local [(define p (bullet-p b))] (place-image (circle 1 'solid 'purple) (+ (posn-x center) (posn-x p)) (- (posn-y center) (posn-y p)) s))) ;draw-bullets : [List of bullet] scene -> scene ;given a list of bullets and a scene, will draw the bullets unto the scene (define (draw-bullets l s) (if (empty? l) s (draw-bullet (first l) (draw-bullets (rest l) s)))) ;Ships------------------------------------------------------------------------- ;move-ship : ship -> ship ;given a ship, will advance it forward one tick ;ship movement ;p = [px + vx * ((thrust * (cos theta)) + (px*G)/(px^2 + py^2)), ; py + vy * ((thrust * (sin theta)) + (px*G)/(px^2 + py^2))] (define (move-ship s) (local [(define p (ship-p s)) (define v (ship-v s)) (define thrust (ship-thrust s)) (define theta (ship-theta s)) (define g (gravity p)) (define px (posn-x p)) (define py (posn-y p)) (define ax (+ (* thrust (cos theta)) (posn-x g))) (define ay (+ (* thrust (sin theta)) (posn-y g))) (define vxf (+ (posn-x v) ax)) (define vyf (+ (posn-y v) ay)) (define pxf (+ px vxf)) (define pyf (+ py vyf)) (define xcenter (posn-x center)) (define ycenter (posn-y center)) (define pf (make-posn (cond [(<= (- xcenter 1) pxf) (- (- xcenter 1))] [(>= (- (- xcenter 1)) pxf) (- xcenter 1)] [else pxf]) (cond [(<= (- ycenter 1) pyf) (- (- ycenter 1))] [(>= (- (- ycenter 1)) pyf) (- ycenter 1)] [else pyf])))] (make-ship pf (make-posn vxf vyf) thrust theta))) ;rotate-ship : ship dir -> ship ;given a ship and a direction, rotates the ship in that direction (define (rotate-ship s d) (if (symbol=? 'left d) (make-ship (ship-p s) (ship-v s) (ship-thrust s) (+ (ship-theta s) (/ (* 5 pi) 180))) (make-ship (ship-p s) (ship-v s) (ship-thrust s) (- (ship-theta s) (/ (* 5 pi) 180))))) ;thrust-ship : ship-> ship ;given a ship, will return a ship with a new thrust (define (thrust-ship s) (let ([thrust (ship-thrust s)] [p (ship-p s)] [v (ship-v s)] [theta (ship-theta s)]) (cond [(= thrust thrust-off) (make-ship p v thrust-on theta)] [(= thrust thrust-on) (make-ship p v thrust-off theta)]))) ;draw-ship : ship boolean -> image ;given a ship and a scene, will put the ship's image on the scene ;if the boolean is true, it means that we are drawing ship 1 (define (draw-ship ship charcolor thrust t) (local [;the tip of the ship (define a (* (+ 5 shipr) (cos t))) (define b (- (* (+ shipr 5) (sin t)))) (define a3 (* (/ shipr 2) (cos t))) (define b3 (- (* (/ shipr 2) (sin t)))) ;right fin (define a1 (* shipr (cos (+ t tipTOfin)))) (define b1 (- (* shipr (sin (+ t tipTOfin))))) ;left fin (define a2 (* shipr (cos (+ t topTOleftFIN)))) (define b2 (- (* shipr (sin (+ t topTOleftFIN))))) ;position-ship : theta -> image ;given a theta of a ship it will position it (define (position-ship) (add-line (add-line (add-line (circle shipr 'outline 'black) a b a1 b1 charcolor) a b a2 b2 charcolor) a1 b1 a2 b2 charcolor)) ;show-afterburn : image -> image ;given an image of a ship, will add afterburners if needed (define (show-afterburn i) (if (> thrust 0) (add-line (add-line i a2 b2 (- a) (- b) 'red) a1 b1 (- a) (- b) 'red) i))] (show-afterburn (position-ship)))) ;place-ships : ship ship scene -> scene ;given ship1, ship2 and the scene, will place the ships at the correct place (define (draw-ships s1 s2 s) (place-image (draw-ship ship1 'blue (ship-thrust s1) (ship-theta s1)) (+ (posn-x center) (posn-x (ship-p s1))) (- (posn-y center) (posn-y (ship-p s1))) (place-image (draw-ship ship2 'green (ship-thrust s2) (ship-theta s2)) (+ (posn-x center) (posn-x (ship-p s2))) (- (posn-y center) (posn-y (ship-p s2))) s))) ;Collisions-------------------------------------------------------------------- ;posn-close? : posn posn number-> boolean ;returns true if the distance between the two posns ;is less than the number (define (posn-close? p1 p2 n) (local [(define distance (sqrt (+ (sqr (- (posn-x p2) (posn-x p1))) (sqr (- (posn-y p2) (posn-y p1))))))] (< distance n))) ;ships-crashed? : ship ship -> boolean ;given two ships, will check to see if they crashed into each other or not (define (ships-crashed? s1 s2) (posn-close? (ship-p s1) (ship-p s2) (* shipr 2))) ;bullets-hit-ship? : ship [List of bullet] -> boolean ;given a list of bullets, will return whether any of the bullets hit the ship (define (bullets-hit-ship? s lob) (local [(define (bullet-hit-ship? b) (posn-close? (ship-p s) (bullet-p b) shipr))] (ormap bullet-hit-ship? lob))) ;collisions? : world -> dead? ;given a world, will return a dead? with the which ship died (define (collisions? w) (local [(define s1 (world-ship1 w)) (define s2 (world-ship2 w)) (define b (world-bullets w)) (define d (world-dead? w))] (cond [(ships-crashed? s1 s2) (make-dead? true true)] [(bullets-hit-ship? s1 b) (make-dead? true false)] [(bullets-hit-ship? s2 b) (make-dead? false true)] [else d]))) ;World------------------------------------------------------------------------- ;next-world : world -> world ;given a world, will advance it once tick (define (next-world w) (local [(define ship1 (world-ship1 w)) (define ship2 (world-ship2 w)) (define bullets (world-bullets w)) (define paused? (world-paused? w)) (define dead? (world-dead? w)) (define colide (collisions? w)) (define score (world-score w)) (define dead?num (who-is-dead? dead?))] (cond [paused? w] [(not (boolean? dead?num)) w] [else (make-world (move-ship ship1) (move-ship ship2) (move-bullets bullets) paused? colide (update-score (who-is-dead? colide) score))]))) ;draw-world : world -> scene ;given a world, will return its image representation (define (draw-world w) (local [(define ship1 (world-ship1 w)) (define ship2 (world-ship2 w)) (define bullets (world-bullets w)) (define paused? (world-paused? w)) (define dead? (world-dead? w)) (define dead?num (who-is-dead? dead?)) (define score (world-score w)) (define wimage (draw-ships ship1 ship2 (draw-bullets bullets (draw-score score (draw-status w background))))) (define pausedText "PAUSED") (define pausedImage (text pausedText 20 'white)) (define dead?Text (who-is-dead?-text dead?num)) (define dead?Image (text dead?Text 20 'white)) (define again?Text "AGAIN?") (define again?Image (text again?Text 15 'white)) (define enterText "Press enter to play again") (define enterImage (text enterText 10 'white))] (cond [paused? (place-image (text pausedText 20 'white) (- (/ width 2) (/ (image-width pausedImage) 2)) 100 wimage)] [(not (boolean? dead?num)) (place-image dead?Image (- (/ width 2) (/ (image-width dead?Image) 2)) 100 (place-image again?Image (- (posn-x center) (/ (image-width again?Image) 2)) 150 (place-image enterImage (- (/ width 2) (/ (image-width enterImage) 2)) 170 wimage)))] [else wimage]))) ;key-handler : world button -> world ;given a world and a pressed button, ;will return the changed worled according to the button (define (key-handler w b) (local [(define ship1 (world-ship1 w)) (define ship2 (world-ship2 w)) (define bullets (world-bullets w)) (define paused? (world-paused? w)) (define dead? (world-dead? w)) (define dead?num (who-is-dead? dead?)) (define score (world-score w))] (cond [(key=? b #\return) (make-world (world-ship1 w1) (world-ship2 w1) (world-bullets w1) (world-paused? w1) (world-dead? w1) (world-score w))] [(key=? b #\backspace) w1] [(not (boolean? dead?num)) w] [(key=? b #\space) (make-world ship1 ship2 bullets (not paused?) dead? score)] [paused? w] [(key=? b #\a) (make-world (rotate-ship ship1 'left) ship2 bullets paused? dead? score)] [(key=? b #\d) (make-world (rotate-ship ship1 'right) ship2 bullets paused? dead? score)] [(key=? b #\j) (make-world ship1 (rotate-ship ship2 'left) bullets paused? dead? score)] [(key=? b #\l) (make-world ship1 (rotate-ship ship2 'right) bullets paused? dead? score)] [(key=? b #\s) (make-world (thrust-ship ship1) ship2 bullets paused? dead? score)] [(key=? b #\k) (make-world ship1 (thrust-ship ship2) bullets paused? dead? score)] [(key=? b #\w) (make-world ship1 ship2 (fire-bullet ship1 bullets) paused? dead? score)] [(key=? b #\i) (make-world ship1 ship2 (fire-bullet ship2 bullets) paused? dead? score)] [else w]))) ;Big-Bang---------------------------------------------------------------------- (big-bang width full-height .1 w1) (on-tick-event next-world) (on-key-event key-handler) (on-redraw draw-world)