/racketcon/monadic-turtles.rkt

http://github.com/VincentToups/racket-lib · Racket · 328 lines · 294 code · 32 blank · 2 comment · 29 complexity · a80b4da6137c5d0001853c3f3c6517f8 MD5 · raw file

  1. #lang racket
  2. (require
  3. racket/gui
  4. racket/match
  5. racket/dict
  6. (rename-in (only-in slideshow/pict bitmap) [bitmap bitmap->pict])
  7. utilities/planar-geometry
  8. utilities/fancy-destructuring
  9. utilities/draw-planar-geometry)
  10. (struct doublet (a b)
  11. #:property
  12. prop:custom-write
  13. (lambda (s port mode)
  14. (display "(doublet " port)
  15. (display (doublet-a s) port)
  16. (display " " port)
  17. (display (doublet-b s) port)
  18. (display ")" port)))
  19. (define (turtles-return item)
  20. (lambda (state-doublet)
  21. (match state-doublet
  22. [(doublet local-state global-state)
  23. (doublet (list (doublet item local-state)) global-state)])))
  24. (define (reduce proc lst)
  25. (foldl (lambda (it ac)
  26. (proc ac it))
  27. (car lst)
  28. (cdr lst)))
  29. (define (turtles-bind turtlesf turtlesf-prod)
  30. (lambda (state-doublet)
  31. (match (turtlesf state-doublet)
  32. [(doublet local-doublets global-state)
  33. (let loop ((local-doublets local-doublets)
  34. (global-state global-state)
  35. (local-doublets-out '()))
  36. (match local-doublets
  37. [(list) (doublet (reduce append (reverse local-doublets-out)) global-state)]
  38. [(cons local-doublet local-doublets)
  39. (match local-doublet
  40. [(doublet val local-state)
  41. (let ((new-turtle-fun (turtlesf-prod val)))
  42. (match (new-turtle-fun (doublet local-state global-state))
  43. [(doublet sub-local-doublets
  44. global-state)
  45. (loop local-doublets global-state (cons sub-local-doublets local-doublets-out))]))])]))])))
  46. (define-syntax (turtles-let* stx)
  47. (syntax-case stx ()
  48. [(turtles-let* ((var expr)) body ...)
  49. (syntax (turtles-bind expr (lambda (var) body ...)))]
  50. [(turtles-let* ((var0 expr0) (var expr) ...) body ...)
  51. (syntax (turtles-bind expr0 (lambda (var0)
  52. (turtles-let* ((var expr) ...) body ...))))]))
  53. (define (turtles-plus tf1 tf2)
  54. (turtles-let*
  55. ((_ tf1))
  56. tf2))
  57. (define turtles-zero
  58. (lambda (state-doublet)
  59. (doublet (list) (doublet-b state-doublet))))
  60. (define m-turtles
  61. (list
  62. (cons 'bind turtles-bind)
  63. (cons 'return turtles-return)
  64. (cons 'plus turtles-plus)
  65. (cons 'zero turtles-zero)))
  66. (define (set-local symbol val)
  67. (lambda (state-doublet)
  68. (match state-doublet
  69. [(doublet local-state global-state)
  70. (doublet (list
  71. (doublet val
  72. (dict-set local-state symbol val)))
  73. global-state)])))
  74. (define (get-local symbol . args)
  75. (match args
  76. [(list) (lambda (state-doublet)
  77. (match state-doublet
  78. [(doublet local-state global-state)
  79. (doublet (list (doublet (dict-ref local-state symbol) local-state))
  80. global-state)]))]
  81. [(list or-val)
  82. (lambda (state-doublet)
  83. (match state-doublet
  84. [(doublet local-state global-state)
  85. (doublet (list (doublet (dict-ref local-state symbol (lambda () or-val)) local-state))
  86. global-state)]))]))
  87. (define (get-global symbol . args)
  88. (match args
  89. [(list)
  90. (lambda (state-doublet)
  91. (match state-doublet
  92. [(doublet local-state global-state)
  93. (doublet (list (doublet (dict-ref global-state symbol) local-state))
  94. global-state)]))]
  95. [(list or-val)
  96. (lambda (state-doublet)
  97. (match state-doublet
  98. [(doublet local-state global-state)
  99. (doublet (list (doublet (dict-ref global-state symbol (lambda () or-val)) local-state))
  100. global-state)]))]))
  101. (define (set-global symbol val)
  102. (lambda (state-doublet)
  103. (match state-doublet
  104. [(doublet local-state global-state)
  105. (doublet (list
  106. (doublet val local-state))
  107. (dict-set global-state symbol val))])))
  108. (define (set-simultaneously . associations)
  109. (lambda (state-doublet)
  110. (match state-doublet
  111. [(doublet local-state global-state)
  112. (let loop ((associations associations)
  113. (acc '()))
  114. (match associations
  115. [(list) (doublet (reverse acc) global-state)]
  116. [(cons association associations)
  117. (loop associations
  118. (cons (doublet
  119. (map cadr association)
  120. (foldl
  121. (lambda (pair local-state)
  122. (dict-set local-state (car pair) (cadr pair)))
  123. local-state
  124. association))
  125. acc))]))])))
  126. (define (split-set symbol . vals)
  127. (lambda (state-doublet)
  128. (match state-doublet
  129. [(doublet local-state global-state)
  130. (doublet (map
  131. (lambda (val)
  132. (doublet val (dict-set local-state symbol val)))
  133. vals)
  134. global-state)])))
  135. (define (prep-jump-args lst)
  136. (let loop [(lst lst)
  137. (acc '())]
  138. (match lst
  139. [(list a b) (reverse (cons (point a b) acc))]
  140. [(cons a (cons b rest))
  141. (loop rest (cons (point a b) acc))])))
  142. (define (jump-to . args)
  143. (let [(locations (prep-jump-args args))]
  144. (apply split-set 'pos locations)))
  145. (define (turn . args)
  146. (turtles-let*
  147. ((facing (get-local 'facing 0))
  148. (helicity (get-local 'helicity 1))
  149. (facing (apply split-set 'facing
  150. (map (lambda (x)
  151. (+ (* x helicity) facing))
  152. args))))
  153. (turtles-return facing)))
  154. (define get-facing-vector
  155. (turtles-let*
  156. ((fac (get-local 'facing 0)))
  157. (turtles-return (radians->point-vector fac))))
  158. (define (move-raw . args)
  159. (turtles-let*
  160. ((pos (get-local 'pos (point 150 150)))
  161. (scaling (get-local 'scaling 1))
  162. (facv get-facing-vector))
  163. (apply split-set 'pos
  164. (map
  165. (lambda (amount)
  166. (point+
  167. pos
  168. (point-scale facv (* scaling amount))))
  169. args))))
  170. (define (move . args)
  171. (turtles-let*
  172. ((p1 (get-local 'pos (point 150 150)))
  173. (p2 (apply move-raw args))
  174. (draw-fun (get-local 'motion-function add-line-pts)))
  175. (draw-fun p1 p2)))
  176. (define (add-line-pts p1 p2)
  177. (turtles-let*
  178. ((things-to-draw (get-global 'things-to-draw))
  179. (things-to-draw (set-global 'things-to-draw
  180. (cons (line-segment p1 p2) things-to-draw))))
  181. (turtles-return things-to-draw)))
  182. (define (make-turtles)
  183. (doublet
  184. ; local state
  185. (list (cons 'pos (point 150 150))
  186. (cons 'facing 0)
  187. (cons 'helicity 1)
  188. (cons 'motion-function add-line-pts)
  189. (cons 'scaling 1))
  190. ; global state
  191. (list (cons 'things-to-draw '()))))
  192. (define (n-times n tsf)
  193. (match n
  194. [0 (turtles-return #t)]
  195. [1 tsf]
  196. [(? positive? (? integer?))
  197. (turtles-let* ((_ tsf))
  198. (n-times (- n 1) tsf))]))
  199. (define (turtles->pict fun . args)
  200. (dlet1 ((:> or
  201. '((width . 300)
  202. (height . 300)))
  203. width 'width
  204. height 'height)
  205. args
  206. (let* ((bitmap (make-object bitmap% width
  207. height
  208. #f
  209. #f))
  210. (dc (new bitmap-dc% [bitmap bitmap]))
  211. (final-state (fun (make-turtles)))
  212. (things (dict-ref (doublet-b final-state) 'things-to-draw '())))
  213. (let loop ((things things))
  214. (match things
  215. [(list) (bitmap->pict bitmap)]
  216. [(cons thing things)
  217. (draw-shape dc thing)
  218. (loop things)])))))
  219. (define turtle-frame (new frame% [label "Turtle"]
  220. [width 300]
  221. [height 300]))
  222. (send turtle-frame show #f)
  223. (define (turtle-paint-callback self dc) 'pass)
  224. (define turtle-canvas (new canvas%
  225. [parent turtle-frame]
  226. [paint-callback (lambda (self dc)
  227. (turtle-paint-callback self dc))]))
  228. (define (turtles-show fun . args)
  229. (dlet1
  230. ((:> or
  231. '((width . 300)
  232. (height . 300)))
  233. width 'width
  234. height 'height) args
  235. (let* ((final-state (fun (make-turtles)))
  236. (things (dict-ref (doublet-b final-state) 'things-to-draw '())))
  237. (send turtle-frame show #f)
  238. (set! turtle-paint-callback
  239. (lambda (self dc)
  240. (send dc set-background "white")
  241. (send dc clear)
  242. (let loop ((things things))
  243. (if (empty? things) 'done
  244. (begin (draw-shape dc (car things))
  245. (loop (cdr things)))))))
  246. (send turtle-frame show #t))))
  247. (define (turtles->png fun . args)
  248. (dlet1 ((:> or
  249. '((width . 300)
  250. (height . 300)
  251. (filename . "/tmp/turtle.png")))
  252. width 'width
  253. height 'height
  254. filename 'filename)
  255. args
  256. (let* ((bitmap (make-object bitmap% width
  257. height
  258. #f
  259. #f))
  260. (dc (new bitmap-dc% [bitmap bitmap]))
  261. (final-state (fun (make-turtles)))
  262. (things (dict-ref (doublet-b final-state) 'things-to-draw '())))
  263. (let loop ((things things))
  264. (match things
  265. [(list) (send bitmap save-file filename 'png)]
  266. [(cons thing things)
  267. (draw-shape dc thing)
  268. (loop things)])))))
  269. (define (set-motion-function . args)
  270. (apply split-set 'motion-function args))
  271. (provide set-motion-function
  272. turtles->png
  273. turtles->pict
  274. turtles-show
  275. n-times
  276. turtles-return
  277. turtles-bind
  278. turtles-plus
  279. turtles-zero
  280. move
  281. turn
  282. set-local
  283. set-global
  284. get-local
  285. get-global
  286. split-set
  287. get-facing-vector
  288. set-simultaneously
  289. jump-to
  290. m-turtles
  291. turtles-let*
  292. doublet
  293. doublet-a
  294. doublet-b
  295. doublet?
  296. add-line-pts)