home—lectures—recipe—hws—exams—D2L—zoom (snow day)
list processing
Asteroids cont.
Part A: Problems 1,2,3,4a due
Feb.27 (Thu) 09:30
start-of-class on D2L-only (no hardcopy).
Part B: The remainder due
Mar.04 (Tue) 23:59start-of-class
(D2L, and hardcopy).
Reading: §6.6, and §10.1–10.3.1.
(Additional recommended, but not required, background reading: all of Chpt.6, and §10.3.
Additional, non-required, challenge-reading: § 10.5.)
All problems are to be written in Racket.
Do not call any of the following functions:
- list (except when making test-cases)
- append (unless you write it yourself; also see the one exception below)
- remove/remove* (unless you write it yourself)
- place-images (plural) (unless you write it yourself); calling place-image (singular) is fine/intended.
- any functions with a “!”, such as set! and set-rest!.
If you want to use let* (as mentioned in class), you certainly may.
Your name and the assignment-number
must be in a comment at the start of the file
All functions/data must include the appropriate steps of the-design-recipe.html.
In particular, test cases alone might be worth 40-50% of the credit for a function.
Have enough test cases to cover different corner cases; often 2–3 can suffice.
- Write the function count-bigs : (-> real? (listof real?) natnum?,
which takes in a threshold and a list of numbers, and returns
how many of them
are larger than the threshold.
To help you out, here are some test cases; no further ones are required.
(The data-definition for list-of-number has already been given in lecture,
so you don't need to repeat steps 1-3 of the design recipe for list-of-number.)
Remember: We can't re-assign to variables, in functional programming.
So we won't have a counter which we increment, like you might in your imperative-programming class.
Instead, be sure to follow the design recipe, and after copying the template,
think about the inventory-with-values
(assuming we call our parameters “thresh” and “nums”):
if we call count-bigs with the particular inputs
(count-bigs 3 (cons 10 (cons 2 (cons 5 empty)))), what is…
- thresh =
- nums =
- (first nums) =
- (rest nums) =
- (count-bigs threshold (rest nums)) =
Fill in each blank with a particular number, or list-of-numbers.
Then, ask yourself: Starting with those pieces of info,
how do I assemble them to get my desired result of 2?
You don't need to include the above in your answer —
it's just to remind you of what you do,
for the “inventory-with-values” step of the design-recipe, #6.
If you get stuck on any of the problems below, make sure you didn't skip this step;
it's the one that can really make things click!
- Write the racket function map-sqr : (-> (listof number?) (listof number?)),
which squares each number in a list;
do not call map .
To help you out, here are some test cases; no further ones are required.
(check-expect (map-sqr empty) empty)
(check-expect (map-sqr (cons 7 empty)) (cons 49 empty))
(check-expect (map-sqr (cons 9 (cons 7 empty))) (cons 81 (cons 49 empty))) |
Copy your hw04(structs) racket file to one that we'll use for this one.
Add a very noticeable dividing-line (say, 80 ;s)
between old code and the new.
You will not need to turn in hardcopy of things before the dividing-line
if they were in your hw04 solution or in the posted hw04-soln.
Please include part A (problems 1,2) in your part B hardcopy.
- For each T in {asteroid,bullet}: so T is a type-variable: replace it
with a particular type in the sentences below
Give at least 4 examples of the type (listof T).
Name them, to more easily use them as test-inputs
for functions like move-Ts and draw-Ts below.
- Collision pt.1:
Write ship-collide-asteroids? : (-> ship? (listof asteroid) boolean?),
which determines whether a single ship collides with any asteroid
in a list-of-asteroids.
(Of course, you should call hw04's ship-collide-asteroid? as a helper.)
This is a bit like count-bigs — or even more like
lecture notes'n contains?.
- Move functions:
- Write move-asteroids, which return a list of all
the move'd asteroids of the input.
(This is very similar to map-sqr above.)
- Write move-bullets similarly.
If you have bullets that time out, this function could also be a good
place to filter them, keeping just the still alive bullets.
You decide what can make bullets disappear, if they do at all —
my hw04 struct had an time-remaining field, but you could
also remove them if they reach the edge of the screen, or some other criterion.
Alternately, you might have bullets live forever, with collisions being the
only way of getting rid of them.
Note that we'll do higher-level processing in functions below — e.g. worrying
about collisions.
- (0pts) Write split-astr, which returns all the smaller chunks of an asteroid
after being hit by a bullet.
Don't worry about too small results; we'll handle that later.
Here's a version that uses my astr-chunk from my
hw04-soln; you are free to let it inspire you, or to copy it with citation:
; return the fragments of `*` after being split apart.
; Note that we're using the rather-odd parameter name `*`,
; which also shadows the built-in multiplication function!
;
(define/contract (split-astr *)
(-> astr? (listof astr?))
(cons (astr-chunk * 0 3)
(cons (astr-chunk * 1 3)
(cons (astr-chunk * 2 3)
'()))))
(check-expect (split-astr astr1)
(cons (astr-chunk astr1 0 3)
(cons (astr-chunk astr1 1 3)
(cons (astr-chunk astr1 2 3)
'())))) |
Alternatively,
you can in-line this task into asteroids-remaining below,
which would obviate the need for append in that function.
- Now write filter-big-astrs : (-> (listof astr) (listof astr)),
which keeps all the asteroids that aren't too small.
Again, a later higher-level-logic function will
call filter-big-astrs as well as split-astr.
Or, if your version of chunk-asteroid never created small asteroids,
then you don't need this function.
- Collision pt.2:
We'll need functions which, given all the asteroids and bullets,
only keep the ones that aren't colliding with each other,
and to split-astr those asteroids which are colliding.
We'll do this in a few steps:
- Let's start with a simpler helper function
bullet-collide-asteroids? : (-> bullet? (listof asteroid) boolean)
which returns whether one single bullet is colliding with any of the asteroids.
- Now write bullets-remaining : (-> (listof bullet) (listof asteroid) (listof bullet), which
returns all the bullets remaining after colliding with any of the provided asteroids.
This function should not include any bullets that have timed out (however you handle that;
my solution used a field time-remaining).
Note that this task may feel like a nested-loop, but we're
actually handling it by putting the inner-loop in the first function.
Now we can write one of our top-level functions, asteroids-remaining : (-> (listof asteroids) (listof bullets) (listof asteroids)).
It returns all the asteroids that exist after
any bullet-collisions that trigger split-astr, and also filtering just the big-enough asteroids.
This function is still straightforward, following the list-template as usual.
As you hopefully guess, this function should call
asteroid-collide-bullets?,
split-astr,
and
filter-big-astrs.
This function is the one time you can call the built-in append:
you can append the result of (filtered) split-asteroids to the result of the recursive call.
(We can't use cons in this situation, since we have an entire list to prepend to the
front of the recursive call.)
- Worlds:
Define a “world” structure which contains
one ship, a list of asteroids,
and a list of bullets.
As usual for our data-definitions,
make examples of the data (at least two).
- Give a template for world-processing functions.
- Write the function draw-world : (-> world? image?),
which draws the world onto an otherwise-empty background.
Note: This is a bit different than the other drawing functions -- it's the one that provides the initial background.
- Write the top-level function move-world : (-> world? world?)
which returns a new world one tick later.
This entails moving the ship, all asteroids, and all bullets,
and handling all collisions between asteroids and bullets.
Note: This function is straightforward
after having written asteroids-remaining
and bullets-remaining.
Just be sure clear what list you pass to those helpers — the list
of already-moved asteroids/bullets.
- Write the function world-handle-key : (-> world? key-event? world?)
which returns a new world updated to handle the keypress.
Will be pretty short —
handle the command to fire a bullet,
and otherwise defers to ship-handle-key.
Your test cases will largely crib from that function.
- Finally, write a function game-over? : (-> world? boolean?),
which returns whether the game is over
(that is, if the ship is colliding with a asteroid, or (optionally) there are no more asteroids).
All the above should have their tests, as well
as signatures and (brief) purpose statements.
for fun: playability
Only after all tests pass,
add
(require 2htdp/universe)
(big-bang some-initial-world
[on-key world-handle-key]
[on-tick update-world]
[to-draw draw-world]
[stop-when game-over?]) |
and you can play your game.
In addition, you can add sounds, using
(play-sound-then <filename> <val>):
This starts playing <filename>, and then returns <val>.
(See
full-racket's documentation
for supported filetypes.)
I used this to wrap my key-handlers' return value,
so that keypresses would cause a sound to play.
Of course, be sure you have the rights to use any sounds you use.
You might record your own.
I addded a few small sound-files with the hw04-soln folder.
home—lectures—recipe—hws—exams—D2L—zoom (snow day)
 This page licensed CC-BY 4.0 Ian Barland Page last generated | Please mail any suggestions (incl. typos, broken links) to ibarland radford.edu |
 |