Problem Set 11: Zombies
Submit this assignment to ps11 on Handin.
Note: Whenever you write a function in this class, follow the design recipe. You will be graded accordingly.
1 Approach and player movement
In this assignment, you will build a simple single-player video game. The player will try to escape hungry zombies for as long as possible.
In this section, you will build the part of the game which draws and moves the player in response to the mouse.
The position of the player icon in the scene will be represented by a Posn. The player will want to move to their icon to other positions (in order to avoid being eaten by a zombie). They will move their icon by clicking or dragging the mouse.
When the player clicks or drags on the scene, the icon will typically not appear in the new position instantly. Rather, the player will see their icon gradually move towards the position of the mouse click or drag. The player icon has a fixed speed, which is the distance (in pixels) it can move each time tick. The player icon will move in a straight line towards the mouse click or drag position.
Now, given a player position and a mouse click position, how do you compute the new player position after one tick? You will define a function approach to answer this question. This function approach will take two positions p and goal and a speed s and return an updated position p’.
To find p’, here is the basic idea. Determine the direction from p to goal (we will represent a direction as a point whose distance from the origin is 1). The direction from p’ to the goal should be the same, but the distance should be s pixels smaller. In this section, you will develop this idea.
; posn-sum : Posn Posn -> Posn ; posn-diff : Posn Posn -> Posn ; posn-scale : Number Posn -> Posn
Problem 2. Here we provide the definitions of three helper functions that compute distance, direction and a typical approach. (Note that these definitions may work only as well as your functions from Exercise 1.) Do not change these definitions.
If you have trouble understanding what these functions do, write examples and plot them on paper.
Provide the missing tests for dist and for approach-helper and the missing purpose statement for approach-helper. You may use check-within instead of check-expect if you prefer.
; dist : Posn Posn -> Number ; computes the distance between two Posns (define (dist p1 p2) (sqrt (+ (sqr (- (posn-x p1) (posn-x p2))) (sqr (- (posn-y p1) (posn-y p2)))))) (check-expect ??? ???) (check-expect ??? ???) ; direction : Posn Posn -> Posn ; computes the direction from p to goal, ; represented as a Posn whose distance from the origin is 1 (define (direction p goal) (posn-scale (/ 1 (dist p goal)) (posn-diff goal p))) (check-expect (direction (make-posn 230 240) (make-posn 200 200)) (make-posn -3/5 -4/5)) (check-expect (direction (make-posn 170 240) (make-posn 200 200)) (make-posn 3/5 -4/5)) (check-expect (direction (make-posn 230 160) (make-posn 200 200)) (make-posn -3/5 4/5)) (check-expect (direction (make-posn 170 160) (make-posn 200 200)) (make-posn 3/5 4/5)) ; approach-helper : Posn Posn Number -> Posn ; ??? (define (approach-helper p goal s) (posn-sum p (posn-scale s (direction p goal)))) (check-expect ??? ???) (check-expect ??? ???)
Problem 3. Develop a data definition Pworld and a struct called pworld with two fields. One field stores a Posn which is the player position. The other field stores a Posn which is the goal position towards which the player moves.
Problem 4. Define an image background. Next, design a function draw-pworld which takes a Pworld and returns an Image of the player icon positioned on the background according to the player position stored in the input Pworld.
Problem 5. Design a mouse-handler update-pgoal which takes a Pworld, two Numbers and a MouseEvent, and returns a Pworld. Either a "button-down" or "drag" event from the mouse will change the goal position towards which the player moves. Any other event will leave the world unchanged.
Problem 6. Define a number player-speed (we suggest a number between 2 and 10). Then design an tick-handler move-player which takes and returns a Pworld. move-player will move the player closer to the goal position stored in the input Pworld. Use approach-helper (for now) and player-speed.
Problem 7. Design a function run-pworld which takes an initial Pworld and runs big-bang with draw-pworld, update-pgoal (as the on-mouse function) and move-player (as the on-tick function).
Problem 8. Try using run-pworld to make sure that player movement is working. If you have done everything correctly so far, the player will eventually jitter or even crash.
The problem is that approach-helper ignores an important case. Suppose that p and goal are already close: that is, within s pixels. Then p’ should just equal goal, because the player does not overshoot its destination (and a zombie stops moving when it has found its meal).
Design a new function approach that is like approach-helper but handles this case and fixes the problem. The first input is the position to be updated. The second input is the unchanging position being approached. The third input is the speed (pixels per tick). The function should return the new position.
; approach : Posn Posn Number -> Posn ; ??? (define (approach p goal s) ???)
Your definition of approach should use approach-helper; but make sure the purpose statements you write for approach-helper and approach describe their difference clearly.
Finally, change move-player to use approach instead of approach-helper. Try run-pworld again to make sure that player movement is working fully.
2 Zombie movement and game ending
Now we will add zombies to the world. The zombies will be a [ListOf Posn]. The player will see them as a number of zombie icons, one for each Posn. Zombies sense that there is something delicious at the player’s position, and so will move there in a straight line.
Problem 9. Develop a data definition Zworld and a struct called zworld with two fields. One field stores a Pworld and the other field stores a [ListOf Posn].
Problem 10. Design a function called draw-zworld which takes a Zworld and returns an Image. This function should add icons for zombies on top of the image of the player world. Use foldr and draw-pworld.
Problem 11. Design a mouse-handler called update-pgoal-2 which takes a Zworld, two Numbers and a MouseEvent, and returns a Zworld. It updates the goal of the player’s movement to that of the input x and y coordinates. Use update-pgoal.
Problem 12. Define a number zombie-speed (we suggest a number between 2 and 10). Next, design a function called move-zombies which takes a [ListOf Posn] and a Posn and returns a [ListOf Posn]. move-zombies will move all of the zombies towards the input Posn. Use map and approach. Hint: use a locally defined function as an input to map.
Problem 13. Design a function called move-everyone which takes and returns a Zworld. move-everyone moves the player towards the goal position in the Pworld in the given Zworld. It also moves all of the zombies towards the player. Use move-zombies and move-player.
Problem 14. Design a function called generate-zombies which takes a NaturalNumber and returns a [ListOf Posn]. Each zombie created by generate-zombies is a random Posn. Use build-list, random and check-random.
Problem 15. Define a number close-enough to be the distance from the player position (in pixels) at or under which a zombie gets to have its meal. Then design a function called the-end which takes a Zworld and returns a Boolean. This function will be passed to big-bang as a stop-when handler, to signal when your big-bang program should end. Your game will run as long as the-end returns false. Use ormap, close-enough and the function dist.
Problem 16. Define a constant run to be big-bang applied to the functions designed above for your game. Define the [ListOf Posn] of the initial Zworld to be ten zombies randomly generated by generate-zombies.