commit 2d2c3b50224a67ec8b52aa871246c7672fd7c088
parent 9411152faa02ad1b7a725723db3403a1cfc5cba7
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Wed, 14 Dec 2022 21:51:26 -0800
Solution to day 15, part 1.
Diffstat:
| A | src/day_15.jl | | | 71 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
1 file changed, 71 insertions(+), 0 deletions(-)
diff --git a/src/day_15.jl b/src/day_15.jl
@@ -0,0 +1,71 @@
+#!/usr/bin/env julia
+# https://adventofcode.com/2022/day/15
+using AdventOfCode
+
+example = readlines(IOBuffer("""
+    Sensor at x=2, y=18: closest beacon is at x=-2, y=15
+    Sensor at x=9, y=16: closest beacon is at x=10, y=16
+    Sensor at x=13, y=2: closest beacon is at x=15, y=3
+    Sensor at x=12, y=14: closest beacon is at x=10, y=16
+    Sensor at x=10, y=20: closest beacon is at x=10, y=16
+    Sensor at x=14, y=17: closest beacon is at x=10, y=16
+    Sensor at x=8, y=7: closest beacon is at x=2, y=10
+    Sensor at x=2, y=0: closest beacon is at x=2, y=10
+    Sensor at x=0, y=11: closest beacon is at x=2, y=10
+    Sensor at x=20, y=14: closest beacon is at x=25, y=17
+    Sensor at x=17, y=20: closest beacon is at x=21, y=22
+    Sensor at x=16, y=7: closest beacon is at x=15, y=3
+    Sensor at x=14, y=3: closest beacon is at x=15, y=3
+    Sensor at x=20, y=1: closest beacon is at x=15, y=3"""))
+input = readlines("data/day_15.txt")
+
+function parse_input(input)
+    pattern = r"Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)"
+    matches = map(x->match(pattern, x), input)
+    @assert !any(map(isnothing, matches))
+    parse.(
+        Int,
+        permutedims(hcat(map(Base.Fix2(getfield, :captures), matches)...))
+    )
+end
+
+function beacon_distance(sensors::AbstractMatrix{Int})
+    abs.(sensors[:,1]-sensors[:,3]) + abs.(sensors[:,2]-sensors[:,4])
+end
+
+"""
+Scan a row and count spaces where a beacon cannot be.
+"""
+function scan_row(sensors::AbstractMatrix{Int}, row::Int)
+    # This tells us how far away each beacon is from the sensor that detects
+    # it. Any space closer than this to the sensor necessarily has no beacon.
+    # One effect of this is that we don't need to worry about scanners that are
+    # more than this distance from the row.
+    maxdist = beacon_distance(sensors)
+    sensoridx = findall(abs.(sensors[:,2] .- row) .< maxdist)
+
+    # Each sensor contributes a range of values that cannot be occupied on a
+    # given row. These ranges can overlap but we'll deal with that in the next
+    # step. For now, track the ranges.
+    beaconfree = Vector{UnitRange{Int}}()
+    for s_i = sensoridx
+        offset = maxdist[s_i]-abs(sensors[s_i,2] - row)
+        push!(beaconfree,
+              (sensors[s_i,1]-offset):(sensors[s_i,1]+offset))
+    end
+    
+    # Deal with the overlap and actual beacons on the row
+    setdiff(reduce(union, beaconfree),
+            unique(sensors[sensoridx[sensors[sensoridx, 4] .== row], 3]))
+end
+
+function part_1(input, row)
+    length(scan_row(parse_input(input), row))
+end
+@assert part_1(example, 10) == 26
+@info part_1(input, 2000000)
+
+function part_2(input)
+    nothing
+end
+@info part_2(input)