advent_of_code_2022

My (attempted) solutions to the 2022 Advent of Code
git clone https://git.eamoncaddigan.net/advent_of_code_2022.git
Log | Files | Refs | README

commit 8e338fb74904687ed443c66495e9cbafcad1070f
parent 183e8d1cf7dd876123f6fce26b231da0eef3f827
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Mon, 19 Dec 2022 20:12:50 -0800

Solution to day 15, part 2.

I overhauled part 1 to run much faster, but part 2 is still pretty slow
(it brute-forces a solution). But it works, so I'm calling this done.

Diffstat:
Msrc/day_15.jl | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 66 insertions(+), 19 deletions(-)

diff --git a/src/day_15.jl b/src/day_15.jl @@ -1,6 +1,7 @@ #!/usr/bin/env julia # https://adventofcode.com/2022/day/15 using AdventOfCode +using DataStructures example = readlines(IOBuffer(""" Sensor at x=2, y=18: closest beacon is at x=-2, y=15 @@ -34,38 +35,84 @@ function beacon_distance(sensors::AbstractMatrix{Int}) end """ -Scan a row and count spaces where a beacon cannot be. +Scan a row and return a list of ranges that can't contain unknown beacons. """ -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) +function scan_row(sensors::AbstractMatrix{Int}, row::Int, + maxdist::Vector{Int} = beacon_distance(sensors)) + # We only care about sensors that are contributing information for the + # current row: find those. 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}}() + # step. + beaconfree_tree = RBTree{Tuple{Int,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)) + push!(beaconfree_tree, + (sensors[s_i,1]-offset, sensors[s_i,1]+offset)) + end + + # Now we build a list of beacon-free locations. We use a linked list here + # for quick pushes/pops, and we take advantage of the balanced tree we used + # on the first pass. + beaconfree = MutableLinkedList{Tuple{Int,Int}}(beaconfree_tree[1]) + for i = 2:length(beaconfree_tree) + newzone = beaconfree_tree[i] + currentzone = pop!(beaconfree) + if newzone[1] <= currentzone[2] + push!(beaconfree, + (currentzone[1], max(currentzone[2], newzone[2]))) + else + append!(beaconfree, currentzone, newzone) + end + end + + beaconfree +end + +""" +Count the number of locations we know to not contain a beacon +""" +function count_beaconfree_locations(sensors::AbstractMatrix{Int}, row::Int) + beaconfree = scan_row(sensors, row) + sum(map(x->(x[2] - x[1]), beaconfree)) +end + +""" +Find the first row that has a spot for a beacon. + +Returns a tuple containing the beacon's row and column number, searching only +in the supplied ranges. +""" +function scan_all_rows(sensors::AbstractMatrix{Int}, search_max::Int) + maxdist = beacon_distance(sensors) + for row=0:search_max + beaconfree = scan_row(sensors, row, maxdist) + if length(beaconfree) == 1 + if beaconfree[1][1] > 0 + return((0, row)) + elseif beaconfree[1][2] < search_max + return((search_max, row)) + end + elseif length(beaconfree) == 2 + return((beaconfree[1][2]+1, row)) + else + error("Unexpected beacon-free range on row $row") + end end - - # Deal with the overlap and actual beacons on the row - setdiff(reduce(union, beaconfree), - unique(sensors[sensoridx[sensors[sensoridx, 4] .== row], 3])) + return nothing end function part_1(input, row) - length(scan_row(parse_input(input), row)) + count_beaconfree_locations(parse_input(input), row) end @assert part_1(example, 10) == 26 @info part_1(input, 2000000) -function part_2(input) - nothing +function part_2(input, search_max) + x, y = scan_all_rows(parse_input(input), search_max) + 4000000 * x + y end -@info part_2(input) +@assert part_2(example, 20) == 56000011 +@info part_2(input, 4000000)