RU beehive logo ITEC dept promo banner
ITEC 380
2017fall
ibarland

homelecturesrecipeexamshwsD2Lbreeze (snow day; distance)

lists
handling lists
frogger

Due: Oct.27 (Fri) 14:00, for 10% additional credit
Accepted until Oct.28 (Sat)(!) 23:59

This homework uses racket to complete the frogger program started in frogger. You'll presumably include parts of your hw04 (or, the posted solution); please clearly mark1 where the new portion of your solution begins.

Follow all steps of the-design-recipe—the design recipe: final version, including templates and unit-tests. For a function's purpose-statement, you might be able to copy/paste from this hw assignment. Tests are worth approximately half the points2 List-processing functions should have at least three tests (lists of length 0, 1, and many). However, if a struct merely contains a list (like world below), and it calls a helper function on that field, then you don't need to test different structs containing lists of varying lengths; your unit-tests for the helper already exercised that aspect.

  1. (8pts) Our frogger program will need to deal with a multiple trucks.
    1. Give a data definition for a list-of-trucks, examples of the data, and a template for any function which takes a list-of-trucks as input
    2. Write the function move-trucks (note the plural “s”), which maps move-truck onto a list of trucks:
      ; move-trucks: list-of-trucks -> list-of-trucks
      ; Return a list of all the trucks, where each has been moved.
      
      Test cases are required, of course.
    3. Write the function draw-truck, which takes in one, single truck and a background image 3, and returns that image with an image representing the truck on placed appropriately top of it.
      Note:A rectangle is a perfectly fine way to draw a truck! You're certainly free to make it look fancier, but it's not worth more credit. And of course, if you use an image-file from somewhere, make sure you have the right to do so, and cite the source.

      Use place-image, since that crops the result appropriately.

    4. Write the function draw-trucks, which takes in a list of trucks and a scene, and returns a new scene with every truck in the list overlayed onto it.
  2. (7pts) Hmm, I guess we might also need to represent a frog.
    1. Give a data-definition / examples / template for a frog.

      Hint: Don't include an image in the structure. Instead, just include some way to indicate which way the frog is facing. I suggest using "left", "up", etc.; since these strings also happen to be key-events, this will make for some convenient code4 for frog-handle-key below.

    2. Write draw-frog : frog, image → image which draws a frog onto the provided image.

      I recommend having a helper-function get-frog-image : string -> image which, given a string indicating a frog’s direction, returns an appropriate image. (If you do, include tests for it.)

      The image library provides rotate and scale. Performance note: if you are using a bitmap for the frog-image, rotateing it every time you draw can be expensive. You may want to pre-compute the four images you'll need.

    3. Write draw-prince(ss), similar to draw-truck and draw-frog.
    4. Write frog-handle-key : frog, key-event → frog. (You'll need to (require 2htdp/universe), to get the function key=?.) This function captures how the frog should react when a key is pressed. Note that it does not deal with any time passing (tick-events) — only key-events. (Keypresses can cause the frog to re-face, or to move — or both, depending on what you want to do.)
  3. (9pts) We need to represent an entire state of the game; we'll call this a “world”.
    1. Give the data-definition, examples, and template for worlds.
    2. Write the function update-world : world → world which returns a new world one “tick” later from the input world.
    3. Then, write world-handle-key : world, keypress → world which returns a new world based on handling a single keypress event.
    4. Finally, write draw-world : world → image. (You might want to use empty-scene to create the initial background-image that will get passed to a helper function.)
    Of course, each of these functions will be straightforward, since they'll mostly call the helpers already written.

    (including test cases5).

  4. (20pts) More trucks, and miscellania:
    1. Write the function truck-off-screen?, which returns whether or not a given truck is entirely off the size of the image you use in draw-world. Note that this function neither consumes nor returns images; however it may use numbers (or better: named constants) which are also used inside draw-world.

      To help you, you can use the following code for overlap?, which returns whether or not two rectangles overlap.
      (There's no reason you couldn't write overlap? yourself; after careful test cases the code becomes straightforward. Howewver, this homework is long enough as-is.

    2. Write the function
      ; remove-off-screen-trucks : list-of-trucks -> list-of-trucks
      ; Return a list containing only those trucks are not off-screen.
      
    3. Write the function replenish-truck : list-of-trucks -> list-of-trucks, which sometimes adds new trucks to the given list. You can use whatever criterion you like, but I recommend: If a list's length falls below some threshold, then add (exactly one) new truck. (You can call length : list -> number; we wrote “my-length” in class, but “length” is also built-in.)

      Note that you can, if you want, create new trucks with some random attributes; see the built-in function random. … However, this makes unit-testing problematic, since you don't exactly know the expected result6. So for this problem, your tests might instead sometimes only check that the length of the result is as expected. (Of course, if you do know the exact expected result for certain inputs -- e.g. when the list is already longer than some threshold -- then do check for that full result.)

    4. Modify update-world so that it (a) moves trucks, then (b) removes trucks which are off the screen, and finally (c) replenishes trucks.
      Add one test case for this.
    5. Write truck-hitting-frog? : truck frog -> boolean.
      You can use overlap? on the truck's and frog's “bounding box”.7 Note that this function neither consumes nor returns images; test cases are required.
    6. Write trucks-hitting-frog? : list-of-trucks frog -> boolean, which returns whether any truck in the list is hitting the given frog.
    7. Write game-over? : world -> boolean. The game is over when the frog is hit by a truck, or the frog has reached the princess.
    8. Modify our old update-prince(ss) so that s/he teleports to a random location, after being in the same spot long enough.
  5. [1pt] If you evaluate
    (big-bang some-initial-world
        [on-tick update-world]
        [on-draw draw-world]
        [on-key update-world-handle-key]
        [stop-when game-over?])
      
    you'll be playing frogger. (Only do this after you pass all your tests for individual functions; no debugging is necessary here, if you've properly tested your functions.)

    Optional / for fun only: If you download and then (require "student-extras.rkt"), then you get the function play-sound-then : string, any/c → any/c8 which takes in the name of a sound file and any other value, starts that sound-file playing, and then just returns the other value. E.g. (play-sound-then "/full/path/to/some-file.wav" (sqrt 16)) will play a sound, then return 4.

    (Some free-to-use sound files can be found at, say, mediacollege.com or partnersinrhyme.com; be sure to site your sources, as per their licenses, and common courtesy.)


1 DrScheme's Insert » Large letters… plays nicely with View » Show Program Contour.      
2 Unit tests are worth about half the points since they're about half the effort/time spent. And yet, they pay for themselves in not needing to step through debuggers later.      
3Perhaps a house .      
4 Convenient, at the price of not being abstract: if we change our program to use (say) "a", "w", "d", "s" to control the frog, then our code would need more substantial revision.

In an even more general setting, there might be a dictionary (e.g. java.util.Map) which translates key-events to frog-directions; this would let a user set preferences for keyboard controls, would scale well to things like having multiple-users, working with non-keyboard controllers, etc.. But for this assignment, we’ll content ourselves with hard-coded keypresses.

     
5Between cleverly-chosen examples and cut/paste, it really isn't too difficult to create test cases. Note that since more thorough tests for (say) update-trucks are provided elsewhere, the test cases for update-world don't need to test worlds with a wide variety of list-of-trucks; two or even one world is sufficient in this case.

In general, white-box test cases for world-handle-key would only focus on any interesting/odd interplay about how different list-of-trucks might interact with certain prince(sse)s. However, in our game, updating trucks and updating the princess is entirely orthogonal, so there is no interplay to worry about. One test case should suffice to test that we're doing the world accessing/constructing correctly.

     
6 Here's how you could actually have unit-tests: Include the random-number-generator (“rng”) as one of the function's inputs. (Presumably it would be an optional-input, using a default system rng if not explicitly provided.) Then when testing, you can pass in a rng which you know is about to generate the number (say) 17, and thus you can predict the overall-function's result. In fact, you might pass in a dummy-rng which always generates 17. In testing/SE terminology, this is called “dependency injection” or “adding a seam”.      
7 If you really want to do more sophisticated collision detection, you are welcome to. Presumably you'd use more sophisticated shapes than rectangles which would presumably correspond to the shape you're using in your draw- methods.      
8 Playing a sound is a side-effect -- it doesn’t return a value; instead it changes state. (In this case, it changes variables inside the sound-card and its driver.) Beginning-student eschews side-effects, which might be why it's not already built-in.

If you look at the code provided inside student-extras.rkt, you'll actually see it merely calls the play-sound which is in full-racket, (along with begin, a construct only needed in languages with side-effects). This illustrates an interesting technical point: Your program is in beginning-student, but the file it's requireing is actually in a different language (full-racket). Racket's module system is novel in that it borders on making calls to libraries in other languages! (A "foreign function interface".)

Racket carefully makes sure you can't have interactions that bleed over between languages. You can even include a file that defines a macro which expands at run-time into an expression in the other language, but still use this macro inside your language. (Imagine: your program happens to use an identifier named “begin” and it calls the play-sound-then which expands to code using both full-racket's begin and your program's identifier. Racket knows these are different identifiers, and keeps track of their separate bindings.) This is one reason that racketeers regard racket not so much as a language, but as a way of designing your own languages (that happens to include access to a fully-featured, industrial-strength language).

     

homelecturesrecipeexamshwsD2Lbreeze (snow day; distance)


©2017, Ian Barland, Radford University
Last modified 2017.Oct.26 (Thu)
Please mail any suggestions
(incl. typos, broken links)
to ibarlandradford.edu
Rendered by Racket.