Tuesday, 14 August 2012

Special Exercise - Space Invaders Part 2


Design a complete Space Invaders Game  - Part 2


For the second iteration I am going to add a big-bang function to the game and draw the game as a game world. The game world current consists of a collection of ufos and our tank.


As adding the game code here for drawing the world is pretty simple I have also added a function that will programatically generate a collection of ufos. This is to save specifying the initial position of each ufo by hand.  To start with I am drawing 3 rows of how ever many ufos will currently fit.

Code






(require racket/base)
(define-struct ufo (x y) #:transparent)
(define-struct tank (x y) #:transparent)
(define-struct world (ufos tank))
; physical constants
(define WIDTH 300) ; the maximal number of blocks horizontally
(define HEIGHT 300) ; the maximal number of blocks horizontally


; graphical constants
(define SIZE 10) ; the size of the block making up the ufo
(define UFO-WIDTH 50)


(define BLOCK ; they are rendered as red squares with black rims  
  (overlay (rectangle (- SIZE 1) (- SIZE 1) "solid" "red")
           (rectangle SIZE SIZE "outline" "black")))






(define TEST-UFO (make-ufo 30 30))   ; for testing
(define TEST-UFOS (list (make-ufo 30 30)
                        (make-ufo 80 30)
                        (make-ufo 130 30))); for testing


; initial tank position should be middle of the screen, one block up from 
; the bottom
(define TANK (make-tank (/ WIDTH 2) (- HEIGHT (* SIZE 2))))


; Creates a row of ufos on the supplied y-cordinate
; Integer Ufos -> Ufos
(define (make-ufos-for-y y-cordinate ufos)
  (cond ((empty? ufos) (make-ufos-for-y y-cordinate 
                          (cons (make-ufo 20 y-cordinate) ufos)))
        ((> (+ (ufo-x (first ufos)) UFO-WIDTH) WIDTH) ufos)
        (else (make-ufos-for-y y-cordinate 
                (cons (make-ufo (+ (ufo-x (first ufos)) UFO-WIDTH)
                                 y-cordinate) ufos )))))




; create a complete collection of rows of ufos
; Integer Ufos -> Ufos
(define (make-ufos rows ufos)
  (cond ((= 0 rows) ufos)
         (else (make-ufos (- rows 1) 
                          (append ufos (make-ufos-for-y ( * rows 30) empty))))))
                                     






; draw a triangular shaped alien
(define (draw-ufo ufo background)
  (place-image/align BLOCK (ufo-x ufo) (ufo-y ufo) "left" "bottom"
      (place-image/align BLOCK (- (ufo-x ufo) SIZE) (ufo-y ufo) "left" "bottom"
       (place-image/align BLOCK (+ (ufo-x ufo) SIZE) (ufo-y ufo) "left" "bottom"
       (place-image/align BLOCK (ufo-x ufo) (+ (ufo-y ufo) SIZE) "left" "bottom"
                background)))))


; draw a tank shaped tank
(define (draw-tank tank background)
  (place-image/align BLOCK (tank-x tank) (tank-y tank) "left" "bottom"
   (place-image/align BLOCK (- (tank-x tank) SIZE) (tank-y tank) "left" "bottom"
    (place-image/align BLOCK (+ (tank-x tank) SIZE) (tank-y tank) "left" "bottom"
     (place-image/align BLOCK (tank-x tank) (+ (tank-y tank) SIZE)
        "left" "bottom"
      (place-image/align BLOCK (- (tank-x tank) SIZE) (+ (tank-y tank) SIZE ) 
          "left" "bottom"
       (place-image/align BLOCK (+ (tank-x tank) SIZE) (+ (tank-y tank) SIZE) 
           "left" "bottom"
        (place-image/align BLOCK (tank-x tank) (- (tank-y tank) SIZE) 
           "left" "bottom"
          background))))))))


; draw the collection of ufos on to the background.
; a simple recursive function
; Ufos Scene -> Scene
(define (draw-ufos ufos background)
           (cond ((empty? ufos)  background)
                 (else (draw-ufo (first ufos) (draw-ufos (rest ufos) 
                        background)))))




; Draws the world as a scene
; World -> Scene
(define (render-scene world)
  (draw-ufos (world-ufos world)
             (draw-tank (world-tank world) (empty-scene WIDTH HEIGHT))))


; This is the big bang function that drives the game.
(define (space-invaders-main rate)
  (big-bang (make-world (make-ufos 3 empty) TANK)
            (to-draw    render-scene)))


(space-invaders-main 0.2)





; tests


; simple example - a single block in an empty landscape
(check-expect (draw-ufo TEST-UFO (empty-scene WIDTH HEIGHT))
  (place-image/align BLOCK (ufo-x TEST-UFO) (ufo-y TEST-UFO) "left" "bottom"
      (place-image/align BLOCK (- (ufo-x TEST-UFO) SIZE) (ufo-y TEST-UFO) 
         "left" "bottom"
       (place-image/align BLOCK (+ (ufo-x TEST-UFO) SIZE) (ufo-y TEST-UFO) 
         "left" "bottom"
       (place-image/align BLOCK (ufo-x TEST-UFO) (+ (ufo-y TEST-UFO) SIZE) 
         "left" "bottom" 
                (empty-scene WIDTH HEIGHT))))))


; test drawing three ufo's via the draw-ufos function
(check-expect (draw-ufos TEST-UFOS (empty-scene WIDTH HEIGHT))
              (draw-ufo (first TEST-UFOS) 
                (draw-ufo (second TEST-UFOS) 
                   (draw-ufo (third TEST-UFOS) (empty-scene WIDTH HEIGHT)))))


; test the tank drawing works
(check-expect (draw-tank TANK (empty-scene WIDTH HEIGHT))
  (place-image/align BLOCK (tank-x TANK) (tank-y TANK) "left" "bottom"
   (place-image/align BLOCK (- (tank-x TANK) SIZE) (tank-y TANK) "left" "bottom"
    (place-image/align BLOCK (+ (tank-x TANK) SIZE) (tank-y TANK) "left" "bottom"
     (place-image/align BLOCK (tank-x TANK) (+ (tank-y TANK) SIZE) "left" "bottom"
      (place-image/align BLOCK (- (tank-x TANK) SIZE) (+ (tank-y TANK) SIZE ) 
           "left" "bottom"
       (place-image/align BLOCK (+ (tank-x TANK) SIZE) (+ (tank-y TANK) SIZE) 
            "left" "bottom"
        (place-image/align BLOCK (tank-x TANK) (- (tank-y TANK) SIZE)
            "left" "bottom" 
        (empty-scene WIDTH HEIGHT)))))))))



No comments:

Post a Comment