AoC 2020 (Julia)
Sonia Mitchell
2022-01-04
2020-julia.Rmd
Day 1: Report Repair
Part 1
## "../inst/2020/day1.txt"
dat = vec(readdlm(path));
# Find the two entries that sum to 2020
numbers = [x for x in combinations(dat, 2) if sum(x) == 2020];
# Convert 1-element Vector{Vector{Float64}} to 2-element Vector{Float64}
numbers = reshape(numbers)[1];
# What do you get if you multiply them together?
prod(Int, numbers)
## 802011
1.727 sec.
Day 2: Password Philosophy
Part 1
# Read in data
path = joinpath("..", "inst", "2020", "day2.txt");
dat = readlines(path);
function sled_password_check(dat)
results = 0
for i in dat
m = match(r"(\d+)-(\d+)\s+(.):\s+(.+)", i) # grep
minimum = parse(Int64, m.captures[1]) # convert to Int64
maximum = parse(Int64, m.captures[2])
letter = m.captures[3]
password = m.captures[4]
n = count(letter, password)
results += (n >= minimum && n <= maximum) ? 1 : 0
end
results
end;
# How many passwords are valid according to their policies?
sled_password_check(dat)
## 640
0.441 sec.
Notes
The ternary operator,
?:
is related toif-elseif-else
syntax and is written ascondition ? value_if_true : value_if_false
.Julia complains of scoping issues when calling
results
+= 1inside a
forloop (if
resultswas defined outside of it). Rather than using
global results += 1`, putting everything into a function will achieve the expected behaviour.
Part 2
function toboggan_password_check(dat)
results = 0
for i in dat
m = match(r"(\d+)-(\d+)\s+(.):\s+(.+)", i) # grep
pos1 = parse(Int64, m.captures[1]) # convert to Int64
pos2 = parse(Int64, m.captures[2])
letter = only(m.captures[3]) # convert to ASCII/Unicode
password = m.captures[4]
# Exactly one of these positions must contain the given letter
test = (password[pos1] == letter) + (password[pos2] == letter) == 1
results += test ? 1 : 0
end
results
end;
# How many passwords are valid according to the new interpretation of the policies?
toboggan_password_check(dat)
## 472
0.158 sec.
Day 3: Toboggan Trajectory
Part 1
# Read in data
path = joinpath("..", "inst", "2020", "day3.txt");
dat = readlines(path);
function trees(x, y, map)
xpos = ypos = 1
results = 0
xlim = length(map[1])
ylim = length(map)
while ypos != ylim
xpos += x
xpos = (xpos > xlim) ? xpos - xlim : xpos
ypos += y
results += map[ypos][xpos] == only("#") ? 1 : 0
end
results
end;
# Starting at the top-left corner of your map and following a slope of right 3 and
# down 1, how many trees would you encounter?
trees(3, 1, dat)
## 237
0.262 sec.
Part 2
# Determine the number of trees you would encounter if, for each of the following
# slopes, you start at the top-left corner and traverse the map all the way to the
# bottom
iterate = [[1,1], [3,1], [5,1], [7,1], [1,2]];
results = [trees(iterate[x][1], iterate[x][2], dat) for x in eachindex(iterate)];
# What do you get if you multiply together the number of trees encountered on each of
# the listed slopes?
prod(results)
## 2106818610
0.313 sec.
Day 4: Passport Processing
Part 1
# Read in data
path = joinpath("..", "inst", "2020", "day4.txt");
dat = readlines(path);
function parsepassports(dat)
record = []
d = Dict{String, String}()
for i in eachindex(dat)
if isempty(dat[i])
push!(record, d)
d = Dict{String, String}()
else
for j in split(dat[i])
key, value = split(j, ":")
d[key] = value
end
i == length(dat) ? push!(record, d) : nothing
end
end
record
end;
function countpassports(passports)
counter = 0
fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]
for i in eachindex(passports)
invalid = sum([!haskey(passports[i], key) for key in fields]) > 0
counter += invalid ? 0 : 1
end
counter
end;
# How many passports are valid?
passports = parsepassports(dat);
countpassports(passports)
## 182
0.491 sec.
Part 2
function checkfields(passports)
fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]
colours = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
counter = 0
for i in passports
invalid = sum([!haskey(i, key) for key in fields]) > 0
if (!invalid)
byr = 1920 <= parse(Int, i["byr"]) <= 2002
iyr = 2010 <= parse(Int, i["iyr"]) <= 2020
eyr = 2020 <= parse(Int, i["eyr"]) <= 2030
if endswith(i["hgt"], "cm")
tmp = parse(Int, replace(i["hgt"], "cm" => ""))
hgt = 150 <= tmp <= 193
elseif endswith(i["hgt"], "in")
tmp = parse(Int, replace(i["hgt"], "in" => ""))
hgt = 59 <= tmp <= 76
else
hgt = false
end
hcl = match(r"^#[0-9a-f]{6}", i["hcl"]) != nothing
ecl = issubset([i["ecl"]], colours)
pid = match(r"^[0-9]{9}$", i["pid"]) != nothing
# Is passport valid?
test = byr & iyr & eyr & hgt & hcl & ecl & pid
counter += test ? 1 : 0
end
end
counter
end;
# How many passports are valid?
checkfields(passports)
## 109
0.314 sec.
Day 5: Binary Boarding
Part 1
# Read in data
path = joinpath("..", "inst", "2020", "day5.txt");
dat = readlines(path);
function findseat(dat)
output = []
for i in eachindex(dat)
row_min, row_max = 0, 127
col_min, col_max = 0, 7
# Get row
row_directions = dat[i][1:7]
for j in row_directions
if j == only("F")
row_max = floor(Int, row_max - (row_max - row_min) / 2)
elseif j == only("B")
row_min = ceil(Int, row_min + (row_max - row_min) / 2)
end
end
# Get column
seat = dat[i][8:10]
for j in seat
if j == only("L")
col_max = floor(Int, col_max - (col_max - col_min) / 2)
elseif j == only("R")
col_min = ceil(Int, col_min + (col_max - col_min) / 2)
end
end
# Calculate seat ID
seat_id = (row_min * 8) + col_max
push!(output, seat_id)
end
output
end;
# Try test data
test = readlines(joinpath("..", "inst", "2020", "day5-test.txt"));
@assert all(findseat(test) .== [357, 567, 119, 820])
# What is the highest seat ID on a boarding pass?
seat_ids = findseat(dat);
maximum(seat_ids)
## 906
0.678 sec.
Day 6: Custom Customs
Part 1
# Read in data
path = joinpath("..", "inst", "2020", "day6-test.txt");
raw = read(path, String);
groups = split(raw, "\n\n"); # split into groups
## 5-element Vector{SubString{String}}:
## "abc"
## "a\nb\nc"
## "ab\nac"
## "a\na\na\na"
## "b\n"
## 5-element Vector{String}:
## "abc"
## "abc"
## "abac"
## "aaaa"
## "b"
# For each group, count the number of questions to which anyone answered "yes"
length.(unique.(dat));
# What is the sum of those counts?
sum(length.(unique.(dat)))
## 11
1.219 sec.
Day 7: Handy Haversacks
Day 8: Handheld Halting
Part 1
# Read in data ------------------------------------------------------------
path = joinpath("..", "inst", "2020", "day8.txt");
dat = read(path, String);
# Define functions --------------------------------------------------------
function tidyday8(dat)
dat = split(dat, "\n") # split by new line
dat = filter(!isempty, dat) # remove empty last element
dat = split.(dat, " ") # split by space
# Tidy up data
inst = [x[1] for x in dat] # extract instructions
val = [x[2] for x in dat] # extract values
vals = map(val) do str
mat = match.(r"[\+|\-](\d*)", str) # regex to extract number
cap = mat.captures # extract capture group
num = parse(Int64, cap[1]) # convert string to number
contains.(str, "-") ? num * -1 : num # multiply by -1 if negative
end
inst, vals
end;
function boot(inst, vals)
accumulator = 0; # initialise accumulator
i = 1; # initialise place in instruction list
log = [];
while i ∉ log # stop before an instruction is run again
push!(log, i) # record i in log
if inst[i] == "acc"
accumulator += vals[i] # add value to accumulator
i += 1 # next instruction
elseif inst[i] == "jmp"
i += vals[i] # jump to a new instruction
elseif inst[i] == "nop"
i += 1 # next instruction
end
end
return(accumulator)
end;
# Run boot code ----------------------------------------------------------
inst, vals = tidyday8(dat);
# Immediately before any instruction is executed a second time, what value is in the
# accumulator?
boot(inst, vals)
## 1749
0.648 sec.
Part 2
# Run boot code ----------------------------------------------------------
function boot2(inst, vals)
stop_condition = length(inst) + 1
for i in 1:length(inst)
new_inst = copy(inst)
if inst[i] == "acc"
continue
elseif inst[i] == "jmp"
new_inst[i] = "nop"
elseif inst[i] == "nop"
new_inst[i] = "jmp"
end
accumulator = 0 # initialise accumulator
j = 1 # initialise place in instruction list
log = []
while j ∉ log # stop before an instruction is run again
if j == stop_condition # found correct termination point!
return(accumulator)
end
push!(log, j) # record i in log
if new_inst[j] == "acc"
accumulator += vals[j] # add value to accumulator
j += 1 # next instruction
elseif new_inst[j] == "jmp"
j += vals[j] # jump to a new instruction
elseif new_inst[j] == "nop"
j += 1 # next instruction
end
end
end
end;
boot2(inst, vals)
## 515
0.317 sec.
Day 9: Encoding Error
Part 1
# Read in data ------------------------------------------------------------
path = joinpath("..", "inst", "2020", "day9.txt");
dat = readlines(path);
# Define functions --------------------------------------------------------
function day11(dat)
# convert string to numeric
parse.(Int64, dat)
end;
function checksums(dat)
preamble = 25
# generate pairwise combination index
cmb = combinations(1:preamble, 2) |> collect
# find sum of each pairwise combination
add = map(x -> dat[x[1]] + dat[x[2]], cmb)
for i in eachindex(dat)
# don't check preamble
if i ∈ 1:preamble
continue
end
# remove elements outwith the preamble
tmp = i - preamble - 1
remove = findall(x -> x[1] == tmp || x[2] == tmp, cmb)
deleteat!(cmb, remove)
deleteat!(add, remove)
# generate next set of indices
tmp = (tmp + 1):(i - 1)
ind = map(x -> [i, x], tmp)
append!(cmb, ind)
# generate next set of additions
new = map(x -> dat[x[1]] + dat[x[2]], ind)
append!(add, new)
# return invalid number
if dat[i] ∉ add
return(dat[i])
end
end
end;
# Find the first number in the list (after the preamble) which is not the sum of
# two of the 25 numbers before it
# What is the first number that does not have this property?
dat = day11(dat)
## 1000-element Vector{Int64}:
## 3
## 17
## 32
## 2
## 19
## 45
## 22
## 18
## 38
## 8
## ⋮
## 92412361470618
## 93964358367387
## 94334470603341
## 123814366611218
## 110087627226740
## 98650943515733
## 104829897365462
## 104882565934705
## 108297043768811
## 257342611
1.119 sec.
Part 2
# Find a contiguous set of at least two numbers in your list which sum to the
# invalid number from step 1
function findweakness(dat, invalid)
for i in eachindex(dat)
total = dat[i]
j = i
while(total < invalid)
j += 1
total += dat[j]
end
if total == invalid
# Find encryption weakness
vals = dat[i:j]
weakness = minimum(vals) + maximum(vals)
return(weakness)
end
end
end;
findweakness(dat, invalid)
## 35602097
0.231 sec.
Day 10: Adapter Array
Part 1
path = joinpath("..", "inst", "2020", "day10.txt");
# Find a chain that uses all of your adapters to connect the charging outlet to your
# device's built-in adapter and count the joltage differences between the charging
# outlet, the adapters, and your device
function day10(path)
dat = readlines(path) # read data
dat = parse.(Int64, dat) # convert string to integer
end;
function part1(dat)
tmp = copy(dat) |> sort # sort values
whole = vcat(0, tmp, tmp[end] + 3) # prepend with 0, append with +3
ones = count(==(1), diff(sort(whole))) # count number of ones
threes = count(==(3), diff(sort(whole))) # count number of threes
ones * threes
end;
# What is the number of 1-jolt differences multiplied by the number of 3-jolt
# differences?
day10(path) |> part1
## 2450
0.753 sec.