Lab 2: Animations

Doug has told you and your partner that a criminal’s fingerprint is somewhere in the crime scene! Your job is to use your handy-dandy magnifying glass to find the fingerprint. You have to find it as fast as possible to solve the case!

Setup

Include this at the top of your program:

include reactors
include image

data Posn:
    | posn(x :: Number, y :: Number)
end

A posn is a datatype that probably looks familiar: it is a way to represent coordinates on a grid by using x and y values.

Note: By defining a posn datatype, you are telling the program, “This is something that will appear later in my program, and I want a way to build the same thing multiple times with different characteristics.” So, when you make a posn in your program, you are building the same structure each time: a variable of type posn! The difference between the posn variables you make are the x, y values entered as parameters (inputs).

To create a new posn with an x-value of 3 and a y-value of 4:
my-posn = posn(3,4)

To access the x- and y-values of my-posn:

>> my-posn.x
3
>> my-posn.y
4

Try this out in the interactions window! What kind of values can you use for x and y? What happens if you try to add a z coordinate?

Note: If you get confused about posn or reactor, check out the help doc we made (listed under lab 2 on our course website).

Part 1

1.1: Building the view

The first thing you need in order to start sleuthing is a clean surface and your magnifying glass.

Write a function crime-scene that creates a square white background, and place your magnifying glass and fingerprint somewhere on it. They should be on opposite ends of the screen.

You might notice that this function does not have any inputs (something that also came up on homework 2). When might it be useful to write a function without any inputs?

Pyret has a useful function image-url for loading images when you don’t want to build an entire graphic from scratch (URL stands for “Universal Resource Locator”… a web address!). image-url takes in the url of an picture from the web and outputs it as a Pyret Image. Once it’s loaded, you can manipulate it the way you would any other Image from the image library.

We used these images:

MAGNIFYING-GLASS-URL = "https://cdn.pixabay.com/photo/2015/10/28/13/27/magnifying-glass-1010537_960_720.png"
FINGERPRINT-URL = "https://cdn.pixabay.com/photo/2013/07/12/15/36/fingerprint-150159_960_720.png"

magnifying-glass-image = image-url(MAGNIFYING-GLASS-URL)
fingerprint-image = image-url(FINGERPRINT-URL)

But you’re welcome to find (or build) your own!

Hint: You might find the function place-image more useful than overlay-image, as it allows you to fix one image (the fingerprint) and put another (the magnifying glass) somewhere on the background.
Note: You must use a posn to store the positions of your magnifying glass and of your fingerprint.

1.2: Random placement

Your partner is pretty forgetful and keeps leaving the magnifying glass in random places all over the crime scene.

Use num-random(n) to change your function so that your magnifying glass is placed at a random spot on your background.


CHECKPOINT:

Call over a TA once you reach this point.


Part 2

2.1: Adding movement

Now it’s time to actually search for the fingerprint! To do so, you will be using a Pyret reactor, which we talked about in the presentation.

Here is a basic example for reference:

my-reactor =
reactor:
    init: init-posn,
    to-draw: crime-scene,
    on-tick: solve-case
end

Change your code from Problem 1 so that your image is drawn as part of a reactor. To do this, you must create two functions:

To use your reactor, run your program and then type interact(my-reactor) in the interactions window.


CHECKPOINT:

Call over a TA once you reach this point


2.2: Move your magnifying glass

If you can’t move the magnifying glass around, how will you find the fingerprint?

So far, our reactor uses solve-case to move the magnifying glass every few milliseconds. In a game, however, we also want to move the magnifying glass as a player presses keys. Brainstorm which keys would you like to use and how the posn of a magnifying glass change based on which key was pressed. Pick only two keys to start (you can add more later).

Write a function move-glass that takes in a posn and a string and returns a new posn. The string represents a keypress, the input posn represents the initial position of an object, and the output posn represents the object’s new position after the keypress.

Below is a list of some string-to-key mappings:

You can also use each key’s literal character to represent that key. For example, “a” is the a key, “b” is the b key, etc.

Add the move-glass function to your reactor like this:

my-reactor =
reactor:
    init: init-posn,
    to-draw: crime-scene,
    on-tick: solve-case,
    on-key: move-glass
end

CHECKPOINT:

Call over a TA once you reach this point


2.3: Additional features

Implement both of these additional features. If you still have time, come up with your own!

Wrap-around:
What happens if the magnifying glass misses the fingerprint and flies off the screen? (Try this out using your current reactor.)

Modify your on-tick function so that if the magnifying glass flies off the screen, it wraps around on the other side.

Collisions:
Write a function found-it that takes in the position of the magnifying glass and returns a Boolean indicating whether the magnifying glass has found (or collided with) the fingerprint.

Add it to your reactor like this:

my-reactor =
reactor:
    init: init-posn,
    to-draw: crime-scene,
    on-tick: solve-case,
    on-key: move-glass,
    stop-when: found-it
end

Next, congratulate your partner for a successful solve by modifying to-draw so that if there is a collision (found-it returns true), the image becomes a congratulatory image of your choice.


CHECKPOINT:

Call over a TA once you reach this point


Case closed!

Thanks to your excellent detective work, the criminal’s fingerprint has been found, Doug is happy, and your latest case is one step closer to being solved!