In addition to following the design recipe, all code must adhere to the following basic style guidelines:
;; ------------------------------------------------- GOOD ;; HHState -> HHState (define (hungry-henry-action-per-clock-tick hh-world-state) (bump-tick-value (eat-all-cup-cakes-in-reach (move-hungry-henry-closer-to-wp hh-world-state)))) ;; HHState -> HHState (define (bump-tick-value hh-world-state) ...) ;; HHState -> HHState (define (eat-all-cup-cakes-in-reach hh-world-state) ...) ;; HHState -> HHState (define (move-hungry-henry-closer-to-wp hh-world-state) ...)
edit → find longest line
menu.;; ------------------------ GOOD (define (f l) (cond [(empty? l) 0] [else (f (rest l))])) ;; ------------------------ BAD (define (f l) (cond [(empty? 1) 0] [else (f (rest l))] ) )
Not observing these very basic guidelines leads to unreadable code and to loss of points.
When you design functions that create (pseudo-)random results, do not give up on testing. To make this guide concrete, consider this example:
; Number Number -> Posn ; create a Posn located at random points in [0,width) x [0,height) (define (create-cupcake width height) (make-posn (random width) (random height)))
Just because this function returns a random Posn
does not mean you cannot test something about the relationship between its input(s) and output.
At a minimum, your tests can validate that the function always produces a Posn
:
(check-expect (posn? (create-cupcake 100 200)) #true) (check-expect (posn? (create-cupcake 20 1000)) #true)
Although this simplistic test may appear to be silly, it ensures that the function runs for some examples (e.g., no typos, primitives called correctly).
One step up your tests can include a property checker that confirms the purpose statement:
; Number Number Posn -> Boolean ; property tester (define (in-range? width height p) (and (<= 0 (posn-x p) width) (<= 0 (posn-y p) height))) (check-expect (in-range? 100 200 (create-cupcake 100 200)) #true) (check-expect (in-range? 20 1000 (create-cupcake 20 1000)) #true)
The introduction of a 2-line property checker for a 1-line function may seem overkill, but keep in mind that we purposefully keep the basic example small.
As your functions get more complicated, deploy “loops” to check entire lists, trees, forests and other complex, of randomly generated data structures:
; N -> [List-of Posn] ; create the given number of cupcakes (define (create-many-cupcakes n) (local ((define (one i) (create-cupcake SCENE-WIDTH SCENE-HEIGHT))) (build-list n one)))
(Note how one
ignores its argument. Still, why would make-list
in lieu of build-list
not produce the correct result here?)
For a function like this, your property checker must use andmap
to check all generated Posn
s:
; Posn -> Boolean ; property tester (define (one? p) (in-range? SCENE-WIDTH SCENE-HEIGHT p)) (check-expect (andmap one? (create-many-cupcakes 100)) #true)