spacepaste

  1.  
  2. ; The Grid
  3. ; --------
  4. ; The grid of trees has dimensions size × size, where the size is set by the following variable:
  5. (define size 10)
  6. ; For example, if the size were 3, the initial grid would be 3 × 3, and be drawn as:
  7. #;.
  8. ; When making a list representing a point, use the following alias to make the intent clear:
  9. (define point list)
  10. ; When extracting the first and second element of a point, use the following aliases:
  11. (define X first)
  12. (define Y second)
  13. ; A cleared part of the grid will be represented as a list of points.
  14. ; For example, the following represents a small “L” shaped path in the grid:
  15. #;(list (point 0 1)
  16. (point 0 0)
  17. (point 1 2)
  18. (point 0 2))
  19. ; If the size of the grid were 3, that would be drawn as:
  20. #;.
  21. ; Drawing The Maze
  22. ; ================
  23. (define barrier (render (scale . 1/4)))
  24. ; floor-piece : function color → image
  25. ; ------------------------------------
  26. ; This function produces a shape of a particular color, with the same dimensions as ‘barrier’.
  27. #;(check-expect (floor-piece rectangle "brown") .)
  28. #;(check-expect (floor-piece ellipse "orange") .)
  29. ; ★ Write a Partial (or Full) Design check-expect for floor-piece:
  30. (check-expect (floor-piece rectangle "brown") .)
  31. (check-expect (floor-piece ellipse "orange") .)
  32. ; ★ Fix floor-piece:
  33. (define
  34. (floor-piece shape color)
  35. (shape 25 28 "solid" color))
  36. (define ground (floor-piece rectangle "brown"))
  37. (define invisible (floor-piece rectangle "transparent"))
  38. (define highlight (overlay (shrink (floor-piece ellipse (list 100 100 0 25)))
  39. invisible))
  40. ; xs : list-of-points → list-of-numbers
  41. ; -------------------------------------
  42. ; This function extracts the X co-ordinates of a list of points.
  43. #;(check-expect (xs (list (point 1 2) (point 3 4) (point 1 5)))
  44. (list 1 3 1))
  45. ; ★ Write a Partial (or Full) Design check-expect for xs:
  46. (check-expect (xs (list (point 1 2) (point 3 4) (point 1 5)))
  47. (list 1 3 1))
  48. ; ★ Fix xs:
  49. (define (xs points)
  50. (map X points))
  51. ; row : number list-of-points → list-of-points
  52. ; --------------------------------------------
  53. ; This function extracts the points that have a particular Y co-ordinate, from a list of points.
  54. #;(check-expect (row (list (point 1 2) (point 3 4) (point 2 2)) 2)
  55. (list (point 1 2) #;(point 3 4) (point 2 2)))
  56. #;(check-expect (row (list (point 1 2) (point 3 4) (point 2 2)) 2)
  57. (local [(define (y? a-point)
  58. (= (Y a-point) 2))]
  59. (list (point 1 2) #;(point 3 4) (point 2 2))))
  60. ; ★ Write a Full Design check-expect for row:
  61. (check-expect (row (list (point 1 2) (point 3 4) (point 2 2)) 2)
  62. (list (point 1 2) #;(point 3 4) (point 2 2)))
  63. (check-expect (row (list (point 1 2) (point 3 4) (point 2 2)) 2)
  64. (local [(define (y? a-point)
  65. (= (Y a-point) 2))]
  66. (list (point 1 2) #;(point 3 4) (point 2 2))))
  67. ; ★ Fix row:
  68. (define (row points y)
  69. (local [(define (y? a-point)
  70. (= (Y a-point) y))]
  71. (sift y? points)))
  72. ; xs->image : number list-of-numbers image image → image
  73. ; -----------------------------------------------------
  74. ; This function draws a row of foreground and background images, with a given total number of images,
  75. ; and a list of which images (numbered left to right as 0, 1, 2, ...) are the foreground ones.
  76. #;(check-expect (xs->image 4 (list 2 0) ground barrier) .)
  77. #;(check-expect (xs->image 4 (list 2 0) ground barrier) (beside ground barrier ground barrier))
  78. #;(check-expect (xs->image 4 (list 2 0) ground barrier)
  79. (local [(define (represent x)
  80. (if [(element? x (list 2 0)) ground]
  81. [else barrier]))]
  82. (beside (represent 0) (represent 1) (represent 2) (represent 3))))
  83. ; ★ Write a Fuller (or Full) Design check-expect for xs->image:
  84. (check-expect (xs->image 4 (list 2 0) ground barrier) .)
  85. (check-expect (xs->image 4 (list 2 0) ground barrier) (beside ground barrier ground barrier))
  86. (check-expect (xs->image 4 (list 2 0) ground barrier)
  87. (local [(define (represent x)
  88. (if [(element? x (list 2 0)) ground]
  89. [else barrier]))]
  90. (beside (represent 0) (represent 1) (represent 2) (represent 3))))
  91. (define (xs->image total some-xs foreground background)
  92. (local [(define (represent x)
  93. (if [(element? x some-xs) foreground]
  94. [else background]))](apply beside(map represent(range 0 total 1)))))
  95. ; points->image : number list-of-points image image → image
  96. ; ---------------------------------------------------------
  97. ; This function takes a list of points and draws them using a foreground image, filling in
  98. ; the rest of the grid with a background image.
  99. (define (points->image a-size points foreground background)
  100. (local [(define (row->image y)
  101. (xs->image a-size (xs (row points y)) foreground background))]
  102. (apply above (map row->image (range 0 size 1)))))
  103. ; draw-all : list-of-points point point → image
  104. ; ---------------------------------------------
  105. ; This function draws:
  106. ; • the maze
  107. ; • kat at a particular point
  108. ; • the path from kat to a particular point
  109. (define kat (render (scale . 1/4)))
  110. (define (draw-all a-maze kat-point click-point)
  111. (align-overlay "left" "top"
  112. (above (rectangle 0 (* (height barrier) (Y kat-point)) "solid" "transparent")
  113. (beside (rectangle (* (width barrier) (X kat-point)) 0 "solid" "transparent")
  114. kat))
  115. (overlay (points->image size (path a-maze kat-point click-point) highlight invisible)
  116. (points->image size a-maze invisible barrier)
  117. (scale ground size))))
  118. ; Test The Functionality
  119. ; ----------------------
  120. ; Run the program now, and you should see a forest of trees, with a small L-shaped path.
  121. ; Pressing keys should move kat to the right.
  122. ; Clicking in the L-shaped path should highlight the point you clicked with an ellipse.
  123. ; Maze Generation Algorithm
  124. ; =========================
  125. ; The maze (cleared path) will start off containing a single point, for example:
  126. #;(check-expect (in-grid? (point -1 0)) #false)
  127. #;(check-expect (in-grid? (point 3 size)) #false)
  128. #;(check-expect (in-grid? (point 1 2)) #true)
  129. #;(check-expect (in-grid? (point (- size 1) 0)) #true)
  130. #;(check-expect (in-grid? (point -1 0)) #false)
  131. #;(check-expect (in-grid? (point 3 size)) #false)
  132. (check-expect (in-grid? (point -1 0))
  133. (if [(< -1 0) #false]
  134. [(< 0 0) #false]
  135. [(> (- size -1)0) #true]
  136. [(> (- size 0)0) #true]
  137. [(< -1 size) #true]
  138. [(< 0 size) #true]
  139. [(< -1 0) #false]
  140. [(< 0 0) #false]
  141. [else #false]))
  142. (define (in-grid? a-point)
  143. (if [(< (X a-point) 0) #false]
  144. [(< (Y a-point) 0) #false]
  145. [(> (- size (X a-point))0) #true]
  146. [(> (- size (Y a-point))0) #true]
  147. [(< (X a-point) size) #true]
  148. [(< (Y a-point) size) #true]
  149. [else #false]))
  150. #;(check-expect (neighbours (point 3 7))
  151. (local [(define x 3)
  152. (define y 7)]
  153. (list (point x 6)
  154. (point x 8)
  155. (point 2 y)
  156. (point 4 y))))
  157. #;(check-expect (neighbours (point 3 7))
  158. (local [(define x 3)
  159. (define y 7)]
  160. (list (point x 6)
  161. (point x 8)
  162. (point 2 y)
  163. (point 4 y))))
  164. (define (neighbours a-point)
  165. (local [(define x (X a-point))
  166. (define y (Y a-point))]
  167. (list (point x (- y 1 ))
  168. (point x (+ y 1))
  169. (point (- x 1) y)
  170. (point (+ x 1)y))))
  171. #;(check-expect (intersection (list "ant" "bee" "cat" "bee" "dog") (list "dog" "eel" "bee"))
  172. (list "bee" "bee" "dog"))
  173. #;(check-expect (intersection (list "ant" "bee" "cat" "bee" "dog") (list "dog" "eel" "bee"))
  174. (local [(define (in-list-2? e) #true)]
  175. (list #;"ant" "bee" #;"cat" "bee" "dog")))
  176. (define (intersection list-1 list-2)
  177. (local [(define e list-1)
  178. (define (in-list-2? e)
  179. (element? e list-2))]
  180. (sift in-list-2? list-1)))
  181. ; random-element : list → any
  182. ; ---------------------------
  183. ; This function produces an element randomly chosen from a non-empty list.
  184. #;(check-expect (<= 0 (random-element (range 0 1000 1)) 999)
  185. #true)
  186. #;(check-expect (= (random-element (range 0 1000 1))
  187. (random-element (range 0 1000 1)))
  188. ; Probably:
  189. #false)
  190. #;(check-expect (element? (random-element (list "programming" "is" "fun"))
  191. (list "programming" "is" "fun"))
  192. #true)
  193. (define (random-element list1)
  194. (element list1 (random (length list1))))
  195. ; random-neighbour : point → point
  196. ; --------------------------------
  197. ; This function produces a random neighbour of a point.
  198. #;(check-expect (element? (random-neighbour (point 123 104)) (list (point 123 103)
  199. (point 123 105)
  200. (point 122 104)
  201. (point 124 104)))
  202. #true)
  203. (define (random-neighbour a-point)
  204. (random-element (neighbours a-point)))
  205. ; bridge? : point list-of-points → boolean
  206. ; ----------------------------------------
  207. ; This function determines whether adding a point to a list-of-points creates a “bridge”:
  208. ; does it connect two or more points in the list-of-points?
  209. #;(check-expect (bridge? (point 3 4)
  210. (list (point 1 2)
  211. (point 3 3) ; one of the neighbours of (point 3 4)
  212. (point 4 5)
  213. (point 2 4))) ; one of the neighbours of (point 3 4))
  214. ; (point 3 4) touches, so connects, (point 3 3) and (point 2 4)
  215. #true)
  216. #;(check-expect (bridge? (point 3 4)
  217. (list (point 1 2)
  218. (point 3 3)
  219. (point 4 5)
  220. (point 3 2)))
  221. #false)
  222. #;(check-expect (bridge? (point 3 4)
  223. (list (point 1 2)
  224. (point 3 3)
  225. (point 4 5)
  226. (point 2 4)))
  227. (>= (length (list (list 3 3) #;(list 3 5) (list 2 4) #;(list 4 4)))
  228. 2))
  229. (define (bridge? a-point points)
  230. (element? a-point (apply join (map neighbours (join points)))))
  231. ; maybe-attach : point list-of-points → list-of-points
  232. ; ----------------------------------------------------
  233. ; This function checks the three conditions mentioned in #3 of the maze generation algorithm,
  234. ; and adds the point to the maze if the three conditions are satisfied, otherwise it just
  235. ; produces the maze unchanged.
  236. #;(check-expect (maybe-attach (point 1 2)
  237. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  238. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  239. #;(check-expect (maybe-attach (point -1 2)
  240. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  241. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  242. #;(check-expect (maybe-attach (point 1 1)
  243. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  244. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  245. #;(check-expect (maybe-attach (point 2 2)
  246. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  247. (list (point 2 2) (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  248. ; ★ Write a Partial (or Full) Design check-expect for the case covered by the previous check-expect:
  249. (define (maybe-attach a-point a-maze)
  250. (if [(same? (and (bridge? a-point a-maze)
  251. (not(element? a-point a-maze)))
  252. #true)
  253. (adjoin a-point a-maze)]
  254. [else a-maze]))
  255. (define (try-grow a-maze)
  256. (local [(define new-maze (maybe-attach (random-neighbour (random-element a-maze)) a-maze))]
  257. (if [(and (same? a-maze new-maze)
  258. (positive? (random (squared size))))
  259. (try-grow a-maze)]
  260. [else new-maze])))
  261. ; The following has a large probability of being correct:
  262. #;(check-expect (local [(define small-maze (try-grow (list (point 0 0))))]
  263. (and (= (length small-maze) 2)
  264. (element? (point 0 0) small-maze)
  265. (or (element? (point 0 1) small-maze)
  266. (element? (point 1 0) small-maze))))
  267. #true)
  268. ; Unless size is small, the following produces a list of ten mazes, growing by one point each time:
  269. #;(repeats try-grow (list (point 0 0)) 10)
  270. ; Moving Kat Around
  271. ; =================
  272. ; This part lets you move kat around the maze, by pressing the arrow keys on your keyboard.
  273. ; The big-bang expression at the end of the program is set up to use the function move.
  274. ; move : point text list-of-points → point
  275. ; ----------------------------------------
  276. ; This function helps react to pressing a key on the keyboard.
  277. ; It takes a point, a text representing a key, and a maze.
  278. ; If the text represents one of the arrow keys, and the point in that direction is in the maze,
  279. ; then produce that point, otherwise produce the original point.
  280. #;(check-expect (move (point 0 2) "up"
  281. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  282. (point 0 1))
  283. #;(check-expect (move (point 0 2) "down"
  284. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  285. (point 0 2))
  286. #;(check-expect (move (point 0 2) "left"
  287. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  288. (point 0 2))
  289. #;(check-expect (move (point 0 2) "right"
  290. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  291. (point 1 2))
  292. #;(check-expect (move (point 0 2) "cat"
  293. (list (point 0 1) (point 0 0) (point 1 2) (point 0 2)))
  294. (point 0 2))
  295. (define (move a-point key maze)
  296. (local[(define(maze? a-point maze)(element? a-point maze))]
  297. (if[(and(same? key "right")(maze?(point(+(X a-point) 1)(Y a-point))maze))(point (+ (X a-point) 1)(Y a-point))]
  298. [(and(same? key "left")(maze?(point(-(X a-point) 1)(Y a-point))maze))(point (- (X a-point) 1)(Y a-point))]
  299. [(and(same? key "up")(maze?(point (X a-point) (- (Y a-point)1))maze))(point (+ (X a-point) 1)(Y a-point))]
  300. [(and(same? key "down")(maze?(point(X a-point) (+ (Y a-point)1))maze))(point (+ (X a-point) 1)(Y a-point))]
  301. [else (point (X a-point)(Y a-point))])))
  302. ; click-point: point number number text → point
  303. ; ---------------------------------------------
  304. ; This function helps react to clicking on the grid.
  305. ; It takes a point representing the last place the mouse was clicked, the new click's co-ordinates,
  306. ; and a text describing whether it's in fact a mouse click (versus, for example, a drag action).
  307. ; If it's the right type of action, the point in the grid representing the mouse location is produced,
  308. ; otherwise the previous point is produced.
  309. (define (click-point previous-point x y type)
  310. (if [(same? type "button-down") (point (quotient x (width barrier))
  311. (quotient y (height barrier)))]
  312. [else previous-point]))
  313. ; path : list-of-points point point
  314. ; ---------------------------------
  315. ; For any two points in the generated maze, there is exactly one non-backtracking path connecting
  316. ; the two points. The following describes a function to find such a connecting path. It works more
  317. ; generally on parts of mazes, since that turns out to allow a simpler recursive implementation.
  318. #;(path sub-maze from to)
  319. ; For part of a maze sub-maze, and a point end-point within it, with at most one path from
  320. ; start-point to end-point within sub-maze, produce the list of points connecting start-point
  321. ; to end-point. If there is no such path, produce the empty list.
  322. ; The approach:
  323. ; If start-point and end-point are the same point, the path contains exactly that point.
  324. ; Otherwise, if start-point is in the sub-maze, then the path, if there is one, continues from
  325. ; one of the neighbours and is within the partial maze with start-point removed. If one of
  326. ; those four neighbours produces a (non-empty) path then adding start-point to that path
  327. ; produces the whole path.
  328. ; Otherwise, we know already that there is no path.
  329. ; One of the simple cases:
  330. #;(check-expect (path (list (point 0 0) (point 0 1) (point 1 1) (point 1 2))
  331. (point 0 1)
  332. (point 0 1))
  333. (list (point 0 1)))
  334. ; Another kind of simple case:
  335. #;(check-expect (path (list (point 0 0) (point 0 1) (point 1 1) (point 1 2))
  336. (point 1 0)
  337. (point 0 1))
  338. (list))
  339. ; When there's a path from one of the neighbours:
  340. #;(check-expect (path (list (point 0 0) (point 0 1) (point 1 1) (point 1 2))
  341. (point 0 1)
  342. (point 1 2))
  343. (local [(define (rest-of-path a-neighbour)
  344. (path (list (point 0 0) (point 1 1) (point 1 2))
  345. a-neighbour
  346. (point 1 2)))
  347. (define maybe-rest-of-path
  348. (join (rest-of-path (point 0 0))
  349. (rest-of-path (point 0 2))
  350. (rest-of-path (point -1 1))
  351. (rest-of-path (point 1 1))))]
  352. (adjoin (point 0 1) maybe-rest-of-path)))
  353. ; When there's no path from one of the neighbours:
  354. #;(check-expect (path (list (point 0 0) (point 0 1) #;(point 1 1) (point 1 2))
  355. (point 0 1)
  356. (point 1 2))
  357. (local [(define (rest-of-path a-neighbour)
  358. (path (list (point 0 0) (point 1 2))
  359. a-neighbour
  360. (point 1 2)))
  361. (define maybe-rest-of-path
  362. (join (rest-of-path (point 0 0))
  363. (rest-of-path (point 0 2))
  364. (rest-of-path (point -1 1))
  365. (rest-of-path (point 1 1))))]
  366. (list)))
  367. ; ★ Write Full Design check-expects for the two previous non-simple cases:
  368. ; ★ Fix path:
  369. (define (path sub-maze start-point end-point)
  370. (local[(define(rest-of-path a-neighbour)(path (remove start-point sub-maze)a-neighbour end-point))
  371. (define (maybe-rest-of-path x-point)
  372. (apply join (map rest-of-path (neighbours x-point))))]
  373. (if[(not (and (element? start-point sub-maze) (element? end-point sub-maze))) (list)]
  374. [(same? start-point end-point) (list start-point)]
  375. [(>(length (maybe-rest-of-path start-point)) 0) (adjoin start-point (maybe-rest-of-path start-point))]
  376. [else (list)])))
  377. ; Launch the Animation
  378. ; ====================
  379. (define make-state list)
  380. (define maze first)
  381. (define kat-point second)
  382. (define path-point third)
  383. (define (key state a-key)
  384. (make-state (maze state) (move (kat-point state) a-key (maze state)) (path-point state)))
  385. (define (draw state)
  386. (apply draw-all state))
  387. (define (tick state)
  388. (make-state (try-grow (maze state)) (kat-point state) (path-point state)))
  389. (define (click state x y type)
  390. (make-state (maze state) (kat-point state) (click-point (path-point state) x y type)))
  391. (big-bang (make-state (list (point 0 0))
  392. (point 0 0)
  393. (point 0 0))
  394. [to-draw draw]
  395. [on-tick tick]
  396. [on-mouse click]
  397. [on-key key])
  398.