view 2022/14/14.jl @ 78:ade1919a5409 default tip

Day 17: streamline PQ
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 02 Jan 2024 18:42:41 +0100
parents 05ddc45b4210
children
line wrap: on
line source


mutable struct PS
    s::String
    pos::Int
end

function initialize(s::String)::PS
    PS(s, 1)
end

function parse_coord(ps::PS)::Tuple{Int,Int}
    e = findfirst((==)(','), ps.s[ps.pos:end])
    x = parse(Int, ps.s[ps.pos:ps.pos+e-2]);
    ps.pos = ps.pos + e;
    e = something(findfirst((==)(' '), ps.s[ps.pos:end]), length(ps.s)-ps.pos+2);
    y = parse(Int, ps.s[ps.pos:ps.pos+e-2]);
    ps.pos = ps.pos + e - 1;
    (x, y)
end

function parse_sep(ps::PS)::Bool
    if ps.pos >= length(ps.s)
        false
    else
        @assert ps.s[ps.pos:ps.pos+3] == " -> "
        ps.pos += 4;
        true
    end
end

function parse_coords(ps::PS)::Vector{Tuple{Int,Int}}
    poss = Vector{Tuple{Int,Int}}();
    while true
        push!(poss, parse_coord(ps));
        if !parse_sep(ps)
            break
        end
    end
    poss
end

function parse_all_coords(fh::IOStream)::Vector{Vector{Tuple{Int,Int}}}
    vs = Vector{Vector{Tuple{Int,Int}}}();
    for l in eachline(fh)
        push!(vs, parse_coords(initialize(l)));
    end
    vs
end

const EMPTY = 0;
const ROCK = 1;
const SAND = 2;

function run_matrix_2(v::Vector{Vector{Tuple{Int,Int}}})
    SAND_POINT = (500,0)
    minx = min(SAND_POINT[1], minimum(minimum(t[1] for t in vv) for vv in v));
    maxx = max(SAND_POINT[1], maximum(maximum(t[1] for t in vv) for vv in v));
    miny = min(SAND_POINT[2], minimum(minimum(t[2] for t in vv) for vv in v));
    maxy = max(SAND_POINT[2], maximum(maximum(t[2] for t in vv) for vv in v));

    maxx += div((maxy-miny)+1, 1);
    minx -= div((maxy-miny)+1, 1);

    m = zeros(Int8, maxx-minx+1, maxy-miny+1+2);
    m[begin:end, end] .= ROCK;

    for line in v
        last = line[1];
        for p in @view line[2:end]
            a, b = CartesianIndex(last .- (minx-1, miny-1)), CartesianIndex(p .- (minx-1, miny-1));
            @assert abs(last[1]-p[1]) == 0 || abs(last[2]-p[2]) == 0
            if a > b
                a, b = b, a;
            end
            m[a:b] .= ROCK;
            last = p;
        end
    end


    show_cave(m);

    # part 2
    println(" === PART 2 === ");
    pos0 = SAND_POINT .- (minx-1, miny-1);
    down, left, right = (0,1), (-1,1), (1,1);
    X, Y = size(m);
    isvalid(pos) = 0 < pos[1] && pos[1] <= X && 0 < pos[2] && pos[2] <= Y

    count = 0;
    pos = pos0;
    while true
        if m[CartesianIndex(pos .+ down)] == EMPTY
            pos = pos .+ down;
            continue;
        elseif m[CartesianIndex(pos .+ left)] == EMPTY
            pos = pos .+ left;
            continue
        elseif m[CartesianIndex(pos .+ right)] == EMPTY
            pos = pos .+ right;
            continue;
        else
            count += 1;
            if pos == pos0
                break;
            end
            m[CartesianIndex(pos)] = SAND;
            println(" ==== $count ==== ");
            pos = pos0;
            continue;
        end
    end
    show_cave(m);
    @show count;
end

function run_matrix(v::Vector{Vector{Tuple{Int,Int}}})
    # Not very efficient...
    SAND_POINT = (500,0)
    minx = min(SAND_POINT[1], minimum(minimum(t[1] for t in vv) for vv in v));
    maxx = max(SAND_POINT[1], maximum(maximum(t[1] for t in vv) for vv in v));
    miny = min(SAND_POINT[2], minimum(minimum(t[2] for t in vv) for vv in v));
    maxy = max(SAND_POINT[2], maximum(maximum(t[2] for t in vv) for vv in v));

    m = zeros(Int8, maxx-minx+1, maxy-miny+1);

    for line in v
        last = line[1];
        for p in @view line[2:end]
            a, b = CartesianIndex(last .- (minx-1, miny-1)), CartesianIndex(p .- (minx-1, miny-1));
            @assert abs(last[1]-p[1]) == 0 || abs(last[2]-p[2]) == 0
            if a > b
                a, b = b, a;
            end
            m[a:b] .= ROCK;
            last = p;
        end
    end

    M0 = copy(m);
    down, left, right = (0,1), (-1,1), (1,1);
    X, Y = size(m);
    isvalid(pos) = 0 < pos[1] && pos[1] <= X && 0 < pos[2] && pos[2] <= Y

    count = 0;
    pos0 = SAND_POINT .- (minx-1, miny-1);
    pos = pos0;
    while true
        if !isvalid(pos .+ down)
            break;
        elseif m[CartesianIndex(pos .+ down)] == EMPTY
            pos = pos .+ down;
            continue;
        elseif !isvalid(pos .+ left)
            break;
        elseif m[CartesianIndex(pos .+ left)] == EMPTY
            pos = pos .+ left;
            continue
        elseif !isvalid(pos .+ right)
            break;
        elseif m[CartesianIndex(pos .+ right)] == EMPTY
            pos = pos .+ right;
            continue;
        else
            count += 1;
            m[CartesianIndex(pos)] = SAND;
            println(" ==== $count ==== ");
            pos = pos0;
            continue;
        end
    end
    show_cave(m);
    @show count;
end

function show_cave(m::Matrix{Int8})
    for r in eachrow(m')
        println(r);
    end
end

open("14/test_input.txt"; read=true) do fh
    ac = (parse_all_coords(fh));
    run_matrix(ac);
    run_matrix_2(ac);
end