view 2022/15/15.jl @ 71:936b17a8e4ff

Day 15 Part 1
author Lewin Bormann <lbo@spheniscida.de>
date Thu, 28 Dec 2023 19:10:41 +0100
parents 05ddc45b4210
children
line wrap: on
line source


using ParserCombinator

const Line = E"Sensor at x=" + PInt64() + E", y=" + PInt64() + E": closest beacon is at x=" + PInt64() + E", y=" + PInt64() + Eos();

struct Point
    x::Int
    y::Int
end

function distance(p::Point, q::Point)::Int
    abs(p.x-q.x) + abs(p.y-q.y)
end

struct Beacon
    sensor::Point
    beacon::Point
    dist::Int
end

function parse_line(s::String)::Beacon
    es = parse_one(s, Line);
    p, q = Point(es[1]::Int, es[2]::Int), Point(es[3]::Int, es[4]::Int)
    Beacon(p, q, distance(p, q))
end

function parse_lines(f::String)::Vector{Beacon}
    v = Vector{Beacon}();
    open(f; read=true) do fh
        for l in eachline(fh)
            push!(v, parse_line(l));
        end
    end
    v
end

function point_is_within_closest(p::Point, b::Beacon)::Bool
    distance(p, b.sensor) <= b.dist && p != b.beacon
end

function point_is_beacon(p::Point, bs::Vector{Beacon})::Bool
    any(b -> b.beacon == p, bs)
end

function point_is_covered(p::Point, bs::Vector{Beacon}; ignorebeacon=false)::Bool
    any(b -> point_is_within_closest(p, b), bs)
end

function n_covered_points(bs::Vector{Beacon}, y::Int)::Int
    minx = minimum(min(b.sensor.x, b.beacon.x)-b.dist for b in bs);
    maxx = maximum(max(b.sensor.x, b.beacon.x)+b.dist for b in bs);

    count = sum(point_is_covered(Point(x, y), bs) for x = minx:maxx);
    count
end

function find_distress_beacon(bs::Vector{Beacon}, minc=0, maxc=20)::Set{Point}
    # Check borders of sensor's coverage area - as there is only one point to check,
    # it must be directly outside a sensor's coverage area.
    v = Set{Point}();
    isvalid(p) = p.x >= minc && p.x <= maxc && p.y >= minc && p.y <= maxc;
    for b in bs
        @show b
        for x = b.sensor.x-b.dist:b.sensor.x+b.dist
            for dir = [-1, 1]
                y = dir*b.sensor.y + abs(b.dist - abs(b.sensor.x-x));
                p = Point(x+sign(x-b.sensor.x), y);
                if isvalid(p) && !point_is_covered(p, bs) && !point_is_beacon(p, bs)
                    push!(v, p);
                    continue;
                end
                if x == b.sensor.x
                    p = Point(x, y+dir);
                    if isvalid(p) && !point_is_covered(p, bs) && !point_is_beacon(p, bs)
                        push!(v, p);
                        continue;
                    end
                end
            end
        end
    end
    v
end

function tuning_frequency(p::Point)::Int
    p.x*4000000+p.y
end

bs = parse_lines("15/input.txt");
println(n_covered_points(bs, 2000000));
db = find_distress_beacon(bs, 0, 4000000);
@show (db, map(tuning_frequency, collect(db)))

bs = parse_lines("15/test_input.txt");
println(n_covered_points(bs, 10));
db = find_distress_beacon(bs);
@show (db, map(tuning_frequency, collect(db)))