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

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)