;=====
; CS 111 - Week 4 Lecture 2 - 2024-09-19
;=====

(require 2htdp/image)
(require 2htdp/universe)

;=====
; REMINDER
;=====
; cond expression
;
; syntax:
;    (cond
;        [boolean-expr1 expr1]
;        [boolean-expr2 expr2]
;        [boolean-expr3 expr3]
;        ...
;        [else expr-else]
;    )
;
; semantics:
; *   evaluate each clause's boolean expression in order
;     until it finds one with the value #true --
;     that clause's 2nd expression becomes the value of the cond
;     expression

;=====
;FROM WEEK 2 LECTURE 2
;=====
;=====
; signature: teal-star: number -> image
; purpose: expects the desired distance between
;   a star's points in pixels, and returns a solid
;   teal star of that size

(check-expect (teal-star 10)
              (star 10 "solid" "teal"))
(check-expect (teal-star 50)
              (star 50 "solid" "teal"))

(define (teal-star edge-size)
    (star edge-size "solid" "teal")
)
;===== (END of part copied over from Week 2 Lecture 2)

;=====
; refactored a bit more from Week 4 Lecture 1
;=====

(define WIDTH 400)
(define HEIGHT 200)
(define BACKDROP (empty-scene WIDTH HEIGHT))

(define CENTER-X (/ WIDTH 2))
(define CENTER-Y (/ HEIGHT 2))

;=====
; signature: teal-star-scene: number -> scene
; purpose: expects a star size, and returns a scene
;    of a centered teal star of that size

(check-expect (teal-star-scene 50)
              (place-image
                  (teal-star 50)
                  CENTER-X
                  CENTER-Y
                  BACKDROP))

(check-expect (teal-star-scene 8)
              (place-image
                   (teal-star 8)
                   CENTER-X
                   CENTER-Y
                   BACKDROP))

(define (teal-star-scene star-size)
   (place-image (teal-star star-size)
                CENTER-X
                CENTER-Y
                BACKDROP)
)

(teal-star-scene 50)
(teal-star-scene 8)

;===== (END of part refactored a bit more from Week 4 Lecture 1)

;-----
; reminder: big-bang's stop-when clause expects a function
;    that expects the current world type and returns a boolean,
;
;    and then each time big-bang's ticker ticks,
;        big-bang calls stop-when's function with the current
;        world value,
;
;    and if that function returns #true, big-bang ends
;        (and returns its finalworld value)

"this big-bang call ends gracefully when the world"
"    reaches 0:"

(big-bang 30
          (on-tick sub1)
          (to-draw teal-star-scene)
          (stop-when zero?))

"this big-bang call will KEEP going until you close"
"    this world-window!!:"

; since the world value is increased by add1 each time
;     big-bang's ticker ticks, so zero? should never return
;     #true for the current world value...!

(big-bang 30
          (on-tick add1)
          (to-draw teal-star-scene)
          (stop-when zero?))

;=====
; I decide I'd like a function to stop a
;    world when the star is too small *or*
;    too big...
; I also decide that I want the maximum star
;    size permitted to be 200

(define STAR-MAX 200)

;=====
; signature: bad-star-size?: number -> boolean
; purpose: expects a star size, and returns #true
;    if it is OUTSIDE of the desired star size range
;    of [1, STAR-MAX], and returns #false otherwise

;-----
; I want 5 tests for this interval data with
;    three ranges (too small, good size, too big)
;    and two boundaries between those intervals,
;    1 and STAR-MAX

