|
| 1 | +(ns dragon-curve) |
| 2 | + |
| 3 | +(def start-turn :right) |
| 4 | +(def segment-length 2) ; should be even |
| 5 | +(def start-points [[0 0] [segment-length 0]]) |
| 6 | +(def start-direction :east) |
| 7 | + |
| 8 | + |
| 9 | +(defn swap-turn |
| 10 | + [turn] |
| 11 | + (case turn |
| 12 | + :left :right |
| 13 | + :right :left)) |
| 14 | + |
| 15 | +(defn unfold-paper-once |
| 16 | + [paper-turns] |
| 17 | + (-> paper-turns |
| 18 | + (into [start-turn]) |
| 19 | + (into (reverse (map swap-turn paper-turns))))) |
| 20 | + |
| 21 | +(defn unfold-paper-times |
| 22 | + [n] |
| 23 | + (reduce (fn [result _] |
| 24 | + (unfold-paper-once result)) |
| 25 | + [] (range n))) |
| 26 | + |
| 27 | +(def paper-turn->direction |
| 28 | + {:left {:north :west |
| 29 | + :west :south |
| 30 | + :south :east |
| 31 | + :east :north} |
| 32 | + :right {:north :east |
| 33 | + :east :south |
| 34 | + :south :west |
| 35 | + :west :north}}) |
| 36 | + |
| 37 | +(defn generate-next-point |
| 38 | + [point direction] |
| 39 | + (let [[x y] point] |
| 40 | + (case direction |
| 41 | + :north [x (+ y segment-length)] |
| 42 | + :east [(+ x segment-length) y] |
| 43 | + :south [x (- y segment-length)] |
| 44 | + :west [(- x segment-length) y]))) |
| 45 | + |
| 46 | +(defn generate-points |
| 47 | + [paper-turns] |
| 48 | + (loop [result start-points |
| 49 | + point (second start-points) |
| 50 | + direction start-direction |
| 51 | + turns paper-turns] |
| 52 | + (if (seq turns) |
| 53 | + (let [new-direction (get (paper-turn->direction (first turns)) direction) |
| 54 | + new-point (generate-next-point point new-direction)] |
| 55 | + (recur (conj result new-point) new-point new-direction (rest turns))) |
| 56 | + result))) |
| 57 | + |
| 58 | +(defn find-midpoint |
| 59 | + [segment] |
| 60 | + (let [[[x1 y1] [x2 y2]] segment] |
| 61 | + [(/ (+ x1 x2) 2) (/ (+ y1 y2) 2)])) |
| 62 | + |
| 63 | +(defn generate-midpoints |
| 64 | + [points] |
| 65 | + (reduce (fn [result segment] |
| 66 | + (conj result (find-midpoint segment))) |
| 67 | + [] (partition 2 1 points))) |
| 68 | + |
| 69 | +(defn find-bounding-box-corners |
| 70 | + [points] |
| 71 | + (let [xs (map first points) |
| 72 | + ys (map second points)] |
| 73 | + [[(- (apply min xs) segment-length) (- (apply min ys) segment-length)] |
| 74 | + [(+ (apply max xs) segment-length) (+ (apply max ys) segment-length)]])) |
| 75 | + |
| 76 | +(defn range-with-offset |
| 77 | + [start end] |
| 78 | + (let [offset (/ segment-length 2)] |
| 79 | + (range (+ start offset) (inc (- end offset)) segment-length))) |
| 80 | + |
| 81 | +(defn generate-grid |
| 82 | + [bounding-box-corners] |
| 83 | + (let [[[x-left y-bottom] [x-right y-top]] bounding-box-corners] |
| 84 | + (for [x (range-with-offset x-left x-right) |
| 85 | + y (range-with-offset y-bottom y-top)] |
| 86 | + [x y]))) |
| 87 | + |
| 88 | +(defn find-neighbors |
| 89 | + [point] |
| 90 | + (map #(generate-next-point point %) [:north :east :south :west])) |
| 91 | + |
| 92 | +(defn can-fill? |
| 93 | + [point obstacles] |
| 94 | + (let [neighbors (find-neighbors point) |
| 95 | + midpoints (map #(find-midpoint [% point]) neighbors)] |
| 96 | + (not (every? obstacles midpoints)))) |
| 97 | + |
| 98 | +(defn fill-grid |
| 99 | + [grid obstacles] |
| 100 | + (filter #(can-fill? % obstacles) grid)) |
| 101 | + |
| 102 | +(defn count-squares |
| 103 | + [n] |
| 104 | + (let [curve-points (generate-points (unfold-paper-times n)) |
| 105 | + curve-midpoints (generate-midpoints curve-points) |
| 106 | + bounding-box-corners (find-bounding-box-corners curve-points) |
| 107 | + grid (generate-grid bounding-box-corners) |
| 108 | + obstacles (set curve-midpoints)] |
| 109 | + (- (count grid) (count (fill-grid grid obstacles))))) |
0 commit comments