day_10.jl (4847B)
1 #!/usr/bin/env julia 2 # https://adventofcode.com/2022/day/10 3 using AdventOfCode 4 using DataFrames 5 6 # Using data frames again, not because they're strictly necessary, but because 7 # I've only touched them once so far and any actual work I do with Julia some 8 # day will certainly require them. I did this a lot last year, leaning into 9 # pandas (and Series objects in particular) just to get the hang of them. Let's 10 # just ignore the fact that I've barely touched python since then, for now. :) 11 12 example = readlines(IOBuffer(""" 13 addx 15 14 addx -11 15 addx 6 16 addx -3 17 addx 5 18 addx -1 19 addx -8 20 addx 13 21 addx 4 22 noop 23 addx -1 24 addx 5 25 addx -1 26 addx 5 27 addx -1 28 addx 5 29 addx -1 30 addx 5 31 addx -1 32 addx -35 33 addx 1 34 addx 24 35 addx -19 36 addx 1 37 addx 16 38 addx -11 39 noop 40 noop 41 addx 21 42 addx -15 43 noop 44 noop 45 addx -3 46 addx 9 47 addx 1 48 addx -3 49 addx 8 50 addx 1 51 addx 5 52 noop 53 noop 54 noop 55 noop 56 noop 57 addx -36 58 noop 59 addx 1 60 addx 7 61 noop 62 noop 63 noop 64 addx 2 65 addx 6 66 noop 67 noop 68 noop 69 noop 70 noop 71 addx 1 72 noop 73 noop 74 addx 7 75 addx 1 76 noop 77 addx -13 78 addx 13 79 addx 7 80 noop 81 addx 1 82 addx -33 83 noop 84 noop 85 noop 86 addx 2 87 noop 88 noop 89 noop 90 addx 8 91 noop 92 addx -1 93 addx 2 94 addx 1 95 noop 96 addx 17 97 addx -9 98 addx 1 99 addx 1 100 addx -3 101 addx 11 102 noop 103 noop 104 addx 1 105 noop 106 addx 1 107 noop 108 noop 109 addx -13 110 addx -19 111 addx 1 112 addx 3 113 addx 26 114 addx -30 115 addx 12 116 addx -1 117 addx 3 118 addx 1 119 noop 120 noop 121 noop 122 addx -9 123 addx 18 124 addx 1 125 addx 2 126 noop 127 noop 128 addx 9 129 noop 130 noop 131 noop 132 addx -1 133 addx 2 134 addx -37 135 addx 1 136 addx 3 137 noop 138 addx 15 139 addx -21 140 addx 22 141 addx -6 142 addx 1 143 noop 144 addx 2 145 addx 1 146 noop 147 addx -10 148 noop 149 noop 150 addx 20 151 addx 1 152 addx 2 153 addx 2 154 addx -6 155 addx -11 156 noop 157 noop 158 noop""")) 159 input = readlines("data/day_10.txt") 160 161 """ 162 Calculate cumulative sum, skipping `nothing` values 163 """ 164 function cumsomething(x) 165 # Life would have been easier if I had used `missing` instead of `nothing`, 166 # but to my mind, "missing" is more specific than "not available". I 167 # probably just need to get over this. 168 cumsum([isnothing(v) ? 0 : v for v in x]) 169 end 170 171 """ 172 Find the X value during every cycle. 173 174 Given a vector of observed cycles and the X values _after_ that cycle, return a 175 vector containing the X value _during_ every cycle. 176 """ 177 function interp_x(cycles::Vector{Int}, X_after::Vector{Int}) 178 X_during = Array{Int}(undef, (last(cycles)+1,)) 179 X_during[1] = 1 180 obs_cycle_idx = 1 181 for cycle = 2:lastindex(X_during) 182 if cycles[obs_cycle_idx] + 1 == cycle 183 X_during[cycle] = X_after[obs_cycle_idx] 184 obs_cycle_idx += 1 185 else 186 X_during[cycle] = X_during[cycle-1] 187 end 188 end 189 X_during 190 end 191 192 function interp_x(cpu::DataFrame) 193 interp_x(cpu[!, :total_cycles], cpu[!, :X]) 194 end 195 196 """ 197 Run the instructions and return a data frame tracing the machine state. 198 199 Note that the X value is the value of the X register at the end of the cycle 200 specified by total_cycles. So if you want the X value during a given cycle 201 number, look one row earlier. 202 """ 203 function run_instructions(input) 204 instructioncycles = Dict( 205 "addx" => 2, 206 "noop" => 1 207 ) 208 209 cpu = DataFrame( 210 instruction = AbstractString[], 211 arg = Vector{Union{Int, Nothing}}(), 212 cycles = Int[] 213 ) 214 215 # First, read in the instructions 216 for instruction = input 217 instparts = split(instruction, " ") 218 push!(cpu, ( 219 instruction = instparts[1], 220 arg = length(instparts) > 1 ? parse(Int, instparts[2]) : nothing, 221 cycles = instructioncycles[instparts[1]] 222 )) 223 end 224 225 # Next, find the value of X and the total number of cycles 226 cpu = transform(cpu, :arg => (v -> 1 .+ cumsomething(v)) => :X) 227 cpu = transform(cpu, :cycles => cumsum => :total_cycles) 228 229 cpu 230 end 231 232 function part_1(input) 233 cpu = run_instructions(input) 234 sum((20:40:last(cpu[!, :total_cycles])) .* interp_x(cpu)[20:40:end]) 235 end 236 @assert part_1(example) == 13140 237 @info part_1(input) 238 239 function draw_crt(X::Vector{Int}) 240 @assert length(X) == 6 * 40 241 crt = fill(".", (6, 40)) 242 243 cycle = 1 244 for row = 1:6, col = 1:40 245 (abs(X[cycle]+1 - col) <= 1) && (crt[row, col] = "#") 246 cycle += 1 247 end 248 249 println(reduce((x,y) -> x*"\n"*y, mapslices(join, crt, dims = 2))) 250 end 251 252 function part_2(input) 253 cpu = run_instructions(input) 254 draw_crt(interp_x(cpu)[1:240]) 255 end 256 println("Example:") 257 part_2(example) 258 println("Puzzle:") 259 part_2(input)