diff --git a/collects/realm/chapter2/source.rkt b/collects/realm/chapter2/source.rkt new file mode 100644 index 0000000000..aa08e37539 --- /dev/null +++ b/collects/realm/chapter2/source.rkt @@ -0,0 +1,206 @@ +#lang racket + +(require rackunit rackunit/text-ui) + +;; Play a Guess my Number game at the REPL. +;; Run program. +;; Pick a number X between and . +;; Evaluate +;; (start ) +;; The program will respond with a number. +;; Use +;; (bigger) +;; and +;; (smaller) +;; to let it know what you think of the guess. +;; Watch how qiuckly the program guesses X. + +; +; +; +; ; +; ; +; ;;; ;;; +; ;; ;; +; ; ; ; ; ;;;; ;;; ;; ;;; +; ; ; ; ; ; ; ; ;; ; +; ; ; ; ; ; ; ; ; +; ; ; ; ;;;;;; ; ; ; +; ; ; ; ; ; ; ; +; ; ; ; ;; ; ; ; +; ;;; ;;; ;;;; ;; ;;;;;;; ;;; ;;; +; +; +; +; + +;; Example: +;; > (start 0 100) ; [0,100] +;; 50 +;; > (bigger) ; [51,100] +;; 75 +;; > (bigger) ; [76,100] +;; 88 +;; > (smaller) ; [76,87] +;; 82 + +;; Number Number -> Number +;; Start a new game in [n,m] and make a guess. +;; > (start 0 100) +;; 50 +(define (start n m) + (set! lower (min n m)) + (set! upper (max n m)) + (guess)) + +;; Lower and upper bounds on the number +(define lower 1) +(define upper 100) + +;; -> Nuber Number +;; Guess half-way between lower and upper bounds. +;; > (begin (start 0 100) (guess)) +;; 50 +(define (guess) + (quotient (+ lower upper) 2)) + +;; -> Number +;; Lower the upper bound and guess again. +;; > (begin (start 0 100) (smaller)) +;; 24 +(define (smaller) + (set! upper (max lower (sub1 (guess)))) + (guess)) + +;; -> Number +;; Raise the lower bound and guess again. +;; > (begin (start 0 100) (bigger)) +;; 75 +(define (bigger) + (set! lower (min upper (add1 (guess)))) + (guess)) + +; +; +; +; +; +; ;;;;;;; ; +; ; ; ; ; +; ; ; ; ;;; ;;;; ; ;;;;;;; ;;;; ; +; ; ; ; ; ; ; ;; ; ; ;; +; ; ; ; ; ; ; +; ; ;;;;;;; ;;;;; ; ;;;;; +; ; ; ; ; ; +; ; ; ; ; ; ; ; ; ; +; ;;;;; ;;;; ;;;;;; ;;;; ;;;;;; +; +; +; +; + + +(module+ test + + ;; basic guesses + + (check-equal? (guess) 50) + + (check-equal? (start 0 100) 50) + + (check-equal? (begin (start 0 100) lower) 0) + (check-equal? (begin (start 0 100) upper) 100) + (check-equal? (begin (start 0 100) (smaller)) 24) + (check-equal? (begin (start 0 000) (smaller)) 0) + (check-equal? (begin (start 0 100) (smaller) lower) 0) + (check-equal? (begin (start 0 100) (smaller) upper) 49) + (check-equal? (begin (start 0 100) (bigger)) 75) + (check-equal? (begin (start 0 000) (bigger)) 0) + + ;; testing a sequence of interactions with expected intermediate states + + (test-begin (start 0 100) + (bigger) + (check-equal? lower 51) + (check-equal? upper 100) + (bigger) + (check-equal? lower 76) + (check-equal? upper 100) + (smaller) + (check-equal? lower 76) + (check-equal? upper 87)) + + ;; doing it all over for negative numbers + + (check-equal? (start -100 0) -50) + + (check-equal? (begin (start -100 0) lower) -100) + (check-equal? (begin (start -100 0) upper) 0) + (check-equal? (begin (start -100 0) (smaller)) -75) + (check-equal? (begin (start -100 0) (smaller)) -75) + (check-equal? (begin (start -100 0) (smaller) lower) -100) + (check-equal? (begin (start -100 0) (smaller) upper) -51) + (check-equal? (begin (start -100 0) (bigger)) -24) + (check-equal? (begin (start -100 0) (bigger)) -24) + + (test-begin (start -100 0) + (bigger) + (check-equal? lower -49) + (check-equal? upper 0) + (bigger) + (check-equal? lower -23) + (check-equal? upper 0) + (smaller) + (check-equal? lower -23) + (check-equal? upper -12)) + + + ;; --------------------------------------------------------------------------- + ;; testing random properties of our functions + + ;; Property: + ;; For all games starting in [n,m] after any number of moves, + ;; lower <= upper. + (define (prop:ordered n m i) + (check-true + (begin (start n m) + (random-moves i) + (<= lower upper)))) + + ;; Property: + ;; For all games starting in [n,m], for any number of moves, + ;; lower grows up, upper grows down, or they are equal. + (define (prop:monotonic n m i) + (check-true + (begin (start n m) + (for/and ([i (in-range i)]) + (define l lower) + (define u upper) + (random-move) + (or (and (< l lower) (= u upper)) + (and (= l lower) (> u upper)) + (and (= l lower) (= u upper))))))) + + ;; Number -> Void + ;; Move randomly n times. + (define (random-moves i) + (unless (zero? i) + (random-move) + (random-moves (sub1 i)))) + + ;; -> Void + ;; Move randomly once. + (define (random-move) + (if (zero? (random 2)) + (smaller) + (bigger))) + + ;; property tests + (for ([i (in-range 1000)]) + (prop:ordered (random 1000) (random 1000) (random 100)) + (prop:monotonic (random 1000) (random 1000) (random 100))) + + ;; reset the boundaries to defaults + (start 1 100) + + "all tests run")