data definitions DrRacket? - scheme

I am having problem with this. It's quite long.
First, heres the data definition for this
(define-struct ball (x y color))
;; Ball = (make-ball Number Number Color)
;; Color is one of 'red, 'yellow, 'blue, etc.
Heres my program
(require 2htdp/image)
(require 2htdp/universe)
;;An LoB is one of
;;--empty
;;--(cons ball LoB)
;;(define (ball-template lob)
;; (cond
;; [(empty? lob) ...]
;; [else
;; (first lob)...
;; (ball-template (rest lob))]))
;;lob-length : LoB -> number
;;Counts the balls in the list
(define (lob-length lob)
(cond
[(empty? lob) 0]
[else
(add1 (lob-length (rest lob)))]))
;;Examples
(check-expect (lob-length empty) 0)
(check-expect (lob-length(cons (make-ball 1 2 "red")
(cons(make-ball 3 3 "blue")
(cons(make-ball 5 86 "white")empty))))3)
;;lob-draw : LoB -> Scene
;;Adds the balls to an empty scene in the form of circles
(define (lob-draw lob)
(cond
[(empty? lob) (empty-scene 300 300)]
[else
(place-image (circle 3 "solid" (ball-color (first lob)))
(ball-x (first lob))
(ball-y (first lob))
(lob-draw (rest lob)))]))
;;Examples
(lob-draw empty)
(lob-draw (cons (make-ball 50 60 "red")
(cons(make-ball 20 15 "blue")
(cons(make-ball 5 200 "black")empty))))
;;lob-member? LoB, ball -> boolean
;;Checks to see if the ball is in the list
(define (lob-member? lob b)
(cond
[(empty? lob) false]
[(same-ball? b (first lob)) true]
[else (lob-member? (rest lob) b)]))
;;Examples
(check-expect (lob-member? empty (make-ball 300 70 "blue"))
false)
(check-expect (lob-member? (cons (make-ball 30 70 "blue")
(cons (make-ball 310 500 "black")
(cons (make-ball 30 340 "yellow") empty)))
(make-ball 310 500 "black")) true)
;;same-ball? ball ball -> boolean
;;Compares two balls
(define (same-ball? b1 b2)
(and (= (ball-x b1) (ball-x b2))
(= (ball-y b1) (ball-y b2))
(string=? (ball-color b1) (ball-color b2))))
;;Example
(check-expect (same-ball? (make-ball 30 30 "white")(make-ball 30 30 "white"))
true)
(check-expect (same-ball? (make-ball 30 30 "white")(make-ball 23 40 "black"))
false)
Just a simple program where consume lists of balls, add them to empty scenes, count how many balls are on a given list, etc...
I've done everything but one thing. I have to design a function lob-yellow, which changes the color of all the balls in a list of Balls to yellow. I am guessing I need cond, but I am not sure how to. Any ideas?

