-
Notifications
You must be signed in to change notification settings - Fork 4
/
03-1-falling-cat.rkt
executable file
·192 lines (146 loc) · 5.77 KB
/
03-1-falling-cat.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
;; The first three lines of this file were inserted by DrRacket. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "htdp-beginner-reader.ss" "lang")((modname 03-1-falling-cat) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f () #f)))
;; falling cat.
;; A cat falls from the top of the scene.
;; The user can pause/unpause the cat with the space bar.
;; start with (main 0)
(require rackunit)
(require "extras.rkt")
(require 2htdp/universe)
(require 2htdp/image)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; MAIN FUNCTION.
;; main : Integer -> World
;; GIVEN: the initial y-position of the cat
;; EFFECT: runs the simulation, starting with the cat falling
;; RETURNS: the final state of the world
(define (main initial-pos)
(big-bang (make-world initial-pos false)
(on-tick world-after-tick 0.5)
(on-key world-after-key-event)
(on-draw world-to-scene)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CONSTANTS
(define CAT-IMAGE (bitmap "cat.png"))
;; how fast the cat falls, in pixels/tick
(define CATSPEED 8)
;; dimensions of the canvas
(define CANVAS-WIDTH 200)
(define CANVAS-HEIGHT 400)
(define EMPTY-CANVAS (empty-scene CANVAS-WIDTH CANVAS-HEIGHT))
(define CAT-X-COORD (/ CANVAS-WIDTH 2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; DATA DEFINITIONS
(define-struct world (pos paused?))
;; A World is a (make-world Integer Boolean)
;; Interpretation:
;; pos describes how far the cat has fallen, in pixels.
;; paused? describes whether or not the cat is paused.
;; template:
;; world-fn : World -> ??
;(define (world-fn w)
; (... (world-pos w) (world-paused? w)))
;;examples of worlds, for testing
(define unpaused-world-at-20 (make-world 20 false))
(define paused-world-at-20 (make-world 20 true))
(define unpaused-world-at-28 (make-world 28 false))
(define paused-world-at-28 (make-world 28 true))
;; help function for key event
;; is-pause-key-event? : KeyEvent -> Boolean
;; GIVEN: a KeyEvent
;; RETURNS: true iff the KeyEvent represents a pause instruction
(define (is-pause-key-event? ke)
(key=? ke " "))
;; examples for testing
(define pause-key-event " ")
(define non-pause-key-event "q")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; world-after-tick : World -> World
;; GIVEN: a world w
;; RETURNS: the world that should follow w after a tick.
;; EXAMPLES:
;; cat falling:
;; (world-after-tick unpaused-world-at-20) = unpaused-world-at-28
;; cat paused:
;; (world-after-tick paused-world-at-20) = paused-world-at-20
;; STRATEGY: Use template for World on w
(define (world-after-tick w)
(if (world-paused? w)
w
(make-world (+ (world-pos w) CATSPEED)
(world-paused? w))))
;; tests:
(begin-for-test
(check-equal?
(world-after-tick unpaused-world-at-20)
unpaused-world-at-28
"in unpaused world, the cat should fall CATSPEED pixels and world should still be unpaused")
(check-equal?
(world-after-tick paused-world-at-20)
paused-world-at-20
"in paused world, cat should be unmoved"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; world-to-scene : World -> Scene
;; RETURNS: a Scene that portrays the given world.
;; EXAMPLE: (world-to-scene (make-world 20 ??))
;; = (place-image CAT-IMAGE CAT-X-COORD 20 EMPTY-CANVAS)
;; STRATEGY: Use template for World on w
(define (world-to-scene w)
(place-image CAT-IMAGE CAT-X-COORD
(world-pos w)
EMPTY-CANVAS))
;; tests
;; an image showing the cat at Y = 20
;; check this visually to make sure it's what you want
(define image-at-20 (place-image CAT-IMAGE CAT-X-COORD 20 EMPTY-CANVAS))
;; note: these only test whether world-to-scene calls place-image properly.
;; it doesn't check to see whether that's the right image!
;; these are not very good test strings!
(begin-for-test
(check-equal?
(world-to-scene unpaused-world-at-20)
image-at-20
"test of (world-to-scene unpaused-world-at-20)")
(check-equal?
(world-to-scene paused-world-at-20)
image-at-20
"test of (world-to-scene paused-world-at-20)"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; world-after-key-event : World KeyEvent -> World
;; GIVEN: a world w and a keyevent kev
;; RETURNS: the world that should follow the given world
;; after the given key event.
;; on space, toggle paused?-- ignore all others
;; EXAMPLES: see tests below
;; STRATEGY: Cases on whether the kev is a pause event
(define (world-after-key-event w kev)
(if (is-pause-key-event? kev)
(world-with-paused-toggled w)
w))
;; world-with-paused-toggled : World -> World
;; RETURNS: a world just like the given one, but with paused? toggled
;; STRATEGY: Use template for World on w
(define (world-with-paused-toggled w)
(make-world
(world-pos w)
(not (world-paused? w))))
;; for world-after-key-event, we need 4 tests: a paused world, and an
;; unpaused world, and a pause-key-event and a non-pause key event.
(begin-for-test
(check-equal?
(world-after-key-event paused-world-at-20 pause-key-event)
unpaused-world-at-20
"after pause key, a paused world should become unpaused")
(check-equal?
(world-after-key-event unpaused-world-at-20 pause-key-event)
paused-world-at-20
"after pause key, an unpaused world should become paused")
(check-equal?
(world-after-key-event paused-world-at-20 non-pause-key-event)
paused-world-at-20
"after a non-pause key, a paused world should be unchanged")
(check-equal?
(world-after-key-event unpaused-world-at-20 non-pause-key-event)
unpaused-world-at-20
"after a non-pause key, an unpaused world should be unchanged"))