PasteRack.org
Paste # 55577
2019-02-17 12:12:00

Fork as a new paste.

Paste viewed 586 times.


Embed:

  1. #lang typed/racket
  2.  
  3. (require typed/racket/gui)
  4.  
  5. (struct node ([ NE : Quadtree ]
  6.               [ NW : Quadtree ]
  7.               [ SW : Quadtree ]
  8.               [ SE : Quadtree ]
  9.               [ coords : coordinate]))
  10.  
  11. (define-type Quadtree (U Null node))
  12.  
  13. (struct coordinate ([x : Integer] [y : Integer]))
  14.  
  15. ; A null-checking wrapper for node-coords.
  16. (: get-node-coords (-> Quadtree coordinate))
  17. (define (get-node-coords quadtree)
  18.   (cond [(null? quadtree) (raise "get-node-coords cannot be called on a null Quadtree.")]
  19.         [else (node-coords quadtree)]))
  20.  
  21. ; A null-checking wrapper for node-<Direction>.
  22. (: get-node-child (-> Quadtree (-> node Quadtree) Quadtree))
  23. (define (get-node-child quadtree direction)
  24.   (cond [(null? quadtree) (raise "get-node-child cannot be called on a null Quadtree.")]
  25.         [else (direction quadtree)]))
  26.  
  27. (define-type Direction (U (-> node Quadtree) 'self))
  28.  
  29. (: in-range? (-> Integer Integer Integer Boolean))
  30. (define (in-range? x y z)(and (<= x z) (>= y z)))
  31.  
  32.  
  33. ; The comparison function discerns which quadrant below a node in which to categorize a set of coordinates.
  34. ; The Finkel and Bentley paper uses a convention by which, for those points lying on cardinal directions,
  35. ; Quadrants I and III are "open", while II and IV are "closed." It seems to me that this will inevitably
  36. ; result in an unbalanced tree, so my implementation gives each quadrant a cardinal direction.
  37. (: coordinate-compare (-> coordinate coordinate Direction))
  38. (define (coordinate-compare a b)
  39.   (let ([a-x : Integer (coordinate-x a)]
  40.         [a-y : Integer (coordinate-y a)]
  41.         [b-x : Integer (coordinate-x b)]
  42.         [b-y : Integer (coordinate-y b)])
  43.     (cond [(and (<= a-x b-x) (<  a-y b-y)) node-NE]
  44.           [(and (>  a-x b-x) (<= a-y b-y)) node-NW]
  45.           [(and (>= a-x b-x) (>  a-y b-y)) node-SW]
  46.           [(and (<  a-x b-x) (>= a-y b-y)) node-SE]
  47.           [(and (= a-x b-x) (= a-y b-y)) 'self]
  48.           ; If you somehow raise this error, I did not account for something.
  49.           ; I am confident the other options are exhaustive.
  50.           [else (raise "coordinate-compare should never return this!")])))
  51.  
  52. (: quadtree-member? (-> Quadtree coordinate Boolean))
  53. (define (quadtree-member? quadtree coords)
  54.   (cond [(null? quadtree) #f]
  55.         [else (let ([quadrant (coordinate-compare (node-coords quadtree) coords)])
  56.                 (cond [(equal? quadrant 'self) #t]
  57.                       [else (quadtree-member? (get-node-child quadtree quadrant) coords)]))]))
  58.  
  59. (: neighbor? (-> coordinate coordinate Boolean))
  60. (define (neighbor? c1 c2)
  61.   (let ([x1 : Integer (coordinate-x c1)]
  62.         [y1 : Integer (coordinate-y c1)]
  63.         [x2 : Integer (coordinate-x c2)]
  64.         [y2 : Integer (coordinate-y c2)])
  65.     (and
  66.      (and (in-range? (- x1 1) (+ x1 1) x2)
  67.           (in-range? (- y1 1) (+ y1 1) y2))
  68.      (nand (equal? x1 x2) (equal? y1 y2)))))
  69.  
  70. (: overlapping-quadrants (-> coordinate coordinate (Listof (-> node Quadtree))))
  71. (define (overlapping-quadrants c1 c2)
  72.   (let* ([x1 : Integer (coordinate-x c1)]
  73.          [y1 : Integer (coordinate-y c1)]
  74.          [x2 : Integer (coordinate-x c2)]
  75.          [y2 : Integer (coordinate-y c2)]
  76.          [NE (if (and (>= (+ x2 1) x1) (>  (+ y2 1) y1)) node-NE null)]
  77.          [NW (if (and (<  (- x2 1) x1) (>= (+ y2 1) y1)) node-NW null)]
  78.          [SW (if (and (<= (- x2 1) x1) (<  (- y2 1) y1)) node-SW null)]
  79.          [SE (if (and (>  (+ x2 1) x1) (<= (- y2 1) y1)) node-SE null)])
  80.     ((inst filter (U Null (-> node Quadtree)) (-> node Quadtree)) procedure? (list NE NW SW SE))))
  81.  
  82. ; For now I'll just address the case I'll be using in Game of Life, and look for direct neighbors.
  83. (: quadtree-neighbors (-> Quadtree coordinate (Listof coordinate)))
  84. (define (quadtree-neighbors quadtree coords)
  85.   (cond [(null? quadtree) null]
  86.         [else
  87.          (let ([quadrants (overlapping-quadrants (node-coords quadtree) coords)]
  88.                [am-neighbor : (Listof coordinate) (if (neighbor? (node-coords quadtree) coords) (list (node-coords quadtree)) null)])
  89.            ((inst append coordinate)
  90.             ((inst append-map coordinate (-> node Quadtree))
  91.              (λ ([direction : (-> node Quadtree)])
  92.                (quadtree-neighbors (direction quadtree) coords)) quadrants) am-neighbor))]))
  93.  
  94. (: descendant-coordinates (-> Quadtree (Listof coordinate)))
  95. (define (descendant-coordinates quadtree)
  96.   (cond [(null? quadtree) null]
  97.         [else (let ([coords (list (get-node-coords quadtree))])
  98.                 (append coords
  99.                         (append-map
  100.                          (λ ([direction : (-> node Quadtree)])
  101.                            (descendant-coordinates (direction quadtree)))
  102.                          (list node-NE node-NW node-SW node-SE))))]))
  103.  
  104. (: quadtree-fold (-> Quadtree (Listof coordinate) Quadtree))
  105. (define (quadtree-fold quadtree coord-list)
  106.   (cond [(null? coord-list) quadtree]
  107.         [else (quadtree-fold (quadtree-insert quadtree (first coord-list)) (rest coord-list))]))
  108.  
  109. (: quadtree-insert (-> Quadtree coordinate node))
  110. (define (quadtree-insert quadtree coords)
  111.   (cond [(null? quadtree) (node null null null null coords)]
  112.         [(equal? 'self (coordinate-compare (get-node-coords quadtree) coords)) quadtree]
  113.         [else
  114.          (let* ([quadrant (coordinate-compare (get-node-coords quadtree) coords)]
  115.                 [r (λ ([direction : (-> node Quadtree)])
  116.                      (if (equal? quadrant direction)
  117.                          (quadtree-insert (get-node-child quadtree direction) coords)
  118.                          (get-node-child quadtree direction)))])
  119.            (node
  120.             (r node-NE)
  121.             (r node-NW)
  122.             (r node-SW)
  123.             (r node-SE)
  124.             (get-node-coords quadtree)))]))
  125.  
  126.  
  127. ; Capes are constructs that let me hand up orphans as needed from the recursive part of the deletion mechanism.
  128. (struct cape ([quadtree : Quadtree]
  129.               [orphans : (Listof coordinate)]))
  130.  
  131. ; constants. never enough constants
  132. (define cell-scale 5)
  133. (define canvas-size 500)
  134.  
  135. (define living-cells
  136.   (quadtree-fold null (map (lambda ([x : Integer]) (coordinate x  50)) (range 2 90))))
  137.  
  138. (define moment-zero (list living-cells))
  139.  
  140. ; Given a coordinate and a Quadtree, determine if it survives.
  141. (: determine-cell-state (-> Quadtree coordinate Boolean))
  142. (define (determine-cell-state population cell)
  143.   (let ([living-neighbors (length (quadtree-neighbors population cell))]
  144.         [orig-state (quadtree-member? population cell)])
  145.     (if (< living-neighbors 2) #f                          ; if the cell has fewer than 2 neighbors, it dies.
  146.         (if (equal? living-neighbors 3) #t                 ; if the cell has exactly 3 neighbors, it comes to life.
  147.             (if (> living-neighbors 3) #f orig-state)))))  ; if the cell has more than 3 neighbors, it dies.
  148.  
  149. ; Advance time by one frame.
  150. (: tick (-> Quadtree Quadtree))
  151. (define (tick living-cells)
  152.   (let ([cell-list (descendant-coordinates living-cells)])
  153.     (quadtree-fold null
  154.                    (append-map (λ ([cell : coordinate])
  155.                                  (filter (curry determine-cell-state living-cells)
  156.                                          (neighbor-coords cell)))
  157.                                cell-list))))
  158.  
  159. ; I'd like to find a cleaner way to do this.
  160. (: neighbor-coords (-> coordinate (Listof coordinate)))
  161. (define (neighbor-coords coordinate)
  162.   (let ([x : Integer (coordinate-x coordinate)]
  163.         [y : Integer (coordinate-y coordinate)])
  164.     (append-map (curry coordmap x) (range (- y 1) (+ y 2)))))
  165.  
  166. ; give all the ys for an x
  167. (: coordmap (-> Integer Integer (Listof coordinate)))
  168. (define (coordmap x y)
  169.   (map (λ ([z : Integer]) (coordinate z y))
  170.        (range (- x 1) (+ x 2))))
  171.  
  172. (define frame (new frame% [label "QTREEGOL"] [width canvas-size] [height canvas-size]))
  173. (define canvas (new canvas% [parent frame]
  174.                     [paint-callback
  175.                      (λ (c dc)
  176.                        (send dc set-pen "black" 1 'transparent)
  177.                        (send dc set-brush "black" 'solid)
  178.                        (send dc draw-rectangle 0 0 canvas-size canvas-size))]))
  179. (send frame show #t)
  180.  
  181.  
  182. ; draww
  183. (: recursidraw (-> (Instance DC<%>) (Listof coordinate) (Instance DC<%>)))
  184. (define (recursidraw dc cells)
  185.   (cond
  186.     [(empty? cells) dc]
  187.     [else   (let ([cell : coordinate (first cells)])
  188.               (send dc set-brush "green" 'solid)
  189.               (send dc set-pen "green" 1 'solid)
  190.               (send dc draw-rectangle
  191.                     (* cell-scale (coordinate-y cell))
  192.                     (* cell-scale (coordinate-x cell)) cell-scale cell-scale)
  193.               (recursidraw dc (rest cells)))]))
  194.  
  195.  
  196. (: evolve (-> Quadtree Void))
  197. (define (evolve quadtree)
  198.   (send canvas on-paint)
  199.   (recursidraw (send canvas get-dc) (descendant-coordinates quadtree))
  200.   (sleep/yield 0.03)
  201.   (evolve (tick quadtree)))
  202.  
  203. (evolve living-cells)

=>