(check-expect (bad-star-size? -5) #true)
(check-expect (bad-star-size? 1) #false)
(check-expect (bad-star-size? 20) #false)
(check-expect (bad-star-size? STAR-MAX) #false)
(check-expect (bad-star-size? 10000) #true)

(define (bad-star-size? star-size)
    ;(cond
    ;
    ;    ; VERSION 1: 3 intervals, 3 branches!
    ;
    ;    [(< star-size 1) #true]
    ;    [(> star-size STAR-MAX) #true]
    ;    [else #false]

    ;    ; I could refactor this! 2 branches have the same
    ;    ;    result value; if too small OR too large, we
    ;    ;    want #true:

    ;    [(or (< star-size 1)
    ;         (> star-size STAR-MAX)) #true]
    ;    [else #false]

    ;    ; I could also just use this boolean expression
    ;    ;    as the body expression (since this function
    ;    ;    returns type boolean):

         (or (< star-size 1)
             (> star-size STAR-MAX))         
    ;)
)

;=====
; let's land a penguin! 

;=====
; some useful named constants related to my penguin scenes

(define PENG-SC-WIDTH 300)
(define PENG-SC-HT 450)

(define PENG-SC-BACKGRD (empty-scene PENG-SC-WIDTH
                                     PENG-SC-HT))

(define PENG-CTR-X (/ PENG-SC-WIDTH 2))

;=====
; bitmap/url expects a string with a URL and returns the
;    image at that URL

(define FLOATING-PENGUIN
        (bitmap/url 
"https://nrs-projects.humboldt.edu/~st10/f24cs111/floating-penguin.png"))

(define LANDED-PENGUIN
        (bitmap/url
"https://nrs-projects.humboldt.edu/~st10/f24cs111/landed-penguin.png"))

FLOATING-PENGUIN
LANDED-PENGUIN

; because we found out the landed penguin's y-coordinate
;    needs to be slightly LESS than the scene's height
;    if we want it to be standing "on" the ground and not
;    underground:
; (how much less? half-of-the-penguin's-height less!)

(define LANDING-HEIGHT (- PENG-SC-HT
                          (/ (image-height LANDED-PENGUIN)
                             2)
                       ))

;=====
; signature: draw-penguin-scene: number -> scene
; purpose: expects a world-number, and if it is less than
;    LANDING-HEIGHT, it returns a scene of a floating penguin
;    whose y-coordinate is the current world-number,
;    otherwise, it returns a scene with the now-landed
;    penguin on the "ground"

; the interval-style data here has 2 intervals/categories,
;     so need 3 tests, one for each interval and
;     one for the boundary between them

(check-expect (draw-penguin-scene 10)
              (place-image FLOATING-PENGUIN
                           PENG-CTR-X
                           10
                           PENG-SC-BACKGRD))

(check-expect (draw-penguin-scene 10000)
              (place-image LANDED-PENGUIN
                           PENG-CTR-X
                           LANDING-HEIGHT
                           PENG-SC-BACKGRD))

(check-expect (draw-penguin-scene LANDING-HEIGHT)
              (place-image LANDED-PENGUIN
                           PENG-CTR-X
                           LANDING-HEIGHT
                           PENG-SC-BACKGRD))

(define (draw-penguin-scene world-num)
   (cond
      [(< world-num LANDING-HEIGHT)
           (place-image FLOATING-PENGUIN
                        PENG-CTR-X
                        world-num
                        PENG-SC-BACKGRD)]
      
      [else (place-image LANDED-PENGUIN
                         PENG-CTR-X
                         LANDING-HEIGHT
                         PENG-SC-BACKGRD)]
   )
)

(draw-penguin-scene 10)
(draw-penguin-scene 10000)

"trying big-bang with:"
"    (to-draw draw-penguin-scene)"
"    (on-tick add1)"
"    ...clauses!"
"CLOSE this window to END this big-bang expression!!"

(big-bang 30
    (to-draw draw-penguin-scene)
    (on-tick add1))

;=====
; the above examples used INTERVAL-STYLE data
;    (categories of numbers that are ranges of numbers)
; BUT, another common kind of data is when
;    your item is one from a smallish set of
;    separate, distinct things
; when you have this kind of data -- where you can
;    list, or enumerate, all the possible values -
;    that's what the text calls ENUMERATION-STYLE data
;
; for example:
; *   the possible colors of a traffic light!
; *   the possible arrow keys on a game controller!
; *   the possible answers for a multiple-choice question!

;=====
; do you see that if you are making a decision
;    based on enumeration-style data,
;    you MIGHT need a cond branch for each possible option?
;
;    *   yup, that's a good start!
;
;    and you can use else for any other value,
;    and you SHOULD when your function says what to
;    do in such a case
;
; (and because you might make a little error handling
;    any one of those, you should have a TEST for EACH
;    possible value, and one for none-of-the-above
;    if your function specified what to do for that)

;=====
; a SILLY example of enumeration data!

;=====
; say that I wanted a very silly function that returns
;    the next animal sound from
;    Sandra Boynton's book, "Moo, Baa, La La La"
;    given one of the sounds (well, for the first few)

;=====
; An animal sound for this function should be ONE of
;    of 4 sounds: moo, baa, la la la, or oink

;====
; signature: next-sound: string -> string
; purpose: expects an animal sound from the early
;    part of Sandra Boyton's book
;    "Moo, Baa, La La La", and returns the next
;    sound from that book as follows:
;    *   for moo, return baa
;    *   for baa, return la la la
;    *   for la la la, return oink
;    *   for oink, return moo
;    *   for anything else, return ???

;-----
; This enumeration contains 4 values, and the function
;    specifies something to do if given a value not in
;    that set, so need at least 5 tests:

(check-expect (next-sound "moo") "baa")
(check-expect (next-sound "baa") "la la la")
(check-expect (next-sound "la la la") "oink")
(check-expect (next-sound "oink") "moo")
(check-expect (next-sound "bark") "???")

(define (next-sound animal-sound)
  (cond
    [(string=? animal-sound "moo") "baa"]
    [(string=? animal-sound "baa") "la la la"]
    [(string=? animal-sound "la la la") "oink"]
    [(string=? animal-sound "oink") "moo"]
    [else "???"]
  )
)

(next-sound "moo")
(next-sound "baa")
(next-sound "huh?")

"NOTE: remember to close any leftover big-bang"
"    window(s)..."