[racket] use plot as render in universe
Here is a more Rackety version of the program, including some graphical (failing) tests.
world->list should have a test.
purpose statements are missing.
#lang racket
(require (prefix-in p: plot) 2htdp/image 2htdp/universe)
(module+ test (require rackunit))
;; ---------------------------------------------------------------------------------------------------
;; constants
(define DAMPING 1/100)
(define UP 1.25)
(define DOWN 0.75)
;; graphical constants
(define BACKGROUND (square 600 'solid 'black))
;; ---------------------------------------------------------------------------------------------------
;; World = [List Price [List-of Price]]
;; World is a list of current price and a list of all past prices in chronological order
(define world0 (list 1 (list 1)))
;; imagine adding a price pf 2 to world0
(define world1 (list 2 (list 1 2)))
;; ---------------------------------------------------------------------------------------------------
;; main function
;; World -> World
;; ???? what is the purpose of this main function ???
(define (sim init)
(big-bang init
(on-tick random-walk 1/2)
(on-key shock)
(to-draw plot-render)))
;; ---------------------------------------------------------------------------------------------------
;; World -> World
;; advances world by one more trading session
;; world = [list _ history0]
;; [list current history] = (random-walk world)
;; => history = (append history0 (list current))
;;
;; (Number n) => '(1 (1)) -> (n (1 n))
(module+ test (check-equal? (length (world-prices (random-walk world0))) 2))
(define (random-walk world)
(match-define (list current historical) world)
(define new-price (+ current (step)))
(list new-price (append historical (list new-price))))
;; -> [-0.5/100, 0.5/100)
(define (step)
(* (damper) (random-btw-neg-half-and-pos-half)))
;; ---------------------------------------------------------------------------------------------------
;; World KeyEvent -> World
(module+ test (check-equal? (world-price (market-shock world0 1.5)) 1.5))
(define (shock world a-key)
(cond
[(key=? a-key "up") (market-shock world UP)]
[(key=? a-key "down") (market-shock world DOWN)]
[else world]))
;; World Number -> World
;; ???
(define (market-shock world shock)
(match-define (list current historical) world)
(define new-price (* current shock))
(list new-price (append historical (list new-price))))
;; -> [-0.5, 0.5)
(define (random-btw-neg-half-and-pos-half)
(- (random) 1/2))
;; -> Number
(define (damper) ;; why is this not a constant?
DAMPING)
;; ---------------------------------------------------------------------------------------------------
;; need to have at least 2 data points otherwise you are greeted by
;; plot: could not determine sensible plot bounds; got x ∈ [0,0], y ∈ [1,1]
(module+ test
(check-equal? (plot-render world0) BACKGROUND)
(define history1 (world-prices world1))
(check-equal? (plot-render world1)
(overlay (p:plot (p:lines (world->list-of-vectors history1))) BACKGROUND)))
(define (plot-render world)
(if (< (length (world-prices world)) 2) ;; express this with rest/rest; this is slow!
BACKGROUND
(overlay (p:plot (p:lines (world->list-of-vectors world))) BACKGROUND)))
;; World -> [#(Int Number)]
(define (world->list-of-vectors world)
(for/list ((price (world-prices world)) (i (in-naturals)))
(vector i price)))
;; ---------------------------------------------------------------------------------------------------
;; general auxiliaries
;; World -> Number
(module+ test (check-equal? (world-price world0) 1))
(define (world-price world)
(first world))
;; World -> [List-of Number]
(module+ test (check-equal? (world-prices world0) (list 1)))
(define (world-prices world)
(second world))
;; ---------------------------------------------------------------------------------------------------
;; run program run %% I recommend against running the main program from the definitions area
; (define final-world (sim world0))