Assuming that the struct is immutable, here are some hints to get you started, fill-in the blanks:
(define (lob-yellow lob)
(cond [<???> ; if the list is empty
<???>] ; return the empty list
[else ; otherwise,
(cons (make-ball ; cons a new ball, build it with:
(<???> (first lob)) ; the x coordinate of the first ball
(ball-y <???>) ; the y coordinate of the first ball
<???>) ; and always the yellow color
(lob-yellow <???>))])) ; recur over the rest of the list
But if the struct were defined like this:
(define-struct ball (x y color) #:mutable) ; now the struct is mutable
... We could implement a solution that modifies each ball in the list in-place:
(define (lob-yellow lob)
(cond [<???> ; if the list is empty
<???>] ; return the empty list
[else ; otherwise,
(set-ball-color! <???> 'yellow) ; set the color of the first ball
(lob-yellow <???>)])) ; recur over the rest of the list

I have filled in a little of your template.
(define (yellow-to-blue lob)
(cond
[(empty? lob) ...]
[else
(cond
[(symbol=? (first lob) 'yellow)
(cons ... (yellow-to-blue (rest lob)))]
[else (cons ... (yellow-to-blue (rest lob)))])]))
Remember to write some test cases before you fill out the dots.

Related

How to have ball collide with bricks in Breakout (racket)

I've been trying to get Breakout to work in Racket, so far the ball bounces from the paddle (paddle is controlled by mouse) and the bricks are present
Here is the full on code:
(require 2htdp/image)
(require 2htdp/universe)
(define WIDTH 400)
(define HEIGHT 400)
(define BALL-RADIUS 10)
(define BALL-IMG (circle BALL-RADIUS "solid" "red"))
(define REC-WIDTH 50)
(define REC-HEIGHT 10)
(define REC-IMG (rectangle REC-WIDTH REC-HEIGHT "solid" "grey"))
(define BRICK-IMG0 (rectangle 60 30 "solid" "blue"))
(define BRICK-IMG1 (rectangle 60 30 "solid" "green"))
(define SCENE (empty-scene WIDTH HEIGHT))
(define STARTGAME
(text "CLICK TO START" 60 "purple"))
(define GAMEOVER
(text "GAMEOVER" 60 "red"))
(define-struct vel (delta-x delta-y))
; a Vel is a structure: (make-vel Number Number)
; interp. the velocity vector of a moving object
(define-struct ball (loc velocity))
; a Ball is a structure: (make-ball Posn Vel)
; interp. the position and velocity of a object
(define-struct rec (pos))
;
;
(define-struct brick (pos))
;
;
; Posn Vel -> Posn
; applies q to p and simulates the movement in one clock tick
(check-expect (posn+vel (make-posn 5 6) (make-vel 1 2))
(make-posn 6 8))
(define (posn+vel p q)
(make-posn (+ (posn-x p) (vel-delta-x q))
(+ (posn-y p) (vel-delta-y q))))
; Ball -> Ball
; computes movement of ball in one clock tick
(check-expect (move-ball (make-ball (make-posn 20 30)
(make-vel 5 10)))
(make-ball (make-posn 25 40)
(make-vel 5 10)))
(define (move-ball ball)
(make-ball (posn+vel (ball-loc ball)
(ball-velocity ball))
(ball-velocity ball)))
; A Collision is either
; - "top"
; - "down"
; - "left"
; - "right"
; - "none"
; interp. the location where a ball collides with a wall
; Posn -> Collision
; detects with which of the bricks the ball collides
;(check-expect (collision (make-posn 0 12)) "left")
;(check-expect (collision (make-posn 15 HEIGHT)) "down")
;(check-expect (collision (make-posn WIDTH 12)) "right")
;(check-expect (collision (make-posn 15 0)) "top")
;(check-expect (collision (make-posn 55 55)) "none")
(define (collision ball rec)
(cond
[(<= (posn-x ball) BALL-RADIUS) "left"]
[(<= (posn-y ball) BALL-RADIUS) "top"]
[(>= (posn-x ball) (- WIDTH BALL-RADIUS)) "right"]
[(>= (posn-y ball) (- HEIGHT BALL-RADIUS)) "GAMEOVER"]
[(and (>= (posn-x ball) (- (posn-x (rec-pos rec)) (/ REC-WIDTH 2)))
(<= (posn-x ball) (+ (posn-x (rec-pos rec)) (/ REC-WIDTH 2)))
(= (posn-y ball) (posn-y (rec-pos rec)))) "down"]
[else "none"]))
; Vel Collision -> Vel
; computes the velocity of an object after a collision
(check-expect (bounce (make-vel 3 4) "left")
(make-vel -3 4))
(check-expect (bounce (make-vel 3 4) "top")
(make-vel 3 -4))
(check-expect (bounce (make-vel 3 4) "none")
(make-vel 3 4))
(define (bounce vel collision)
(cond [(or (string=? collision "left")
(string=? collision "right"))
(make-vel (- (vel-delta-x vel))
(vel-delta-y vel))]
[(or (string=? collision "down")
(string=? collision "top"))
(make-vel (vel-delta-x vel)
(- (vel-delta-y vel)))]
[else vel]))
; WorldState is a Ball
; interp. the current state of the ball
; WorldState -> Image
; renders ball at its position
;(check-expect (image? (render INITIAL-BALL)) #true)
(define (render a-world)
(draw-ball (world-ball a-world)
(draw-bricklist (world-bricks a-world)
(draw-rect (world-rec a-world)))))
(define (draw-ball ball img)
(place-image BALL-IMG
(posn-x (ball-loc ball))
(posn-y (ball-loc ball))
img))
(define (draw-rect rec)
(place-image REC-IMG
(posn-x (rec-pos rec))
(posn-y (rec-pos rec))
SCENE))
(define (draw-bricklist list img)
[cond ((empty? list) img)
((cons? list) (draw-bricklist (rest list) (draw-brick (first list) img)))])
(define (draw-brick brick image)
(place-image (choice BRICK-IMG0 BRICK-IMG1)
(posn-x (brick-pos brick))
(posn-y (brick-pos brick))
image))
(define (choice a b )
(if (zero? (random 1)) a b ))
; WorldState -> WorldState
; moves ball to its next location
;(check-expect (tick (make-ball (make-posn 20 12) (make-vel 1 2)))
; (make-ball (make-posn 21 14) (make-vel 1 2)))
;(define (tick ball)
; (move-ball (make-ball (ball-loc ball)
; (bounce (ball-velocity ball)
; (collision (ball-loc ball))))))
(define (tick world)
(make-world (move-ball (make-ball (ball-loc (world-ball world))
(bounce (ball-velocity (world-ball world))
(collision (ball-loc (world-ball world))
(world-rec world)))))
(world-rec world)
(world-bricks world)))
(define (mouse world mouse-x mouse-y mouse-event)
(cond
[(string=? mouse-event "move") (make-world (world-ball world)
(make-rec (make-posn mouse-x REC-Y-POS))
(world-bricks world))]
[else world]))
(define INITIAL-BALL (make-ball (make-posn 20 12)
(make-vel 1 2)))
(define REC-Y-POS (- HEIGHT (+ 20 REC-HEIGHT)))
(define INITIAL-REC (make-rec (make-posn 100 REC-Y-POS)))
(define INITIAL-BRICKS (list
(make-brick (make-posn 50 100))
(make-brick (make-posn 111 100))
(make-brick (make-posn 172 100))
(make-brick (make-posn 233 100))
(make-brick (make-posn 294 100))
(make-brick (make-posn 355 100))
(make-brick (make-posn 50 131))
(make-brick (make-posn 111 131))
(make-brick (make-posn 172 131))
(make-brick (make-posn 233 131))
(make-brick (make-posn 294 131))
(make-brick (make-posn 355 131))
(make-brick (make-posn 50 162))
(make-brick (make-posn 111 162))
(make-brick (make-posn 172 162))
(make-brick (make-posn 233 162))
(make-brick (make-posn 294 162))
(make-brick (make-posn 355 162))))
(define-struct world (ball rec bricks))
(define INITIAL-WORLD-STATE (make-world INITIAL-BALL INITIAL-REC INITIAL-BRICKS))
(define (main state)
(big-bang state (on-mouse mouse) (on-tick tick 0.01) (to-draw render)))
(main INITIAL-WORLD-STATE)
The Ball just flies straight through the bricks. I've added this following code to it:
;--------------------------------------------------------------------------------------------------
(define (overlap?
brick-one-x brick-one-y brick-one-width brick-one-height
brick-two-x brick-two-y brick-two-width brick-two-height)
(cond
[(and
(and
(>= (+ brick-one-x brick-one-width) brick-two-x); within right bounds
(<= brick-one-x (+ brick-two-x brick-two-width)); within left bounds
)
(and
(>= (+ brick-one-y brick-one-height) brick-two-y) ;within top bounds
(<= brick-one-y (+ brick-two-y brick-two-height)) ; within bottom bounds
))
#t]
[else ;not overlapping
#f]))
(define (brick-collide-ball? brick ball)
(if
(overlap?
;balls dimensions and location
(posn-x (ball-loc ball))
(posn-y (ball-loc ball))
10
10
;first brick in list
(posn-x (brick-pos brick))
(- (posn-y (brick-pos brick)) 10)
60
30)
#t
#f))
(define (delete-bricks bricklist ball)
(cond
[(empty? bricklist) empty]
((cons? bricklist)
(if (brick-collide-ball? (first bricklist) ball)
(delete-bricks (rest bricklist) ball)
(cons (first bricklist)(delete-bricks (rest bricklist) ball))))))
;--------------------------------------------------------------------------------------------------------
but it does not seem to make an impact on anything. I'm stuck now and would appreciate any tips to improve!
I used the following in my version of the Atari classic:
(define (colliding? b1 b2)
(match-define (body x1 y1 w1 h1) b1)
(match-define (body x2 y2 w2 h2) b2)
(not (or (eq? b1 b2)
(< (+ x1 w1) x2) (> x1 (+ x2 w2))
(< (+ y1 h1) y2) (> y1 (+ y2 h2)))))
Here both the paddle and the ball is a "body", which is an rectangle
with and (x,y) position and a width, w, and a height h.
(struct body (x y w h) #:transparent)
Note: If you ball happens to move very fast, it can happen that it "jumps over" the paddle. This can be fixed by having a maximum velocity.
One way you might be able to address this problem without falling into a velocity-jump in the other answer is to calculate the intersection of the line along which the ball is currently traveling and the bounding line of (presumably the front of) the paddle. Then you just determine if this point is actually on the line segment of the paddle and the line segment between the ball's previous position and it's hypothetical position to determine if a collision would have happened "between" frames and handle that reflection. A more thorough check would be to do the same thing with the edge of the paddle since that game did allow the paddle to hit the ball from the side.
Example calculation:
#lang racket/base
(require racket/struct)
(struct line (a b c) #:transparent) ; ax+by=c
(struct pos (x y) #:transparent)
(define (meet l1 l2)
(let-values (((a b c) (apply values (struct->list l1)))
((d e f) (apply values (struct->list l2))))
(let ((denominator (- (* a e) (* b d))))
(if (zero? denominator)
'parallel ;probably impossible in your game
(let ((x* (- (* c e) (* b f)))
(y* (- (* a f) (* c d))))
(pos (/ x* denominator)
(/ y* denominator)))))))

.Dynamically changing the clock tick rate in big-bang

The on-tick clause includes an option to change the clock tick rate. I have included my code to dynamically change the value depending on the world state value. The code is not working and I can't understand why. Another issue - how are world programs debugged? The "step" option doesn't work.
; physical constants
(define HEIGHT 300)
(define WIDTH 100)
(define YDELTA 3)
; graphical constants
(define BACKG (empty-scene WIDTH HEIGHT))
(define ROCKET (rectangle 5 30 "solid" "red"))
(define ROCKET-CENTER (/ (image-height ROCKET) 2))
(define ROCKET-XPOS 10)
; LRCD -> LRCD
(define (main1 s)
(big-bang s
[to-draw show]
[on-key launch]
[on-tick fly (clock-rate s)]))
; LRCD -> Image
; renders the state as a resting or flying rocket
(define (show x)
(cond
[(string? x) (rocket-ht HEIGHT)]
[(<= -3 x -1)
(place-image (text (number->string x) 20 "red")
ROCKET-XPOS (* 3/4 WIDTH)
(rocket-ht HEIGHT))]
[(>= x 0)
(rocket-ht x)]))
; LRCD -> image
; positions the rocket at correct height
(define (rocket-ht ht)
(place-image ROCKET ROCKET-XPOS (- ht ROCKET-CENTER) BACKG))
; LRCD KeyEvent -> LRCD
; starts the count-down when space bar is pressed,
; if the rocket is still resting
(define (launch x ke)
(cond
[(string? x) (if (string=? ke " ") -3 x)]
[else x]))
; LRCD -> LRCD
; raises the rocket by YDELTA,
; if it is moving already
(define (fly x)
(cond
[(string? x) x]
[(<= -3 x -1) (if (= x -1) HEIGHT (add1 x))]
[else (- x YDELTA)]))
(define (clock-rate s)
(cond
[(number? s) (if (< s 0) 1 1/28)]
[else 1/28]))

Rewrite an item in a list of list

This seems straightforward, but I can't seem to find a solution. I want to replace an item within a list of a list with something, but if that item appears multiple times then you randomly replace one of them, but not both. I want to do this in ISL+.
I created the function flatten which appends all sublists :
(check-expect (flatten '((a b) (c) (d e f g) (h i j)))
(list 'a 'b 'c 'd 'e 'f 'g 'h 'i 'j))
(define (flatten lol)
(foldr append empty lol))
I also made rewrite, which replaces the value at index n with whatever you choose
(check-expect (rewrite '(x x x - x x x x) 3 'x)
(list 'x 'x 'x 'x 'x 'x 'x 'x))
(define (rewrite ls n val)
(cond
[(empty? ls) (error "error")]
[(= n 0) (cons val (rest ls))]
[else (cons (first ls) (rewrite (rest ls) (sub1 n) val))]))
The problem is I don't know how to apply this to a list of list and I also don't know how to randomly replace one of items if it occurs more than once. This is what I have for the final product, but it's probably not the way to go:
(define (fullreplace b)
(local [
;makes a list of nested lists of each index the element occurs
;problem is that it makes a list of nested lists so I can't use flatten either
(define (position ls ele n)
(cond [(empty? ls) 0]
[(equal? ele (first ls)) (list n (position (rest ls) ele (add1 n))) ]
[else (position (rest ls) ele (+ 1 n))]))]
;lol-full? checks if the item occurs in the list of lists at all
(if (lol-full? b) b (rewrite (flatten b)
(position (flatten b) '- 0)
"item replaced"))))
;just used for testing
(define lol2 (list
(list 2 2 2 2)
(list 4 '- 4 '-)
(list '- 8 8 8)
(list 16 '- '- 16)))
(fullreplace lol2) may return this or where any of the other '- are located:
(list
(list 2 2 2 2)
(list 4 '- 4 2)
(list '- 8 8 8)
(list 16 '- '- 16))
I've been working on this awhile so any new insight would go a long way. Thank you
The "random" part is what makes this problem pathological. If you could just replace the first occurrence, it would be easy. But to replace a random occurence, you must first know how many occurrences there are. So before you go replacing stuff, you have to go a-counting:
(define (count/recursive val tree)
(cond ((equal? val tree)
1)
(else (foldl (λ (next-value total)
(cond ((equal? val next-value)
(add1 total))
((list? next-value)
(+ total (count/recursive val next-value)))
(else total))) 0 tree))))
Then you need a function that can replace the nth occurrence of a value:
(define (replace/recursive val replace-with n tree)
(cond ((equal? val tree)
replace-with)
(else
(cdr
(foldl (λ (next-value total/output-tree)
(local ((define total (car total/output-tree))
(define output-tree (cdr total/output-tree)))
(cond ((equal? next-value val)
(cons (add1 total)
(cons (if (= total n) replace-with next-value) output-tree)))
((list? next-value)
(cons (+ total (count/recursive val next-value))
(cons (replace/recursive val replace-with (- n total) next-value)
output-tree)))
(else (cons total (cons next-value output-tree)))))) (cons 0 empty) tree)))))
Finally, you use random to pick the instance you will replace, using count/recursive to limit how high of a number random picks:
(define original '((x x (x y x) a b (((c x z x) x) y x x))))
(replace/recursive 'x '- (random (count/recursive 'x original)) original)
How to replace all occurences of a value with another value:
(define (replace-all needle new-value haystack)
(cond ((equal? needle haystack) new-value)
((pair? haystack)
(cons (replace-all needle new-value (car haystack))
(replace-all needle new-value (cdr haystack))))
(else haystack)))
The only thing to change is to check if the first part constituted a change. If it did you don't do the replace on the other half. Use equal? to compare structure.
It's not random. It will replace the first occurence it finds either by doing car before cdr or cdr before car.

How can I improve this auxilary function in Racket?

I'm working in HtDP, Chapter 4 using the BSL language.
The problem I was working on is:
Exercise 136: If you run main, press the space bar (fire a shot), and
wait for a good amount of time, the shot disappears from the canvas.
When you shut down the world canvas, however, the result is a world
that still contains this invisible shot.
Design an alternative tock function, which not just moves shots one
pixel per clock tick but also eliminates those whose coordinates
places them above the canvas. Hint: You may wish to consider the
design of an auxiliary function for the recursive cond clause.
The solution that I came up with is below (in a spoiler). However, I feel that I'm doing something redundant. Basically my application of the auxiliary function isn't quite correct.
(define (main w0)
(big-bang w0
(on-tick ticking)
(on-key fire-key)
(to-draw to-render)))
(define HEIGHT 100)
(define WIDTH 80)
(define TURRET-X-POS (/ WIDTH 2))
(define BKGRND (empty-scene WIDTH HEIGHT))
(define SHOT-IMG (triangle 4 "solid" "red"))
(define (to-render w0)
(cond
[(empty? w0) BKGRND]
[else (place-image SHOT-IMG TURRET-X-POS (first w0) (to-render (rest w0)))]))
(define (fire-key w0 ke)
(cond
[(key=? ke " ") (cons HEIGHT w0)]
[else w0]))
(define (ticking w0)
(cond
[(empty? w0) empty]
[(empty? (only-inbound-shots w0)) empty]
[else (cons (sub1 (first (only-inbound-shots w0)))
(ticking (rest (only-inbound-shots w0))))]))
(define (only-inbound-shots w0)
(cond
[(< (first w0) -4) (rest w0)]
[else w0]))
UPDATE:
(This is much cleaner than before)
(define HEIGHT 100) ;height of scene
(define WIDTH 80) ;width of scene
(define TURRET-X-POS (/ WIDTH 2)) ;position of turret, ie. shot's x-coordinate
(define BKGRND (empty-scene WIDTH HEIGHT)) ; scene itself
(define SHOT-IMG (triangle 4 "solid" "red")) ;image representing the shot
(define Y-BOUNDARY -4) ;y-coordinate where shot is no longer visible in scene
;List-of-numbers -> List-of-numbers
;renders all shots fired
(define (to-render w0)
(cond
[(empty? w0) BKGRND]
[else (place-image SHOT-IMG TURRET-X-POS (first w0)
(to-render (rest w0)))]))
;List-of-numbers, key event -> List-of-numbers
;only allows the space bar to fire a shot
;one space bar event produces one shot
(define (fire-key w0 ke)
(cond
[(key=? ke " ") (cons HEIGHT w0)]
[else w0]))
;List-of-numbers -> List-of-numbers
;during each clock tick, the y-coordinate each of the shot
; in List-of-numbers is updated
;each y-coordinate decreases by -1
(define (ticking w0)
(cond
[(empty? w0) w0]
[else (only-inbound-shots (update-shots w0) Y-BOUNDARY)]))
;List-of-numbers -> List-of-numbers
;does the actual updating of the shots in List-of-numbers
;each shot's value is decreased by -1
(define (update-shots w0)
(cond
[(empty? w0) w0]
[else (cons (sub1 (first w0)) (update-shots (rest w0)))]))
;List-of-numbers -> List-of-numbers
;checks to see if the first shot in the List-of-numbers has gone past the Y-BOUNDARY
;if so then remove shot from the List-of-numbers and return the rest of the List
;otherwise return the List without change
(define (only-inbound-shots w0 y-boundary)
(cond
[(empty? w0) w0]
[(< (first w0) y-boundary) (rest w0)]
[else w0]))
;List-of-numbers -> List-of-numbers
;creates the world of shots
;seed value is empty, additional values created by space bar
(define (main w0)
(big-bang w0
(on-tick ticking)
(on-key fire-key)
(to-draw to-render)))
TESTS added:
I'm still working on the tests.
(define test-shots
(cons -6 (cons -5 (cons 10 empty))))
(define test-shots-2
(cons -6 (cons 2 (cons 7 empty))))
(define test-shots-3
(cons 4 (cons 9 (cons 10 empty))))
(check-expect (to-render test-shots)
(place-image SHOT-IMG TURRET-X-POS -6
(place-image SHOT-IMG TURRET-X-POS -5
(place-image SHOT-IMG TURRET-X-POS 10
BKGRND))))
(check-expect (to-render test-shots-2)
(place-image SHOT-IMG TURRET-X-POS -6
(place-image SHOT-IMG TURRET-X-POS 2
(place-image SHOT-IMG TURRET-X-POS 7
BKGRND))))
TEST with world functions added:
(define HEIGHT 1) ; makes test a little faster
(check-expect
(fire-key
(ticking
(ticking
(ticking
(ticking
(fire-key
(ticking
(ticking
(ticking
(ticking (fire-key empty " ")))))
" ")))))
" ")
(cons -3 (cons 1 empty))
The usual comments about missing contracts, purpose statements, and data definitions apply here. As well as tests of the individual functions; a big reason why world.ss/universe.ss are really nice libraries is that they enable one to test functions that are conceptually performing Input/Output.
I'm inferring a lot about what your data definition is from the code, but (1.) you should not put that onus on the reader, and (2.) it could lead to mistakes in my reasoning.
It looks to me like you have deviated significantly from the template in your definition of ticking; it does not look like any template I can think of. A similar comment applies to only-inbound-shots
You may want to break ticking up into multiple subroutines, and then compose them.
An example of what I mean by this: If you were to make a function to take the average of a list of numbers, a simple way to do it is to make two new functions: the first produces the sum of the numbers, and the second produces the length of the list; these are trivial to write via the Design Recipe. Then average is:
;; average : [Listof Number] -> Number
;; produces average value of input (x_1 x_2 ... x_n
(define (average l)
(/ (sum-of-list l) (length-of-list l)))
But if you were to try to do it in a single definition of average that followed the template for [Listof Number], you would have some problems getting the right answer. (I do not think it can be done properly without using an accumulator or two.)
That factoring into very simple subroutines and then composing them at the end to get the desired effect is what I mean by breaking ticking up and then composing the pieces. (If you're not destructuring your input, function composition is a perfectly valid design process: see HtDP section 3.1.)
More importantly, though, I think is to make some tests for the individual functions. Especially only-inbound-shots: I suggest you think about this function on its own.
Pretend that you don't know who might call it, and only that they will obey its contract (e.g. they will only pass in a World, whatever you defined that to be here).
And then make sure you produce the right answer for any possible legal input they provide.
Don't think about how you use it yourself in your other code above, because you don't want to try to keep all that in your head at the same time. Its actually simpler to generalize here, and think about what only-inbound-shots should do on any possible input.
To provide you with some concrete food for thought on the matter of testing, here are some hypothetical pictures describing the inputs you might try to handle in your tests:
, ,
Update 28 Feb 2013:
While I still recommend writing individual unit tests of each of your functions, end-to-end testing is also important. In this case, the game as currently rendered won't tell you if have shots lying outside the scene or not (because place-image, unlike say overlay, automatically crops them from the rendering).
So, if you want to debug the game while it is running, it can be useful to get that kind of information. Say like a drop down bit of text that renders on top of the game (one often sees this in video games to show you things like Frame Rate). So here is one strategy for getting that information out while the game is running: Swap in an alternative rendering function, that is layered on top of your existing one, but prints out other information about the world w0 argument.
(In this case, it might be useful to see its length, though one can imagine extracting other information.)
;; List-of-numbers -> Image
;; Renders w0 via to-render, with a printout of shot count in top left corner.
(define (to-render-with-count w0)
(place-image/align (text (number->string (length w0)) 30 'blue)
0 0 "left" "top"
(to-render w0)))
Then you hook in to-render-with-count in your big-bang invocation. It may also be useful to slow down the clock tick rate, so that you can see what happens as keystrokes and clock ticks are intermixed, so I have made that change too (in the on-tick clause):
(define (main w0)
(big-bang w0
(on-tick ticking 0.1)
(on-key fire-key)
(to-draw to-render-with-count)))
Now, I can interactively notice interesting trends. Trends that yield situations like this:
How is it that I have 148 balls on the screen but only four are showing? What kind of world would have that happen? (If you close the window created by big-bang, it will return the current world to the Interactions Window, so you will see right there exactly what kind of World would have that happen.)
I put the final answer here because the original question has a lot going on already.
(define HEIGHT 200) ;height of scene
(define WIDTH 80) ;width of scene
(define TURRET-X-POS (/ WIDTH 2)) ;position of turret, ie. where shot's x-coordinate
(define BKGRND (empty-scene WIDTH HEIGHT)) ; scene itself
(define SHOT-IMG (triangle 4 "solid" "red")) ;image representing the shot
(define Y-BOUNDARY -4) ;y-coordinate where shot is no longer visible in scene
;List-of-numbers -> List-of-numbers
;renders all shots fired
(define (to-render w0)
(cond
[(empty? w0) BKGRND]
[else (place-image SHOT-IMG TURRET-X-POS (first w0) (to-render (rest w0)))]))
;List-of-numbers, key event -> List-of-numbers
;only allows the space bar to fire a shot
;one space bar event produces one shot
(define (fire-key w0 ke)
(cond
[(key=? ke " ") (cons HEIGHT w0)]
[else w0]))
;List-of-numbers -> List-of-numbers
;updates world state every clock tick
(define (ticking w0)
(cond
[(empty? w0) w0]
[else (remove-outbound-shots (update-shots w0) Y-BOUNDARY)]))
;List-of-numbers -> List-of-numbers
;updates all shots
(define (update-shots w0)
(cond
[(empty? w0) w0]
[else (cons (sub1 (first w0)) (update-shots (rest w0)))]))
;List-of-numbers -> List-of-numbers
;removes all shots exceeding the y-boundary from list
(define (remove-outbound-shots w0 y-boundary)
(cond
[(empty? w0) w0]
[(< (first w0) y-boundary) (remove-outbound-shots (rest w0) y-boundary)]
[else (cons (first w0) (remove-outbound-shots (rest w0) y-boundary))]))
;List-of-numbers -> List-of-numbers
;creates the world of shots
;seed value is empty, additional values created by space bar
(define (main w0)
(big-bang w0
(on-tick ticking)
(on-key fire-key)
(to-draw to-render)))
Tests:
(define test-shots-1
(cons 1 (cons 4 (cons 10 (cons -6 (cons -5 (cons 1 (cons 4 (cons 10 (cons 10 (cons -6 (cons -9 empty))))))))))))
(define test-shots-4
(cons 10 (cons -6 (cons -5 (cons 1 (cons 4 (cons 10 empty)))))))
(check-expect (remove-outbound-shots test-shots-4 -4) (list 10 1 4 10))
(check-expect (remove-outbound-shots test-shots-1 -4) (list 1 4 10 1 4 10 10))

on-key in racket

(require 2htdp/image)
(require 2htdp/universe)
(define (render t)
(text (number->string t) 10 "red"))
(define (ball-image t)
(place-image (circle 10 "solid" "red")
150
150
(empty-scene 300 300)))
(define (change w a-key)
(cond
[(key=? a-key "left") (ball-image w)]
[(key=? a-key "right") (ball-image w )]
[(= (string-length a-key) 1) w]
[(key=? a-key "up") (ball-image w )]
[(key=? a-key "down") (ball-image w )]
[else w]))
(big-bang 100
(on-tick sub1 )
(to-draw ball-image)
(on-key change))
I am trying to get the red ball I have placed in the middle to move up, down, left, or right. When I press any of the arrow keys, it says it expects a number but given an image. What am I doing wrong?
First of all you need to understand how the world is processed in this main circle:
The system takes the first argument of big-bang - 100, and remembers it as a WorldState.
Then it passes it to a on-tick (sub1) function, provided it exists on each tick.
When the key is pressed, it calls on-key (change) and passes the woldState there, as a w argument.
There you draw some pictures and return it in case of an arrow key is pressed. So when an arrow is pressed, it returns the result of ball-image = result of place-image - image
The system remembers it as a current worldState,
and with the next tick, it passes the new value to the old procedure: sub1.
Since the value is now an image, sub1 rejects it.
--
If you want to move a ball in two directions, you have to store at least two coordinates (x . y). So let now the WorldState be the pair of two numbers. We don't need a on-tick function, since nothing changes on its own. Also we don't need to draw the ball in the keyboard processor, so let's simple change the corresponding value in the pair (worldState), and draw it only during the call (ball-image) which puts the ball into the new place (remember, x = (car t), y = (cdr t), and (x . y) = (cons x y)):
(require 2htdp/image)
(require 2htdp/universe)
(define (ball-image t) ;<-- the t-parameter is our WorldState
(place-image (circle 10 "solid" "red")
(car t) ;<-- here now x variable coordinate
(cdr t) ;<-- here now y variable, instead of 150
(empty-scene 300 300)))
(define (change w a-key)
(cond ;w - is the previous worldState, V here we change it
[(key=? a-key "left") (cons (sub1 (car w)) (cdr w))];and
[(key=? a-key "right") (cons (add1 (car w)) (cdr w))];return
[(= (string-length a-key) 1) w] ;<-- this line is excess
[(key=? a-key "up") (cons (car w) (sub1 (cdr w)))]
[(key=? a-key "down") (cons (car w) (add1 (cdr w)))]
[else w])) ;<-- If the key of no interest, just
return the previous WorldState
(big-bang '(150 . 150) ;<-- initial state
(to-draw ball-image) ;<-- redraws the world
(on-key change)) ;<-- process the event of key press

Resources