On this page:
1 Tower Defense
1.1 Additional Preliminaries
1.2 Problem Description

Problem Set 13

Last updated: Tue, 14 Apr 2015 14:47:36 -0400

OUT: Monday 4/13/2015, 9pm EST
DUE: Monday 4/20/2015, 9pm EST

1 Tower Defense
1.1 Additional Preliminaries

Save your solutions for this problem to a file named defense.rkt.
Run the following expression (you must have required extras.rkt) to check that your file is properly named and is in the proper directory:

(check-location "13" "defense.rkt")

Add these additional provides at the top of your file (below the requires), so that we can test your solution:
(provide Unit<%>)
(provide StatefulWorld<%>)
(provide mk-world)
(provide mk-ally)
(provide mk-enemy)
(provide mk-merc)

1.2 Problem Description

The main objective of this assignment is to create a small "tower defense" game using OOP.
There’s a small war going on and you are on top of a tower with a cross bow, guarding a base. You can see 3 types of units advancing towards you:
  • AllyUnit: These are your reinforcements arriving to help. Represent them as a solid green square with a side of 20 pixels.

  • EnemyUnit: These are enemies attacking the base. Represent them as solid red circle with a radius of 12 pixels.

  • MercenaryUnit: These units switch sides from enemy to ally every 3 ticks.
    • When the game begins they behave and look like an AllyUnit.

    • On the third tick, they turn into an EnemyUnit.

    • This cycle repeats for the remainder of the game. Specifically, imagine there exists a tick-merc function, an INITIAL-MERC constant, and ally? and enemy? predicates. Then the following tests should pass. NOTE: This code is meant only to demonstrate the timing of the transitions. It may or may not be a good idea to write these actual functions.
      (check-pred ally? INITIAL-MERC)
      (check-pred ally? (tick-merc INITIAL-MERC))
      (check-pred ally? (tick-merc (tick-merc INITIAL-MERC)))
      (check-pred enemy? (tick-merc (tick-merc (tick-merc INITIAL-MERC))))

The crossbow should be represented as a target that mirrors the movement of the mouse. Clicking on a unit such that the center of the target is within the bounds of the unit’s shape removes that unit from the game.
Eliminate EnemyUnits by clicking on them and allow AllyUnits to enter the base.
A unit "enters" the base if there is overlap between the unit’s shape and the base or if their edges touch.
For MercenaryUnits:
  • If at the time of entering the base if this unit is an enemy then you will lose points.

  • If at the time of entering the base if this is an ally then you will gain points.

  • EnemyUnit eliminated: ( +40 )

  • EnemyUnit reaches Base: ( -40 )

  • AllyUnit eliminated: ( -20 )

  • AllyUnit reaches Base: ( +20 )

  • MercenaryUnit eliminated:
    • when it is impersonating an EnemyUnit: ( +60 )

    • when it is impersonating an AllyUnit: ( -60 )

  • MercenaryUnit reaches Base:
    • when it is impersonating an EnemyUnit: ( -60 )

    • when it is impersonating an AllyUnit: ( +60 )

Displaying the game:
  • The World is displayed as a canvas of width = 400 and height = 500 pixels.

  • The Base is a yellow solid rectangle of width = 400 and initial height = 50 pixels and situated at the bottom of the canvas.

  • The score should be displayed in the middle of the base and should fit within the bounds of the base.

Other game rules:
  • The height of the base also represents your progress in the battle and should grow or shrink as the score increases or decreases such that height of base = 50 + score/5 pixels.

  • The game ends when either
    • height of base <= 10 pixels, i.e., score <= -200

    • height of base >= 500 pixels, i.e., score >= 2250

  • The aiming point is represented as two concentric circles and a "plus sign" crosshair. The inner and outer radii should be 5 and 10 pixels, respectively. The crosshair should be a vertical "plus sign" of height and width equal to the diameter of the outer circle.

  • As soon as an unit reaches the base it should be removed from the game state. A unit "reaches" the base when either their edges touch or any part of the unit overlaps with the base.

  • The game starts with some number of units at the top of the canvas. A new random unit should enter from the top of the canvas every 4 ticks. Entering units should have a random velocity between a specified min and max velocity, a center x coordinate within the canvas bounds, and a center y coordinate of 0.

Interfaces and functions to provide:
; Represents a unit in the game.
(define Unit<%>
  (interface ()
    ; get-loc : -> posn
    ; Returns the location of this unit as a posn.
    ; get-color : -> Color
    ; Returns the color of this unit.
; Represents a mutable world object.
(define StatefulWorld<%>
  (interface ()
    ; on-tick! : -> Void
    ; EFFECT: mutates this world to its next state.
    ; on-mouse! : Coordinate Coordinate MouseEvent -> Void
    ; EFFECT: mutates this world to its next state from the given mouse parameters.
    ; target-loc : -> posn
    ; Returns the center of the target as a posn.
    ; get-units : -> ListOf<Unit<%>>
    ; Returns the current units in this world.
    ; add-unit! : Unit<%> -> Void
    ; EFFECT: adds the given unit to the world
    ; get-base-height : -> Natural
    ; Returns the height of the base, in pixels.
; A Velocity is a Natural, representing Pixels/tick in the downward direction.
; mk-world : Velocity Velocity Natural -> StatefulWorld<%>
; Creates a world with num-units initial random units,
; where units have the specified min and max velocity.
; WHERE: minvel <= maxvel
(define (mk-world maxvel minvel num-units) ...)
; mk-enemy : posn Velocity -> Unit<%>
; Creates an enemy unit with the given parameters.
; mk-ally : posn Velocity -> Unit<%>
; Creates an ally unit with the given parameters.
; mk-merc : posn Velocity -> Unit<%>
; Creates a mercenary unit with the given parameters.