🦌 Advent Of Code 2020
The goals are:
Learn Julia
Write readable code (avoid golfing)
Write efficient code if possible
See Advent Of Code 2020 for more information.
Day 1
156
413
444
508
684
847
900
961
1068
1083
1094
1105
1123
1130
1137
1140
1146
1154
1157
1165
1986
1990
1991
1994
1995
1999
2000
2001
2005
2007
day1_part1 (generic function with 1 method)
xxxxxxxxxx
function day1_part1(input)
for n ∈ input
r = searchsorted(input_day1, 2020 - n)
if r.start ≤ r.stop
return n * input[r.start]
end
end
end
day1_part2 (generic function with 1 method)
xxxxxxxxxx
function day1_part2(input)
l = length(input)
for i ∈ 1:l
for j ∈ i+1:l
v = 2020 - input[i] - input[j]
if v ≤ 0
break
end
r = searchsorted(input_day1, v)
if r.start ≤ r.stop
return input[i] * input[j] * input[r.start]
end
end
end
end
Result for part 1: 913824
Result for part 2: 240889536
Day 2
xxxxxxxxxx
struct Password
policy_range :: UnitRange
policy_char :: Char
p :: String
end
nb_of_valid_passwords_part2 (generic function with 1 method)
xxxxxxxxxx
nb_of_valid_passwords_part2(passwords) =
count(
password ->
(password.p[password.policy_range.start] == password.policy_char) ⊻
(password.p[password.policy_range.stop] == password.policy_char),
passwords
)
parse_day2 (generic function with 1 method)
xxxxxxxxxx
function parse_day2(lines)
line_regexp = r"^(\d+)-(\d+) ([a-z]): ([a-z]+)$"
function parse_line(line)
m = match(line_regexp, line)
Password(
UnitRange(parse(Int, m.captures[1]), parse(Int, m.captures[2])),
m.captures[3][1],
m.captures[4]
)
end
parse_line.(lines)
end
nb_of_valid_passwords_part1 (generic function with 1 method)
xxxxxxxxxx
nb_of_valid_passwords_part1(passwords) =
count(
password -> count(==(password.policy_char), password.p) ∈ password.policy_range,
passwords
)
Test 1, part 1: true
Test 1, part 2: true
1:13
'r'
"gqdrspndrpsrjfjx"
5:16
'j'
"jjjjkjjzjjjjjfjzjjj"
14:16
'r'
"rrrnrrrrrcnrgxrr"
1:3
'k'
"bkktwhgktv"
3:5
'q'
"dxqqqzmqvs"
11:14
's'
"sssssssssssssv"
1:3
'd'
"cdzdq"
13:16
'q'
"scdqpdgpkvbwwqbv"
9:10
'd'
"ddrdddlddd"
15:17
'v'
"jvvvvvvgcvvvvrcvnv"
2:3
's'
"xssx"
8:15
'j'
"jwjjjjkhjjjltjmjjjr"
7:15
'm'
"wqspfmtpjftmplwp"
1:11
's'
"swdgzhgsxtssndzfm"
3:4
'b'
"bgjrg"
1:12
'x'
"jxgxxxpjwpsht"
5:15
'x'
"xxjxwshpxjxxxxsnxvz"
4:11
'r'
"bjnrpswfprrng"
12:14
'j'
"wjjzmwnmmvzsjhnnkj"
3:4
'd'
"dddv"
14:15
'z'
"nlzzzzzzzzzdzzzzzz"
8:12
'r'
"zrrrrprrxrrrrkrhk"
1:2
'z'
"qlfzd"
1:6
'j'
"kqjpjzpsgjjqz"
1:5
's'
"qfssks"
2:5
'r'
"nrrzrr"
4:6
'g'
"kggggg"
6:7
'c'
"cccccdqcc"
2:6
'x'
"vjkxbrfwnj"
16:18
's'
"kssssssswssssssssssb"
Result for part 1: 600
Result for part 2: 245
Day 3
parse_day3 (generic function with 1 method)
xxxxxxxxxx
function parse_day3(lines)
map(==("#"), hcat(split.(lines, "")...))'
end
nb_trees_encountered (generic function with 2 methods)
xxxxxxxxxx
function nb_trees_encountered(area :: AbstractArray{Bool, 2}, moves = [(1, 3)])
m, n = size(area)
map(moves) do (di, dj)
foldl(1:di:m, init = (1, 0)) do (j, sum), i
mod1(j + dj, n), sum + area[i, j]
end[2]
end
end
nb_trees_encountered_part2 (generic function with 1 method)
xxxxxxxxxx
nb_trees_encountered_part2(area) = nb_trees_encountered(area, [(1, 1), (1, 3), (1, 5), (1, 7), (2, 1)])
Test 1, part 1: true
Test 1, part 2: true
323×31 LinearAlgebra.Adjoint{Bool,Array{Bool,2}}:
0 0 1 0 0 0 0 0 0 1 1 1 0 0 … 0 0 1 1 0 0 1 0 1 0 0 0 0
0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 1 1 0 0 0 1 1 0 0 0 1 1
0 0 1 0 1 0 0 1 0 0 0 0 0 0 0 1 0 1 0 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0
0 0 0 1 0 0 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 1 1
0 0 0 0 0 0 1 0 1 0 1 1 0 0 … 0 1 0 0 0 0 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 1 1 0 0 0 0 0 0 1 0
⋮ ⋮ ⋮ ⋱ ⋮ ⋮ ⋮
0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1
0 0 0 0 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 1 0 1 0 0 0
1 0 0 0 0 0 0 0 0 0 0 0 1 1 … 0 0 0 0 0 0 0 0 0 1 0 0 1
0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0
0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
Result for part 1: 262
Result for part 2: 2698900776
Day 4
parse_day4 (generic function with 1 method)
xxxxxxxxxx
function parse_day4(lines)
passports = [Dict{String, String}()]
for line ∈ lines
if line == ""
push!(passports, Dict())
else
for value in split(line, ' ')
key_value = split(value, ':')
last(passports)[key_value[1]] = key_value[2]
end
end
end
passports
end
count_nb_valid_passport (generic function with 2 methods)
xxxxxxxxxx
function count_nb_valid_passport(passports :: AbstractArray{Dict{String,String}}, check_fields_integrity = false)
required_fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]
function check_field(field, value)
try
field == "byr" && 1920 ≤ parse(Int, value) ≤ 2020 ||
field == "iyr" && 2010 ≤ parse(Int, value) ≤ 2020 ||
field == "eyr" && 2020 ≤ parse(Int, value) ≤ 2030 ||
field == "hgt" &&
((inches = match(r"(\d+)in", value)) ≠ nothing && 59 ≤ parse(Int, inches.captures[1]) ≤ 76 ||
(cm = match(r"(\d+)cm", value)) ≠ nothing && 150 ≤ parse(Int, cm.captures[1]) ≤ 193) ||
field == "hcl" && occursin(r"^#[0-9a-f]{6}$", value) ||
field == "ecl" && value ∈ ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"] ||
field == "pid" && occursin(r"^[0-9]{9}$", value)
catch
false
end
end
count(passports) do p
all(required_fields) do f
haskey(p, f) && (!check_fields_integrity || check_field(f, p[f]))
end
end
end
Test 1, part 1: true
Test 1, part 2: true
Test 2, part 2: true
"hcl"
"#6c4ab1"
"ecl"
"oth"
"cid"
"189"
"hgt"
"174cm"
"iyr"
"2015"
"eyr"
"2026"
"pid"
"526744288"
"byr"
"1947"
"hcl"
"#808e9e"
"ecl"
"grn"
"pid"
"688706448"
"hgt"
"162cm"
"iyr"
"2017"
"cid"
"174"
"eyr"
"2025"
"byr"
"1943"
"hcl"
"#733820"
"ecl"
"oth"
"cid"
"124"
"pid"
"111220591"
"iyr"
"2019"
"eyr"
"2001"
"hgt"
"159in"
"byr"
"1933"
"hcl"
"#fffffd"
"ecl"
"oth"
"pid"
"812929897"
"hgt"
"159cm"
"iyr"
"2026"
"cid"
"291"
"eyr"
"2024"
"byr"
"1942"
"hcl"
"#ceb3a1"
"ecl"
"amb"
"cid"
"83"
"pid"
"524032739"
"iyr"
"2013"
"hgt"
"191cm"
"eyr"
"2028"
"byr"
"1974"
"hcl"
"eefed5"
"ecl"
"gry"
"pid"
"88405792"
"hgt"
"183cm"
"cid"
"221"
"eyr"
"2029"
"byr"
"1963"
"hcl"
"#18171d"
"ecl"
"grn"
"pid"
"777881168"
"hgt"
"181cm"
"iyr"
"2018"
"eyr"
"2021"
"byr"
"1923"
"hcl"
"#a5e1b5"
"ecl"
"gry"
"pid"
"062495008"
"hgt"
"178cm"
"iyr"
"2016"
"eyr"
"2027"
"byr"
"1941"
"hcl"
"#efcc98"
"cid"
"56"
"pid"
"649868696"
"iyr"
"2011"
"eyr"
"2025"
"hgt"
"164cm"
"byr"
"1971"
"hcl"
"#888785"
"ecl"
"blu"
"pid"
"117915262"
"hgt"
"188cm"
"iyr"
"2020"
"eyr"
"2023"
"byr"
"1925"
"ecl"
"brn"
"cid"
"174"
"pid"
"143293382"
"iyr"
"2012"
"eyr"
"2024"
"hgt"
"193cm"
"byr"
"1946"
"hcl"
"#602927"
"ecl"
"blu"
"hgt"
"192cm"
"pid"
"251564680"
"iyr"
"2011"
"eyr"
"2021"
"byr"
"1976"
"hcl"
"#b6652a"
"ecl"
"blu"
"hgt"
"164cm"
"pid"
"695538656"
"iyr"
"2010"
"eyr"
"2022"
"cid"
"244"
"byr"
"1973"
"hcl"
"#ceb3a1"
"ecl"
"hzl"
"pid"
"358398181"
"hgt"
"74in"
"iyr"
"2014"
"eyr"
"2027"
"cid"
"329"
"byr"
"1949"
"hcl"
"#623a2f"
"ecl"
"blu"
"cid"
"211"
"hgt"
"172cm"
"iyr"
"2019"
"eyr"
"2023"
"pid"
"657051725"
"byr"
"1954"
"hcl"
"#602927"
"ecl"
"amb"
"pid"
"562699115"
"hgt"
"162cm"
"iyr"
"2018"
"eyr"
"2026"
"byr"
"2000"
"hcl"
"#b6652a"
"ecl"
"brn"
"pid"
"835184859"
"hgt"
"157cm"
"iyr"
"2013"
"eyr"
"2027"
"byr"
"1981"
"hcl"
"#cfa07d"
"ecl"
"brn"
"pid"
"763432667"
"hgt"
"63in"
"iyr"
"2010"
"cid"
"107"
"eyr"
"2027"
"byr"
"1981"
"hcl"
"f55bf8"
"ecl"
"amb"
"hgt"
"177cm"
"cid"
"314"
"pid"
"632519974"
"eyr"
"2025"
"iyr"
"2015"
"byr"
"2009"
"hcl"
"#602927"
"ecl"
"hzl"
"pid"
"614239656"
"hgt"
"169cm"
"iyr"
"2014"
"eyr"
"2024"
"byr"
"1992"
"ecl"
"oth"
"pid"
"136852264"
"cid"
"339"
"iyr"
"2011"
"eyr"
"2024"
"byr"
"1949"
"hcl"
"#6b5442"
"ecl"
"brn"
"pid"
"772739059"
"hgt"
"157cm"
"iyr"
"2020"
"eyr"
"2025"
"byr"
"1945"
"hcl"
"#18171d"
"ecl"
"grn"
"pid"
"053763751"
"iyr"
"2018"
"eyr"
"2022"
"byr"
"1933"
"hcl"
"#18171d"
"ecl"
"oth"
"pid"
"214212776"
"cid"
"122"
"iyr"
"2020"
"eyr"
"2030"
"hgt"
"170cm"
"byr"
"1988"
"ecl"
"brn"
"pid"
"883116919"
"hgt"
"187cm"
"iyr"
"2018"
"eyr"
"2020"
"byr"
"1938"
"hcl"
"#a97842"
"ecl"
"grn"
"cid"
"329"
"pid"
"636649774"
"iyr"
"2020"
"eyr"
"2025"
"hgt"
"158cm"
"byr"
"1946"
"hcl"
"#341e13"
"ecl"
"blu"
"hgt"
"161cm"
"pid"
"461889565"
"iyr"
"2020"
"eyr"
"2023"
"cid"
"97"
"byr"
"1951"
"hcl"
"#cfa07d"
"ecl"
"hzl"
"hgt"
"168cm"
"pid"
"492241189"
"iyr"
"2013"
"eyr"
"2029"
"cid"
"150"
"byr"
"1980"
"hcl"
"#733820"
"ecl"
"gry"
"hgt"
"150cm"
"pid"
"401735295"
"cid"
"153"
"eyr"
"2024"
"iyr"
"2016"
"byr"
"1998"
"hcl"
"#a97842"
"ecl"
"hzl"
"hgt"
"184cm"
"pid"
"453480077"
"iyr"
"2018"
"eyr"
"2025"
"byr"
"2001"
Result for part 1: 264
Result for part 2: 224
Day 5
parse_day5 (generic function with 1 method)
xxxxxxxxxx
function parse_day5(lines)
map(lines) do line
row = parse(Int, join(replace(split(line[1:7], ""), "F" => "0", "B" => "1")); base = 2)
col = parse(Int, join(replace(split(line[8:10], ""), "L" => "0", "R" => "1")); base = 2)
row * 8 + col
end
end
Test 1, part 1: true
find_missing_id (generic function with 1 method)
xxxxxxxxxx
function find_missing_id(ids)
sorted_ids = sort(ids)
for i ∈ 1:length(sorted_ids)-1
if sorted_ids[i] + 1 ≠ sorted_ids[i + 1]
return sorted_ids[i] + 1
end
end
end
594
699
683
645
823
452
160
836
459
38
684
152
613
387
335
159
454
246
632
729
220
468
734
644
571
251
797
630
810
638
Result for part 1: 850
Result for part 2: 599
Day 6
parse_day6 (generic function with 1 method)
xxxxxxxxxx
function parse_day6(lines) :: Array{Array{Set{Char}}}
groups = [[]]
for line ∈ lines
if line == ""
push!(groups, [])
else
push!(last(groups), Set(line))
end
end
groups
end
sum_of_group_lengths (generic function with 1 method)
xxxxxxxxxx
function sum_of_group_lengths(groups, f)
map(groups) do group
length(reduce(f, group))
end |> sum
end
Test 1, part 1: true
Test 1, part 2: true
Number of groups read from data/day06.txt: 490
Result for part 1: 6735
Result for part 2: 3221
Day 7
xxxxxxxxxx
struct Bags
matrix :: Array{Int, 2}
names :: Dict{String, Int}
end
parse_day7 (generic function with 1 method)
xxxxxxxxxx
function parse_day7(lines)
l = length(lines)
m = zeros(Int, l, l)
name_indices = Dict{String, Int}()
get_bag_index(name) = get!(name_indices, name, length(name_indices) + 1)
for i ∈ 1:l
line = lines[i]
bag = match(r"^\w+ \w+", line).match
bag_index = get_bag_index(bag)
for inner_bag ∈ eachmatch(r"(\d+) (\w+ \w+) bag", line)
n = parse(Int, inner_bag[1])
m[bag_index, get_bag_index(inner_bag[2])] = n
end
end
Bags(m, name_indices)
end
bags_that_contain (generic function with 1 method)
xxxxxxxxxx
function bags_that_contain(name, bags)
visited = falses(length(bags.names))
to_visit = [bags.names[name]]
while !isempty(to_visit)
current = pop!(to_visit)
visited[current] = true
containing = findall(≠(0), bags.matrix[:, current] .* .!visited)
append!(to_visit, containing)
end
findall(visited)
end
nb_bags_contained (generic function with 1 method)
xxxxxxxxxx
function nb_bags_contained(name, bags)
function nb_bags_contained(bag_index)
content = bags.matrix[bag_index, :]
indexes = findall(≠(0), content)
1 + (isempty(indexes) ? 0 : sum(nb_bags_contained.(indexes) .* filter(≠(0), content)))
end
nb_bags_contained(bags.names[name]) - 1
end
Test 1, part 1: true
Test 1, part 2: true
Test 2, part 2: true
594×594 Array{Int64,2}: 0 3 0 0 0 0 0 0 0 0 0 0 0 0 … 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 5 2 0 0 0 0 0 … 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ⋮ ⋮ ⋮ ⋱ ⋮ ⋮ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 … 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
"posh teal"
213
"clear teal"
277
"faded tomato"
118
"dull indigo"
193
"drab indigo"
17
"mirrored chartreuse"
385
"shiny lime"
66
"dark brown"
429
"dotted fuchsia"
264
"shiny green"
519
"mirrored violet"
157
"posh gray"
529
"bright brown"
493
"drab cyan"
462
"posh brown"
557
"dim aqua"
259
"vibrant violet"
88
"light lime"
325
"shiny silver"
408
"vibrant silver"
518
"muted olive"
523
"dotted bronze"
498
"wavy aqua"
285
"vibrant bronze"
108
"wavy beige"
536
"mirrored olive"
332
"faded crimson"
503
"dull blue"
391
"faded magenta"
588
"posh coral"
172
Result for part 1: 335
Result for part 2: 2431
Day 8
xxxxxxxxxx
struct Instruction
operator :: Symbol
argument :: Int
end
parse_day8 (generic function with 1 method)
xxxxxxxxxx
function parse_day8(lines)
map(lines) do line
splitted = split(line, ' ')
Instruction(Symbol(splitted[1]), parse(Int, splitted[2]))
end
end
execute (generic function with 1 method)
xxxxxxxxxx
# The returned boolean is true if the execution has finished correctly or false if there is a infinite loop.
# The returned integer is the accumulator when the inifinite loop is detected or when the execution has finished correctly.
function execute(instructions) :: Tuple{Bool, Int}
pos = 1
accumulator = 0
n = length(instructions)
visited_positions = falses(n)
while pos ≤ n && !visited_positions[pos]
visited_positions[pos] = true
ins = instructions[pos]
if ins.operator == :acc
accumulator += ins.argument
pos += 1
elseif ins.operator == :jmp
pos += ins.argument
else # :nop.
pos += 1
end
end
pos > n, accumulator
end
fix_to_not_loop (generic function with 1 method)
xxxxxxxxxx
function fix_to_not_loop(instructions)
copy = deepcopy(instructions) # To avoid modifying the input.
function switch(pos)
if copy[pos].operator == :jmp
copy[pos] = Instruction(:nop, copy[pos].argument)
true
elseif copy[pos].operator == :nop
copy[pos] = Instruction(:jmp, copy[pos].argument)
true
else
false
end
end
for i ∈ 1:length(copy)
if switch(i)
finished, accumulator = execute(copy)
if finished
return accumulator
end
switch(i)
end
end
end
Test 1, part 1: true
Test 1, part 2: true
:acc
-8
:jmp
5
:acc
0
:acc
44
:acc
42
:jmp
324
:acc
-17
:jmp
1
:acc
-17
:jmp
51
:acc
-13
:acc
4
:jmp
1
:nop
608
:jmp
274
:acc
-17
:jmp
169
:acc
28
:nop
508
:jmp
1
:jmp
-112
:jmp
-613
:acc
26
:nop
-151
:jmp
-471
:acc
50
:acc
16
:nop
-119
:acc
46
:jmp
1
Result for part 1: (false, 1928)
Result for part 2: 1319
Day [9]
parse_day9 (generic function with 1 method)
xxxxxxxxxx
function parse_day9(lines)
parse.(Int64, lines)
end
first_number_without_valid_pair (generic function with 1 method)
xxxxxxxxxx
function first_number_without_valid_pair(numbers, preamble_size)
for i ∈ preamble_size+1:length(numbers)
previous = numbers[i-preamble_size:i-1]
n = numbers[i]
if n ∉ [a + b for a ∈ previous for b ∈ previous if a ≠ b]
return n
end
end
end
find_encryption_weakness (generic function with 1 method)
xxxxxxxxxx
function find_encryption_weakness(numbers, target)
l = length(numbers)
for i ∈ 1:l
for j ∈ i+1:l
ξ = numbers[i:j]
s = sum(ξ)
if s == target
return maximum(ξ) + minimum(ξ)
elseif s > target
break
end
end
end
end
Test 1, part 1: true
Test 1, part 2: true
10
21
30
41
7
31
43
13
18
16
8
27
48
49
19
46
50
37
9
26
81140653782337
134921301295717
81753820513729
89128049157277
110682581369284
86295433501493
100234164113687
115503232106027
108042699036276
182767846428000
Result for part 1: 31161678
Result for part 2: 5453868