day_14.jl (3567B)
1 #!/usr/bin/env julia 2 # https://adventofcode.com/2022/day/14 3 using AdventOfCode 4 5 example = readlines(IOBuffer(""" 6 498,4 -> 498,6 -> 496,6 7 503,4 -> 502,4 -> 502,9 -> 494,9""")) 8 input = readlines("data/day_14.txt") 9 10 module Maps 11 # Let's see if we can solve this by just making a Set of filled blocks. 12 # Could be too slow or memory hungry, IDK. 13 struct MapState 14 filled_blocks::Set{Tuple{Int,Int}} 15 lowest_row::Int 16 has_floor::Bool 17 end 18 19 function MapState(input::Vector{<:AbstractString}, 20 add_floor = false) 21 filled_blocks = Set{Tuple{Int,Int}}() 22 lowest_row = -Inf 23 for path = input 24 lines = map(x -> parse.(Int, split(x, ",")), split(path, " -> ")) 25 for i = 2:length(lines) 26 xline = range(minmax(lines[i-1][1], lines[i][1])..., 27 step = 1) 28 yline = range(minmax(lines[i-1][2], lines[i][2])..., 29 step = 1) 30 for x = xline, y = yline 31 push!(filled_blocks, (x, y)) 32 lowest_row = max(lowest_row, y) 33 end 34 end 35 end 36 MapState(filled_blocks, lowest_row, add_floor) 37 end 38 39 # I'm going to try to keep these function signatures implementation-neutral 40 # in case I need to change my approach. Ideally I can leave the basic 41 # sand-filling algorithm alone and only change the underlying 42 # datastructures and methods (if necessary). 43 """ 44 Return true IFF sand at the given coordinate is destined to fall forever. 45 """ 46 function isfallingforever(mapstate::MapState, coord::Tuple{Int,Int}) 47 !mapstate.has_floor && coord[2] > mapstate.lowest_row 48 end 49 50 """ 51 Return true IFF the given coordinate is filled with sand or stone. 52 """ 53 function isfilled(mapstate::MapState, coord::Tuple{Int,Int}) 54 (mapstate.has_floor && coord[2] >= mapstate.lowest_row + 2) || 55 coord in mapstate.filled_blocks 56 end 57 58 """ 59 Fill the given block with sand. 60 """ 61 function fillblock!(mapstate::MapState, coord::Tuple{Int,Int}) 62 push!(mapstate.filled_blocks, coord) 63 end 64 65 """ 66 Drop a block from (500, 0) until it comes to rest or falls forever. 67 68 Returns true if the block comes to rest and false if it falls forever or 69 the map is "full". Updates mapstate. 70 """ 71 function dropblock!(mapstate::MapState) 72 coord = (500, 0) 73 while !isfallingforever(mapstate, coord) && !isfilled(mapstate, coord) 74 if !isfilled(mapstate, coord .+ (0, 1)) 75 coord = coord .+ (0, 1) 76 elseif !isfilled(mapstate, coord .+ (-1, 1)) 77 coord = coord .+ (-1, 1) 78 elseif !isfilled(mapstate, coord .+ (1, 1)) 79 coord = coord .+ (1, 1) 80 else 81 fillblock!(mapstate, coord) 82 return true 83 end 84 end 85 return false 86 end 87 88 """ 89 Fill the map with sand until all sand will fall forever or the source is filled. 90 91 Return the number of blocks that came to rest 92 """ 93 function fillmap!(mapstate::MapState) 94 blocks_at_rest = 0 95 while dropblock!(mapstate) 96 blocks_at_rest += 1 97 end 98 blocks_at_rest 99 end 100 end 101 102 function part_1(input) 103 Maps.fillmap!(Maps.MapState(input)) 104 end 105 @assert part_1(example) == 24 106 @info part_1(input) 107 108 function part_2(input) 109 Maps.fillmap!(Maps.MapState(input, true)) 110 end 111 @assert part_2(example) == 93 112 @info part_2(input)