Please send questions to st10@humboldt.edu .

(require 2htdp/batch-io)
(require 2htdp/image)
(require 2htdp/universe)

; CS 131 - Week 6, Lecture 2

; remember: SAVE your Definitions window before
;    trying to Run any of the batch-io functions;
;    they need to refer to the directory the 
;    Definitions window is saved in

;------------
; signature: read-file: string -> string
; purpose: expects the name of a file in the directory
;    where the Definitions window is saved, and 
;    reads its contents, then producing a string
;    containing its contents (with \n for each newline)
; (this will cause an error if the file does not exist)

(read-file "looky.txt")

;------------
; signature: write-file: string string -> boolean
; purpose: expects a file name and a string to be 
;    written to that file, and has the side-effect
;    of writing that string to a file with that
;    name in the current working directory, and
;    produces whether it succeeded

(write-file "sample.dat" "212")

; uncomment this to see the error message you get if you try
;    to read from a non-existent file...
;
;(read-file "bunny-rabbit.txt")

;-----------
; data definition for a list-of-string
; 
; a list-of-string is:
;   - empty, or
;   - (cons string list-of-string)

;------------
; template for a function uses-str-list that has a list-of-string
;    parameter string-list
;
; (define (uses-str-list ... string-list ...)
;    (cond
;       [(empty? string-list) ...]
;       [else (...(first string-list) ...
;                 (uses-string-list ... (rest string-list) ...) ...)]
;    )
; )

;------------
; signature: read-lines: string -> list-of-string
; purpose: expects the name of a file in the current
;    directory, reads its contents, and produces them
;    as a list of strings, one string per line in the
;    file

(read-lines "looky.txt")

;-----------
; read-words
; what if you'd like the contents of a file to
;    be considered as chunks, or TOKENS, separated
;    by white space? (space, tab, newline)
; read-words might be convenient:

;------------
; signature: read-words: string -> list-of-string
; purpose: expects the name of a file in the current
;   directory, reads its contents, and produces them
;   as a list of strings, one string per 
;   write-space-separated token in the file

(read-words "looky.txt")

(first (read-words "clicker1.txt"))

;------------
; what if you'd like each line to be a list, a list of
;    its tokens expressed as strings,
;    within a list of lines?
; you want: read-words/line 

;----------
; data definition for a list-of-string-list:
;
; a list-of-string-list is:
;    - empty, or
;    - (cons list-of-string  list-of-string-list)

;------------
; template for a function uses-str-lists that has a  
;    list-of-string-list parameter string-list-list
;
; (define (uses-str-lists ... string-list-list ...)
;    (cond
;       [(empty? string-list-list) ...]
;       [else (...(first string-list-list) ...
;                 (uses-str-lists ... 
;                    (rest string-list-list) ...) ...)]
;    )
; )

;------------
; signature: read-words/line: string -> list-of-string-list
; purpose: expects the name of a file in the current
;    directory, reads it contents, and produces them
;    as a list of string lists, one list per line,
;    where each line is represented as a list of
;    white-space separated tokens

(read-words/line "looky.txt")
(first (read-words/line "clicker1.txt"))

;------------
; LET's USE SOME OF THESE...

;------------
; data definition for a list-of-number
; 
; a list-of-number is:
;   - empty, or
;   - (cons number list-of-number)

;------------
; template for a function uses-num-list that has a list-of-number
;    parameter num-list
;
; (define (uses-num-list ... num-list ...)
;    (cond
;       [(empty? num-list) ...]
;       [else (...(first num-list) ...
;                 (uses-num-list ... (rest num-list) ...) ...)]
;    )
; )

;----------
; signature: num-list-to-string: list-of-number -> string
; purpose: expects a list of numbers, and produces
;    a string depiction of each number in the list
;    followed by a newline "\n" (as one big string)

(define (num-list-to-string num-list)
    (cond
       [(empty? num-list) ""]
       [else (string-append
              (number->string (first num-list))
              "\n"
              (num-list-to-string (rest num-list)))]
    )
)

(check-expect (num-list-to-string empty) "")
(check-expect (num-list-to-string (list 1 2 3))
              "1\n2\n3\n")

;----------
; signature: write-number-list: string list-of-number
;    -> boolean
; purpose: expects the name of a file to write to,
;    and it tries (as a side-effect) to write those
;    numbers to that file, one per line, and
;    produces whether it succeeds

