RU beehive logo ITEC dept promo banner
ITEC 380
2016summerIII
ibarland

homelecturesrecipeexamshwsD2Lbreeze (snow day; distance)

hw05
Invaders I

Due Jun.28 17:45 Note: 15min before class (but intended to be completable by Jun.21).

Your name and the assignment-number must be in a comment at the start of the file, and your hardcopy must be stapled. All functions/data must include the appropriate steps of the-design-recipe—the design recipe: final version. In particular, test cases alone might be worth half the credit for a function. Unless otherwise indicated, two test cases will suffice for most problems, if they are testing different situations.

The following problems will form the basis of our space-invader program, which we'll complete in the next homework. You may base your answer on hw03b-soln.rkt (but you certainly don't need to); of course give credit and URL if you do so.

Complete problems in racket, except for 5 and 6 which you'll complete in Java and racket, as instructed. Your Java method should compile and run and be correct, but then you can paste it into a racket block-comment (#| |#) so that you only need submit one file. Make sure it is property formatted, of course.

  1. (3pts) Write the template for a bullet-processing function, in both racket and Java.
    (In Java, you'll have to comment out the template, since the “...” won't compile.)

  2. (5pts) In both racket and in Java: Write the function move-bullet (Java: Bullet#move) that, for the given bullet, returns a new bullet object one “tick” (frame) later (ignoring any boundaries/collisions).

    The Java and racket versions should both do the same thing: return a new object, rather than mutating any fields (Cf. derive-from in the lecture notes). The two versions should correspond as directly as possible (including all names being the same, up to idiomatic differences — racket prefers hyphens whereas Java uses camelCase).

    We won't use Java test-cases (since that entails overriding Object#equals, which is not really related to our problem, and is more involved than we'd prefer).

  3. (4pts) Aliens
    Write move-alien : alien, real, real → alien, which returns an alien whose (x,y) position is shifted by the indicated amount. It should ignore walls/boundaries. (We already have the data-definition from hw03b-soln.rkt, but you still need the per-function design-recipe steps of course).

  4. (3 pts) Cannons
    As discussed, a cannon might be represented as just a single real-number (see hw03b-soln.rkt). Write the function cannon-handle-key : cannon, keypress → cannon which returns a new cannon updated to handle a key event, moving the cannon either left, right, or not at all, depending.

    Since the documentation shows that key-events are (a certain subset of) strings, it's certainly possible to compare them via string=?. However, you might see that there is also the function key=?, which definitely feels more appropriate for this task (even though it works exactly the same as string=?, on key-events). In order to use key=?, you need to (require 2htdp/universe).

  5. Worlds:
    1. (2pts) Define a “world” structure which contains a cannon, one bullet, and (for now) exactly one alien.

      As usual for our data-definitions, make examples of the data (at least two), and a template.

    2. Give a template for a world-processing functions.

    3. (3pts) Write the function update-world : world → world which returns a new world one “tick” later.

      For the time being, you can have update-world simply move the world's alien to the right. Ultimately, this function will decide whether the alien(s) should be moving to the left or to the right (or, down) -- and to do that, we'll need to keep some further info (where?) which tracks which direction they're currently moving in. (And if you want to start implementing this now, that's totally fine — it'll save you time on the next homework, where we finish the game.)

      For now, you don't need test cases that involve the bullet colliding with anything — you can just have the bullet far away from any aliens or cannons.

    4. (2pts) Write the function world-handle-key : world, keypress → world which returns a new world updated to handle the keypress. (Should be easy -- mostly defers to cannon-handle-key, and your test cases will largely crib from that.)

  6. Drawing functions:
    1. (2pts) Write the function draw-alien : alien, image -> image, which takes an alien and a “background” image, and returns that background image with an alien drawn on top of it.
      hint:place-image is a handy function; it is similar to overlay/xy except that it crops the result to the background.
      hint: For test-cases, include drawing an alien that is: (a) in the middle of a small image; and (b) one that is mostly off the left-edge but has just a few pixels showing.
      Note: Here's an image you can (modify to) use in your test-cases, in addition to a solid rectangle or whatever else you might choose: house-with-flowers.rkt. If you place this file in the same directory as other functions, you can just (require "house-with-flowers.rkt"), and then use its exported id (“house-with-flowers”, coincidentally). You don't need to print this file.

    2. (2pts) Write the similar function draw-cannon : cannon, image → image (where, as mentioned in hw03b-soln.rkt, cannon might be just a number).

    3. (0pts) Write the function draw-world : world -> image, which (for the moment) draws the alien and cannon (only) onto a blank background. You may not call list, nor place-images.
      hint: Call draw-alien with an empty-scene for the background; call draw-cannon passing it the result of draw-alien as the second argument.
    4. (1pt) Write the function draw-bullet : bullet, image -> image.

    5. (3pts) Change your draw-world so that it incorporates drawing the bullet as well. (You don't need to include your previous part (c); you can update the code and test-cases directly. Part (c.) was just so that you'd understand how to combine the drawing of two things, before you try drawing three.)

  7. (5pts) To do collision detection, write a suitable helper function to detect when two rectangles overlap (e.g. the bullet's bounding-box and the region occupied by a brick). Make at least four test-cases for this.

    There are several reasonable ways to represent rectangles, but it helps if your representation aligns with the arguments needed for 2htdp/image's overlay/xy, which places an images according to its center. I recommend passing in four numbers for each rectangle, rather than the more heavyweight approach of defining a rectangle-struct.

    hint: I recommend using half-open intervals, to define a rectangular region: A rectangle centered at 40,50 that is 60x80 would be considered to have x-coordinates in [40-30,40+30) — up to (but not including) the endpoint. It's not that big a deal, but it does mean it's easy to tile the entire plane while neither overlapping at all, nor leaving any 1-pixel-wide (or even infitesimal) gaps.

    If doing so, a useful helper function to write might be (<=< a b c): is ab < c?

    cool hint, from robotics: We can reduce this problem involving two rectangles to a simpler one: checking if an expanded version of the first rectangle merely contains the center of the second.
    For example: consider a 20×30 rectangle whose center is at (500,400) and a second rectangle which is 80×60. These two rectangles overlap exactly when the second rectangle's center is inside the (20+80)×(30+60) rectangle centered at (500,400). Sketching this example on paper will help you understand why this works.

All the above should have their tests, as well as signatures and (brief) purpose statements. Only after all tests pass, the following should work (in racket):

(require 2htdp/universe)

(big-bang some-initial-world
          [on-key  world-handle-key]
          [on-tick update-world]
          [to-draw draw-world])


1 Your final program doesn't need to include any "transitional" results from the template: For example, you don't need the stubbed-out version of a function from step 5. However, you should still have passed through this phase.      

2 Bullet#move is the O.O.-ish notation for a (non-static) method move inside class Bullet      

3

On a future hw, we'll write “udpate-bullet”, which will account for collisions and going off-screen. That function will call this one as a helper.

     

4 The problem is that (in a class class Foo, we can't just override Foo#equals(Foo) (that would be overloading, not overriding); we have to write more:

  public boolean equals( /* Foo this, */ Object that ) {
    if (that==null) {
      return false;
      }
    else if (this==that) {
      return true; // short-circuit a common case
      }
    else if (that.getClass() != this.getClass()) {
      return false;  // CAUTION: unless we can be .equals to a subclass?
      }
    else {
      Foo thatt = (Foo)that;
      // NOW you can add your code that actually compares fields, say:
      return (this.someField==null ? thatt.someField==null : this.someField.equals( thatt.someField ))
          && (this.someOtherField==null ? thatt.someOtherField==null : this.someOtherField.equals(thatt.someOtherField)
          // If our constructors never create objects w/ `null`, we can shorten the preceding lines a bit.
          && …
          ;
      }
    }
      
Sheesh! And if that's not enough, remember: Whenever you override equals, you must also override hashCode. Thanks, Java! Here's a full explanation about overriding equals (with the hashcode discussion as Item 8).      

5We'll upgrade the world so that it contains a list-of-aliens in a future homework.      

6A comprehensive set of black-box test cases is much much more involved: one rectangle defines 9 regions of potential interest, plus 16 dividing line segments/points; the second rectangle can have its northwest corner in any of those 9+16 regions, and its southwest corner in many of those. From counting-skills learned in discrete math, I count (…lemme think…) 25*24/2 + 25 = 325 test cases, to be reasonably comprehensive. You need to provide about 1% of such tests, whew!      

7

The “grow-the-second-rectangle” trick is exactly analagous to determining if two circles overlap by: seeing if the second circle's center-point is located inside the first circle “grown” by the second circle's radius: that is, whether (x2,y2) is within r1+r2 of (x1,y1).

     

homelecturesrecipeexamshwsD2Lbreeze (snow day; distance)


©2015, Ian Barland, Radford University
Last modified 2016.Jun.28 (Tue)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Rendered by Racket.