Skip to content

Commit

Permalink
Day 10: Solve part 1 and 2
Browse files Browse the repository at this point in the history
  • Loading branch information
nihas101 committed Dec 10, 2023
1 parent a01e2b1 commit fe5b171
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 17 deletions.
170 changes: 166 additions & 4 deletions src/advent_of_code_2023/day10.clj
Original file line number Diff line number Diff line change
@@ -1,9 +1,171 @@
(ns advent-of-code-2023.day10
(:require
[advent-of-code-2023.utils.string :as u]))
[clojure.set :as set]
[advent-of-code-2023.utils.graph :as g]))

(defn parse-input [input])
(defn parse-input [input]
(let [{:keys [positions] :as m} (g/parse-positional-map input identity)]
(assoc m :start
(ffirst (filterv (comp #{\S} second) positions)))))

(defn day10-1 [parsed-input])
(defonce ^:private valid-state?
#{[\| :north]
[\| :south]
[\- :east]
[\- :west]
[\L :west]
[\L :south]
[\J :east]
[\J :south]
[\7 :north]
[\7 :east]
[\F :west]
[\F :north]
[\S :north]
[\S :east]
[\S :south]
[\S :west]})

(defn day10-2 [parsed-input])
(defn- next-position [[^long x ^long y] direction]
(condp = direction
:north [x (dec y)]
:east [(inc x) y]
:south [x (inc y)]
:west [(dec x) y]))

(defn- next-direction [pipe direction]
(condp = [pipe direction]
[\| :north] :north
[\| :south] :south
[\- :east] :east
[\- :west] :west
[\L :west] :north
[\L :south] :east
[\J :east] :north
[\J :south] :west
[\7 :north] :west
[\7 :east] :south
[\F :west] :south
[\F :north] :east
[\S :north] :north
[\S :east] :east
[\S :south] :south
[\S :west] :west))

(defn- next-pipe-pos-fn [{:keys [positions]}]
(fn next-pipe-pos [[[^long x ^long y] _ direction] visited]
(let [next-pos (next-position [x y] direction)
next-pipe (positions next-pos)]
(cond
(visited next-pos) nil
(valid-state? [next-pipe direction]) [next-pos next-pipe (next-direction next-pipe direction)]
:else nil))))

(defn try-loop [start-state continue? next-pipe-pos]
(loop [pipe-pos start-state
visited #{}
path []]
(cond
(continue? pipe-pos) (when-let [np (next-pipe-pos pipe-pos visited)]
(recur np
(conj visited (first np))
(conj path pipe-pos)))
:else (conj path pipe-pos))))

(defn- continue?-fn [direction]
(fn continue? [[n v d]]
(or (and n (not= v \S))
(= direction d))))

(defn- find-loop [{:keys [start] :as m}]
(first
(transduce
(comp
(map
(fn [direction]
(try-loop [start \S direction]
(continue?-fn direction)
(next-pipe-pos-fn m))))
(remove nil?))
conj []
[:north :east :south :west])))


(defn day10-1 [m]
(-> m
find-loop
count
dec ; Subtract the start point that appears twice
(quot ,,, 2)))

(defn- enclosed-candidates [loop]
(let [loop-pos (set (mapv first loop))
x-pos (mapv first loop-pos)
y-pos (mapv second loop-pos)
min-x (reduce min x-pos)
min-y (reduce min y-pos)
max-x (reduce max x-pos)
max-y (reduce max y-pos)]
(for [x (range min-x (inc ^long max-x))
y (range min-y (inc ^long max-y))
:when ((complement loop-pos) [x y])]
[x y])))

(defonce ^:private valid-neighbouring-pipes
{[\| :north] #{\| \7 \F}
[\| :south] #{\| \L \J}
[\- :east] #{\- \J \7}
[\- :west] #{\- \L \F}
[\L :west] #{\| \7 \F}
[\L :south] #{\- \J \7}
[\J :east] #{\| \7 \F}
[\J :south] #{\- \L \F}
[\7 :north] #{\- \L \F}
[\7 :east] #{\| \L \J}
[\F :west] #{\| \L \J}
[\F :north] #{\- \J \7}
[\S :north] #{\| \7 \F}
[\S :east] #{\- \J \7}
[\S :south] #{\| \L \J}
[\S :west] #{\- \L \F}})

(defn- determine-separators [loop]
(let [sec (rest (second loop))
sec-last (rest (second (reverse loop)))
s (->> [sec sec-last]
(mapv valid-neighbouring-pipes)
(apply set/intersection)
vec
first)]
(if (#{\L \J \|} s)
#{\L \S \J \|}
#{\L \J \|})))

(defn- loop-map [loop]
(reduce (fn [m [pos v _]] (assoc m pos v))
{}
loop))

(defn- enclosed?-fn [loop]
(let [sep (determine-separators loop)
loop-m (loop-map loop)]
(fn enclosed? [[x y]]
;; Rasterization: We count how many times we cross a pipe going west
;; from a possible enclosed point
;; If it is an odd amount -> enclosed
;; This can probably be done a lot smarter to avoid a lot of duplicate work for a given row
(->> x
range
(transduce
(comp
(map (fn [xx] [xx y]))
(map loop-m)
(filter sep))
conj [])
count
odd?))))

(defn day10-2 [pipe-map]
(let [loop (find-loop pipe-map)
enclosed-cand (enclosed-candidates loop)]
(count (filterv (enclosed?-fn loop) enclosed-cand))))
6 changes: 2 additions & 4 deletions src/advent_of_code_2023/utils/graph.clj
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,9 @@
(path-fn path-val c))
:else (path-fn path-val c)))))

(defn bfs [start-state end-val neighbours visited-state path-fn]
(defn bfs [start-state continue? neighbours visited-state path-fn]
(pruning-bfs start-state neighbours visited-state
(constantly 0)
(fn [[n v]] (and n (not= v end-val)))
path-fn))
(constantly 0) continue? path-fn))

;;; Positional graphs

Expand Down
57 changes: 48 additions & 9 deletions test/advent_of_code_2023/day10_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,65 @@
[advent-of-code-2023.day10 :refer :all]
[advent-of-code-2023.test-utils :as tu]))

(defonce ^:private example-input (parse-input ""))
(defonce ^:private example-input (parse-input ".....
.S-7.
.|.|.
.L-J.
....."))

(defonce ^:private example-input-2 (parse-input "..F7.
.FJ|.
SJ.L7
|F--J
LJ..."))

(def ^:private input (parse-input (tu/slurp-input "resources/day10.txt")))

(deftest day10-1-example-test
(testing "day10-1 example"
(is (= nil
(day10-1 example-input)))))
(is (= 4 (day10-1 example-input)))))

(deftest day10-1-example-2-test
(testing "day10-1 example 2"
(is (= 8 (day10-1 example-input-2)))))

(deftest day10-1-test
(testing "day10-1"
(is (= nil
(day10-1 input)))))
(is (= 6968 (day10-1 input)))))

(deftest day10-2-example-test
(testing "day10-2 example"
(is (= nil
(day10-2 example-input)))))
(is (= 1 (day10-2 example-input)))))

(defonce ^:private example-input-3 (parse-input "...........
.S-------7.
.|F-----7|.
.||.....||.
.||.....||.
.|L-7.F-J|.
.|..|.|..|.
.L--J.L--J.
..........."))

(deftest day10-2-example-3-test
(testing "day10-2 example 3"
(is (= 4 (day10-2 example-input-3)))))

(defonce ^:private example-input-4 (parse-input "FF7FSF7F7F7F7F7F---7
L|LJ||||||||||||F--J
FL-7LJLJ||||||LJL-77
F--JF--7||LJLJ7F7FJ-
L---JF-JLJ.||-FJLJJ7
|F|F-JF---7F7-L7L|7|
|FFJF7L7F-JF7|JL---7
7-L-JL7||F7|L7F-7F7|
L.L7LFJ|||||FJL7||LJ
L7JLJL-JLJLJL--JLJ.L"))

(deftest day10-2-example-4-test
(testing "day10-2 example 4"
(is (= 10 (day10-2 example-input-4)))))

(deftest day10-2-test
(testing "day10-2"
(is (= nil
(day10-2 input)))))
(is (= 413 (day10-2 input)))))

0 comments on commit fe5b171

Please sign in to comment.