(define (write-number-list file-name number-list)
  (write-file file-name 
              (num-list-to-string number-list))
)

; when there are side-effects -- you may need to include
;    some comments discussing WHAT side effects should 
;    occur...!
; OR -- some creative use of read functions to verify what
;    was written;
(check-expect (write-number-list "look-test1.txt" empty)
              true)

; ...after this write-number-list example, a read-file of that 
;    file should produce the empty string, if write-number-list 
;    worked properly;

(check-expect (read-file "look-test1.txt") "")

(check-expect (write-number-list "look-test2.txt"
                                 (list 1 2 3))
              true)

; ...after this write-number-list example, a read-file of that 
;    file should produce the string "1\n2\n3", if write-number-list 
;    worked properly;
; (the newline after 3 is actually there, but read-file doesn't
;    include it in its results string...

(check-expect (read-file "look-test2.txt")
              "1\n2\n3")

;------------

(define WIDTH 300)
(define HEIGHT 200)

(define MIN-RAD 5)
(define MAX-RAD (/ (min WIDTH HEIGHT) 2))

;----------
; signature: generate-radii: whole-number -> number-list
; purpose: expects the number of radii desired, and
;    generates a list of that many radii all in the
;    range [MIN-RAD, MAX-RAD)

(define (generate-radii how-many)
    (cond
       [(= how-many 0) empty]
       [else (cons (+ MIN-RAD (random (- MAX-RAD MIN-RAD)))
                   (generate-radii (- how-many 1)))]
    )
)

(check-expect (generate-radii 0) empty)

; at least this will test if the generated result is a list,
;    and is of length 4...

(check-expect (length (generate-radii 4)) 4)

;------------
; example of using write-number-list -- it can create a
;    file saving the list of radii generated by a call
;    of generate-radii -- even if we generate a LOT of radii;

(write-number-list "100-radii.txt" (generate-radii 100))

;-----------
; signature: string-list-to-num-list: list-of-string ->
;    list-of-number
; purpose: expects a list of strings (whose contents
;    are formattable as numbers!) and produces
;    a list of equivalent numbers

(define (string-list-to-num-list string-list)
    (cond
       [(empty? string-list) empty]
       [else (cons (string->number(first string-list))
               (string-list-to-num-list 
                (rest string-list)))]
    )
)

(check-expect (string-list-to-num-list empty)
              empty)
(check-expect (string-list-to-num-list (list "1" "2" "3"))
              (list 1 2 3))

;----------
; signature: many-circles: number-list -> scene
; purpose:  expects a list of circle radii
;    and produces a scene containing centered circles
;    with those radii

(define BACKDROP (empty-scene WIDTH HEIGHT))

(define (many-circles rad-list)
   (cond
      [(empty? rad-list) BACKDROP]
      [else (place-image 
               (circle (first rad-list) "outline" "black")
               (/ WIDTH 2)
               (/ HEIGHT 2)
               (many-circles (rest rad-list)))]
   )
)

(check-expect (many-circles empty)
              BACKDROP)

(check-expect (many-circles (cons 50 (cons 25 (cons 66 empty))))
              (place-image 
                 (circle 50 "outline" "black")
                 (/ WIDTH 2)
                 (/ HEIGHT 2)
                 (place-image 
                    (circle 25 "outline" "black")
                    (/ WIDTH 2)
                    (/ HEIGHT 2)
                    (place-image
                       (circle 66 "outline" "black")
                       (/ WIDTH 2)
                       (/ HEIGHT 2)
                       BACKDROP))))

(many-circles (cons 50 (cons 25 (cons 66 empty))))

;------------
; now, here is an example of using string-list-to-num-list
;    to allow many-circles to use radii numbers from
;     a file...
; (my error at the end of class? Trying to do the read of the
;     file before doing the write to that file! Ouch!
;     ...these are now in the right order to work!)

; first, create a file of radii values...

(write-number-list "20-radii.txt" (generate-radii 20))

; now, use that file's contents to generate a scene of many circles

(many-circles (string-list-to-num-list
                  (read-words "20-radii.txt")))

; and if you look at the file 20-radii.txt, you'll see it
;    DOES contain 20 numbers suitable for circle radii..