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 d9a3b9fb9cab0c3c7c7c83fdc5556c66af337971
parent c1f3cd0bf3859795b4ebce2aa04094990424224c
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Tue,  3 Jan 2023 23:05:34 -0800

Solution to day 23, part 1.

Diffstat:
Asrc/day_23.jl | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 140 insertions(+), 0 deletions(-)

diff --git a/src/day_23.jl b/src/day_23.jl @@ -0,0 +1,140 @@ +#!/usr/bin/env julia +# https://adventofcode.com/2022/day/23 +using AdventOfCode + +example = readlines(IOBuffer(""" + ....#.. + ..###.# + #...#.# + .#...## + #.###.. + ##.#.## + .#..#..""")) +input = readlines("data/day_23.txt") + +Elf = Tuple{Int, Int} +ElfSet = AbstractSet{Elf} + +""" +Read the input and return a set of elf positions. +""" +function parse_input(input::Vector{<:AbstractString}) + elves = Set{Elf}() + for (y, line) = enumerate(input) + for (x, c) = enumerate(split(line, "")) + c == "#" && push!(elves, (y, x)) + end + end + elves +end + +""" +Find the neighbors of an elf. + +Returns a 3x3 boolean matrix that is true IFF an elf is in a position relative +to the given elf. +""" +function neighbors(elf::Elf, elves::ElfSet) + neighbors = zeros(Bool, (3, 3)) + for Δy = -1:1, Δx = -1:1 + if (Δy, Δx) == (0, 0) + neighbors[2+Δy, 2+Δx] = false # simplify move calculation + else + neighbors[2+Δy, 2+Δx] = (elf[1]+Δy, elf[2]+Δx) ∈ elves + end + end + neighbors +end + +""" +Propose a move for one elf at the given timestep. +""" +function propose_move(elf::Elf, elves::ElfSet, timestep::Int) + elfneighbs = neighbors(elf, elves) + if !any(elfneighbs) + return elf + else + for i = 0:3 + direction = [:n, :s, :w, :e][(i + timestep) % 4 + 1] + if direction == :n + !any(elfneighbs[1, :]) && return (elf[1]-1, elf[2]) + elseif direction == :s + !any(elfneighbs[3, :]) && return (elf[1]+1, elf[2]) + elseif direction == :w + !any(elfneighbs[:, 1]) && return (elf[1], elf[2]-1) + else # :e + !any(elfneighbs[:, 3]) && return (elf[1], elf[2]+1) + end + end + end + elf +end + +""" +Find a mapping from proposed elf positions to current elf positions. + +timestep starts at 0 and should increment at each step in the process. This is +a bit confusing (for me anyway), because the mapping is from *proposed* +locations to *originating* locations, when the converse seems a bit more +intuitive. +""" +function moveelves(elves::ElfSet, timestep::Int) + camefrom = Dict{Elf,Elf}() + invalidmoves = Vector{Elf}() + # Step 1: Propose moves for each elf, mark invalid moves + for elf₁ = elves + elf₂ = propose_move(elf₁, elves, timestep) + if elf₂ ∈ keys(camefrom) + # This is an invalid move. We'll mark it for now and leave the + # current elf in its present location + camefrom[elf₁] = elf₁ + push!(invalidmoves, elf₂) + else + # This move is fine (so far) + camefrom[elf₂] = elf₁ + end + end + + # Step 2: Put elves at marked-invalid locations back where they came from + for elf₂ = invalidmoves + camefrom[camefrom[elf₂]] = camefrom[elf₂] + delete!(camefrom, elf₂) + end + + camefrom +end + +moveelves(elves::Dict{Elf,Elf}, timestep::Int) = moveelves(keys(elves), timestep) + +""" +Return the (height, width) of the area containing all elves. +""" +function mapsize(elves::ElfSet) + (ymin, ymax) = extrema(x -> x[1], elves) + (xmin, xmax) = extrema(x -> x[2], elves) + (ymax-ymin+1, xmax-xmin+1) +end + +""" +Return the number of empty spots on the map. +""" +function emptyspots(elves::ElfSet) + prod(mapsize(elves)) - length(elves) +end + +emptyspots(elves::Dict{Elf,Elf}) = emptyspots(keys(elves)) + +function part_1(input) + elves = parse_input(input) + for ts = 0:9 + elves = moveelves(elves, ts) + end + emptyspots(elves) +end +@assert part_1(example) == 110 +@info part_1(input) + +function part_2(input) + nothing +end +@info part_2(input)