Compare commits

..

36 Commits

Author SHA1 Message Date
7a8503c3fe Use auto instead of apply. 2021-01-02 21:22:07 -08:00
2b69cbd391 Rewrite using induction on list length. 2021-01-02 18:16:00 -08:00
661bcbb557 Add README. 2021-01-01 15:52:53 -08:00
c6ac448ad2 Add day 25 solution. 2020-12-29 00:50:26 -08:00
3d85144065 Add J solution for day 5. 2020-12-23 22:40:42 -08:00
4e8b636d2f Add day 1 and 2 solutions in J. 2020-12-23 21:47:05 -08:00
ee819d1362 Add day 3 solution in J. 2020-12-23 21:46:46 -08:00
34cb639327 Add day 23. 2020-12-23 21:46:32 -08:00
b33e678c7e Add day24 solution. 2020-12-23 21:44:32 -08:00
94ddabc590 Add solutions to day21 and day22. 2020-12-21 22:40:37 -08:00
32895b3e17 Add updated solutions, including day 20. 2020-12-20 00:31:30 -08:00
77c91f8386 Add unclean solution to day 17. 2020-12-16 21:29:26 -08:00
8ed0896adb Add unclean day16 solution. 2020-12-15 22:05:25 -08:00
c17cf38795 Add day15 solution. 2020-12-14 21:36:25 -08:00
f53c65fb0d Add experimental formalization of (inefficient) solution. 2020-12-13 23:32:11 -08:00
6ecae2b5bf Add day 4 solution. 2020-12-13 21:53:37 -08:00
c3a12cbf59 Clean up proof for day 8. 2020-12-13 20:34:28 -08:00
8ea03a4c51 Finish first proof for day 8.
Apparently writing proof objects by hand is
easier than using tactics.
2020-12-12 22:49:52 -08:00
7757fd2b49 Add day 13 solution. 2020-12-12 22:09:28 -08:00
f0fbba722c Flail around with this goddamn proof some more. 2020-12-12 20:08:21 -08:00
51a679ec63 Mess around some more with proof for day 8. 2020-12-12 01:44:16 -08:00
8459219e60 Clean up day 12. 2020-12-12 01:44:04 -08:00
5f231facc6 Add day 12 solution. 2020-12-11 21:19:04 -08:00
ae6f86302d Give formalizing day 8 a shot. 2020-12-11 11:32:54 -08:00
479affa4cc Add solutions to days 10 and 11 2020-12-11 11:32:33 -08:00
17492a8c23 Make changes to the template. 2020-12-11 11:32:12 -08:00
d1e15ca2c9 Factor console code into a separate file. 2020-12-11 11:32:01 -08:00
2688950560 Add day9 solution. 2020-12-08 21:34:39 -08:00
af8170a3e6 Clean up some solutions. 2020-12-07 23:38:38 -08:00
eafb73cfc1 Add day 8 solution. 2020-12-07 21:11:05 -08:00
61713a5faa Add say 7 solution. 2020-12-06 21:35:13 -08:00
e79b0344fd Add initial day6 solution. 2020-12-05 21:15:22 -08:00
479b1d745e Update code to use advent lib. 2020-12-05 16:06:51 -08:00
e9d0701b4c Extract some common utilities into a library. 2020-12-05 15:36:08 -08:00
7826194b37 Remove debug output form knapsack. 2020-12-05 00:06:11 -08:00
3d45bd3da3 Add knapsack implementation. 2020-12-05 00:05:02 -08:00
37 changed files with 1498 additions and 143 deletions

55
README.md Normal file
View File

@@ -0,0 +1,55 @@
# Advent of Code 2020 Solutions
Here's to my first 50 star year since 2017! The goal
was to make it to top 100 at least once this year,
and it finally came true on day 22 (and again in day 25
part 2, but that hardly counts). Honestly,
I am not at all happy with myself, though.
## Kinds of Solutions
I "raced" in [Crystal](https://crystal-lang.org), but also tried my hand at
formal verification in [Coq](https://coq.inria.fr/), and tried out
an APL dialect called [J](https://jsoftware.com/) for fun.
I didn't always clean my race day solutions, particularly the ones to the
"hard" days. I will write about my Coq solutions on [my site](https://danilafe.com).
## Rankings
Here's the (rather embarassing) table with my times.
```
-------Part 1-------- -------Part 2--------
Day Time Rank Score Time Rank Score
25 00:07:18 105 0 00:07:24 94 7
24 00:25:27 1496 0 00:43:46 1165 0
23 00:27:30 887 0 01:35:00 1057 0
22 00:03:44 42 59 01:16:52 2242 0
21 00:21:57 612 0 00:26:05 439 0
20 00:27:52 465 0 03:00:50 845 0
19 00:14:56 123 0 00:48:50 439 0
18 00:26:17 1209 0 00:28:01 574 0
17 00:17:23 400 0 00:19:14 281 0
16 00:11:50 627 0 01:03:40 2040 0
15 00:18:11 1772 0 00:30:38 1924 0
14 00:18:05 1220 0 00:39:57 1173 0
13 00:04:59 198 0 01:05:11 1606 0
12 00:06:19 206 0 00:14:17 245 0
11 00:13:23 414 0 00:19:11 261 0
10 00:05:04 293 0 00:31:04 1493 0
9 00:08:55 1721 0 00:26:50 3540 0
8 00:04:23 275 0 00:09:56 256 0
7 00:17:46 832 0 00:24:22 615 0
6 00:03:31 385 0 00:09:38 999 0
5 00:08:21 839 0 00:27:23 3404 0
4 00:07:40 786 0 00:20:26 440 0
3 00:02:46 119 0 00:07:40 448 0
2 00:03:48 341 0 00:06:01 272 0
1 00:07:52 1072 0 00:09:00 748 0
```
## Crystal for Competitive Programming
I really enjoyed writing Crystal for the Advent of Code, but there were a few reasons
why it wasn't perfect for the task.
* When you're not banking on brute force speed, you lose some time to the compiler.
* Numbers are _always_ 32-bit by default, and require constant `_i64` suffixes everywhere
in the code when they're involved.
* Type annotations (however necessary they are) for hashes and arrays make refactoring
a little bit slower.

36
console.cr Normal file
View File

@@ -0,0 +1,36 @@
def parse(input)
input.lines.map do |s|
code, int = s.split(" ")
{code, int.to_i32}
end
end
def run(prog)
acc = 0
pc = 0
visited = Set(Int32).new
input = Channel(Int32).new
output = Channel({Symbol, Int32}).new
spawn do
loop do
if pc >= prog.size
output.send({:term, acc})
break
elsif visited.includes? pc
output.send({:inf, acc})
break
end
visited << pc
code, int = prog[pc]
case code
when "acc"
acc += int
when "jmp"
pc += int - 1
end
pc += 1
end
end
{input, output}
end

View File

@@ -1,4 +1,5 @@
INPUT = File.read("day1.txt").lines.map(&.chomp.to_i32)
require "advent"
INPUT = input(2020, 1).lines.map(&.chomp.to_i32)
def part1
input = INPUT.clone.sort!

3
day1.ijs Normal file
View File

@@ -0,0 +1,3 @@
c =. > 0 ". each cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day1.txt.cache'
>./,(c*/c)*2020=c+/c
>./,(c*/c*/c)*2020=c+/c+/c

37
day10.cr Normal file
View File

@@ -0,0 +1,37 @@
require "advent"
INPUT = input(2020, 10).lines.map(&.to_i32).sort!
def part1(input)
diff_1 = 0
diff_3 = 0
curr = 0
input << (input.max + 3)
input.each do |i|
diff_1 += 1 if (i - curr) == 1
diff_3 += 1 if (i - curr) == 3
curr = i
end
puts diff_1 * diff_3
end
def count_ways(input, prev, index, mem, indent = 0)
if m = mem[{prev, index}]?
return m
end
return 0_i64 if input[index] - prev > 3 || index >= input.size
return 1_i64 if index == input.size - 1
total = count_ways(input, input[index], index+1, mem, indent + 1)
total += count_ways(input, prev, index+1, mem, indent + 1)
mem[{prev, index}] = total
return total
end
def part2(input)
input << (input.max + 3)
count_ways(input, 0, 0, {} of {Int32, Int32} => Int64)
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

64
day11.cr Normal file
View File

@@ -0,0 +1,64 @@
require "advent"
INPUT = input(2020, 11).lines.map(&.chars)
abstract class Search
def search(current, x, y, dx, dy)
x, y = x + dx, y + dy
return 0 if y < 0 || y >= current.size
return 0 if x < 0 || x >= current[y].size
search_impl(current,x,y,dx,dy)
end
abstract def search_impl(current, x, y, dx, dy)
end
class FirstSearch < Search
def search_impl(current, x, y, dx, dy)
return current[y][x] == '#' ? 1 : 0
end
end
class SecondSearch < Search
def search_impl(current, x, y, dx, dy)
return 1 if current[y][x] == '#'
return 0 if current[y][x] == 'L'
return search(current, x, y, dx, dy)
end
end
DIRS = [{-1,-1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1,1}]
def step(current, step, check, n)
current.each_with_index do |row, y|
row.each_with_index do |seat, x|
step[y][x] = current[y][x]
count = DIRS.sum do |dx, dy|
check.search(current, x, y, dx, dy)
end
step[y][x] = 'L' if seat == '#' && count >= n
step[y][x] = '#' if seat == 'L' && count == 0
end
end
{step, current}
end
def run(input, search, n)
current = input
step = input.clone
loop do
current, step = step(current, step, search, n)
break if current.zip_with(step) { |l, r| l == r }.all?
end
current.sum(&.count(&.==('#')))
end
def part1(input)
run(input, FirstSearch.new, 4)
end
def part2(input)
run(input, SecondSearch.new, 5)
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

45
day12.cr Normal file
View File

@@ -0,0 +1,45 @@
require "advent"
DISTS = [{1, 0}, {0, 1}, {-1, 0}, {0, -1}]
INPUT = input(2020, 12).lines.map do |l|
{l[0], l[1..].to_i32}
end
def part1(input)
pos = {0, 0}
angle = 0
input.each do |c, n|
case c
when 'N' then pos = pos.add({0, n})
when 'S' then pos = pos.add({0, -n})
when 'E' then pos = pos.add({n, 0})
when 'W' then pos = pos.add({-n, 0})
when 'L' then angle = (angle + n) % 360
when 'R' then angle = (angle + (360-n)) % 360
when 'F'
dx, dy = DISTS[angle//90]
pos = pos.add({dx * n, dy * n})
end
end
pos.map(&.abs).sum
end
def part2(input)
pos = {10, 1}
spos = {0, 0}
angle = 0
input.each do |c, n|
case c
when 'N' then pos = pos.add({0, n})
when 'S' then pos = pos.add({0, -n})
when 'E' then pos = pos.add({n, 0})
when 'W' then pos = pos.add({-n, 0})
when 'L' then (n//90).times { pos = {-pos[1], pos[0]} }
when 'R' then (n//90).times { pos = {pos[1], -pos[0]} }
when 'F' then spos = spos.add({pos[0] * n, pos[1]*n})
end
end
spos.map(&.abs).sum
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

34
day13.cr Normal file
View File

@@ -0,0 +1,34 @@
require "advent"
INPU = input(2020, 13).lines
INPUT = {INPU[0].to_i32, INPU[1].split(",")}
def part1(input)
early, busses = input
busses.reject! &.==("x")
busses = busses.map &.to_i32
bbus = busses.min_by do |b|
(early / b).ceil * b
end
diff = bbus * (((early/bbus).ceil * bbus).to_i32 - early)
end
def part2(input)
_, busses = input
busses = busses.map_with_index do |x, i|
x.to_i32?.try { |n| { n, i } }
end
busses = busses.compact
n = 0_i64
iter = 1_i64
busses.each do |m, i|
while (n + i) % m != 0
n += iter
end
iter *= m
end
puts n
puts busses
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

60
day14.cr Normal file
View File

@@ -0,0 +1,60 @@
require "advent"
raw = input(2020, 14).lines
input = [] of {UInt64, UInt64, Array(Int32), UInt64, UInt64}
and_mask = (2_u64**63) + (2_u64**63-1)
or_mask = 0_u64
floats = [] of Int32
raw.each do |line|
if data = line.match(/^mask = (.+)$/)
new_mask = data[1]
and_mask = (2_u64**63) + (2_u64**63-1)
or_mask = 0_u64
floats = [] of Int32
new_mask.reverse.chars.each_with_index do |c, i|
floats << i if c == 'X'
or_mask = or_mask | (1_u64 << i) if c == '1'
and_mask = and_mask & ~(1_u64 << i) if c == '0'
end
elsif data = line.match(/^mem\[(\d+)\] = (\d+)$/)
input << {and_mask, or_mask, floats, data[1].to_u64, data[2].to_u64}
else
raise "Invalid line"
end
end
def part1(input)
memory = {} of UInt64 => UInt64
input.each do |i|
and_mask, or_mask, floats, addr, v = i
memory[addr] = (v & and_mask) | or_mask
end
memory.values.sum
end
def generate_possible(indices, index, n, &block : UInt64 -> _)
if index == indices.size
yield n
return
end
float_addr = indices[index]
no = n | (1_u64 << float_addr)
generate_possible(indices, index+1, no, &block)
nz = n & ~(1_u64 << float_addr)
generate_possible(indices, index+1, nz, &block)
end
def part2(input)
memory = {} of UInt64 => UInt64
input.each do |i|
_, or_mask, floats, addr, v = i
generate_possible(floats, 0, addr | or_mask) do |addr|
memory[addr] = v
end
end
memory.values.sum
end
puts part1(input)
puts part2(input)

24
day15.cr Normal file
View File

@@ -0,0 +1,24 @@
require "advent"
INPUT = input(2020, 15).split(",").map(&.to_i32)
def run(input, times)
ls = {} of Int32 => Int32
temp = 0
(times-1).times do |i|
n = input[i]? || temp
temp = i - (ls[n]? || i)
ls[n] = i
end
return temp
end
def part1(input)
run(input, 2020)
end
def part2(input)
run(input, 30000000)
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

65
day16.cr Normal file
View File

@@ -0,0 +1,65 @@
require "advent"
def parse_range(str)
data = str.match(/([a-z ]+): (\d+)-(\d+) or (\d+)-(\d+)/).not_nil!
return {data[1], (data[2].to_i32..data[3].to_i32), (data[4].to_i32.. data[5].to_i32)}
end
fields, your, nearby = input(2020, 16).split("\n\n")
fields = fields.lines.map { |l| parse_range(l) }
your = your.lines[1].split(",").map(&.to_i32)
nearby = nearby.lines[1..].map(&.split(",").map(&.to_i32))
def part1(input)
fields, your, nearby = input
all_ranges = [] of Range(Int32, Int32)
fields.each do |a|
all_ranges << a[1] << a[2]
end
nearby.select! do |nb|
nb.all? do |n|
all_ranges.any? &.includes?(n)
end
end
nearby << your
field_map = {} of String => Set(Int32)
fields.each do |f|
field_map[f[0]] = Set(Int32).new
nearby[0].size.times do |i|
next if field_map.values.includes? i
all_match = nearby.all? do |nb|
f[1].includes?(nb[i]) || f[2].includes?(nb[i])
end
if all_match
field_map[f[0]] << i
end
end
end
sum = 1_u64
numbers = (0...nearby[0].size).to_a
solved = {} of String => Int32
while solved.size != fields.size
cleared = [] of {String, Int32}
field_map.each do |k, v|
next unless v.size == 1
cleared << {k, v.to_a[0]}
end
cleared.each do |f,n|
solved[f] = n
field_map.each do |k, v|
v.delete n
end
end
end
fields.each do |f|
next unless f[0].starts_with? "departure"
sum *= your[solved[f[0]]]
end
sum
end
def part2(input)
end
puts part1({fields, your, nearby})
puts part2({fields, your, nearby})

47
day17.cr Normal file
View File

@@ -0,0 +1,47 @@
require "advent"
require "benchmark"
INPUT = input(2020, 17).lines.map(&.chars)
def solve(input, dim)
step = input.clone
cubes = Set(Array(Int32)).new
new_cubes = Set(Array(Int32)).new
input.each_with_index do |row, y|
row.each_with_index do |c, x|
cubes << [x,y].concat([0] * (dim-2)) if c == '#'
end
end
8.times do |i|
print '.'
neighbor_count = Hash(Array(Int32), Int32).new(0)
Array.product([[-1,0,1]] * dim).each do |diff|
next if diff.all? &.==(0)
cubes.each do |c|
neighbor_count[c.zip_with(diff) { |a,b| a+b }] += 1
end
end
new_cubes.clear
neighbor_count.each do |n, i|
new_cubes << n if i == 3 || (cubes.includes?(n) && i == 2)
end
new_cubes, cubes = cubes, new_cubes
end
cubes.size
end
def part1(input)
solve(input, 3)
end
def part2(input)
solve(input, 4)
end
(3..).each do |i|
print "Dim #{i} "
bm = Benchmark.measure { puts " #{solve(INPUT, i)}" }
puts bm.real * 1000
end

54
day18.cr Normal file
View File

@@ -0,0 +1,54 @@
require "advent"
INPUT = input(2020, 18).lines
class Array(T)
def push_op(op)
r = pop
l = pop
self << ((op == '*') ? (l*r) : (l+r))
end
def has_op?
!empty? && last != '('
end
end
def translate(toks, prec)
output = [] of Int64
stack = [] of Char
toks.each do |tok|
case tok
when .number? then output << tok.to_i64
when '(' then stack << '('
when ')'
while stack.has_op?
output.push_op(stack.pop)
end
stack.pop
else
while stack.has_op? && prec[stack.last] < prec[tok]
output.push_op(stack.pop)
end
stack << tok
end
end
while stack.has_op?
output.push_op(stack.pop)
end
output.last
end
def part1(input)
input.sum do |line|
translate(line.chars.reject &.==(' '), {'*' => 0, '+' => 0})
end
end
def part2(input)
input.sum do |line|
translate(line.chars.reject &.==(' '), {'*' => 1, '+' => 0})
end
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

68
day19.cr Normal file
View File

@@ -0,0 +1,68 @@
require "advent"
rlines, _, strings = input(2020, 19).partition("\n\n")
strings = strings.lines
rules = {} of String => String
rlines.lines.map do |l|
rule, _, text = l.partition(": ")
rules[rule] = text
end
alias Matcher = Proc(String,Int32,Array(Int32))
def char(c)
->(str : String, i : Int32) { str[i]? == c ? [i+1] : [] of Int32 }
end
def any(ps)
->(str : String, i : Int32) { ps.flat_map { |p| p.call(str, i) } }
end
def seq(ps)
->(str : String, i : Int32) {
base = [i]
ps.each do |p|
base = base.flat_map { |i| p.call(str, i) }
end
base
}
end
def to_regex(rules, rule)
end
def build_rule(rules, built, rule) : Matcher
if exists = built[rule]?
return exists
end
body = rules[rule]
return built[rule] = char body[1] if body.matches? /"."/
branches = [] of Matcher
top = any(branches)
built[rule] = top
branches.concat(body.split(" | ").map { |b| seq(b.split(" ").map { |subrule| build_rule(rules, built, subrule) }) })
top
end
def part1(input)
rules, lines = input
matcher = build_rule(rules, {} of String => Matcher, "0")
lines.count do |l|
matcher.call(l, 0).includes? l.size
end
end
def part2(input)
rules, lines = input
rules["8"] = "42 | 42 8"
rules["11"] = "42 31 | 42 11 31"
matcher = build_rule(rules, {} of String => Matcher, "0")
lines.count do |l|
matcher.call(l, 0).includes? l.size
end
end
puts part1({rules,strings})
puts part2({rules,strings})

View File

@@ -1,4 +1,5 @@
INPUT = File.read("day2.txt").lines.map(&.chomp)
require "advent"
INPUT = input(2020, 2).lines.map(&.chomp)
def parse(line)
data = line.match(/([0-9]+)-([0-9]+) ([a-z]): ([a-z]+)/)

6
day2.ijs Normal file
View File

@@ -0,0 +1,6 @@
r =: >"1 cut each cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day2.txt.cache'
rs =: >0".each'-'cut"1>(0}"1 r)
cs =: 0{"1>1{"1 r
ss =: 2{"1 r
+/1>**/|:rs-+/"1 cs=>ss
+/1=+/|:(rs-1){"1 cs=>ss

166
day20.cr Normal file
View File

@@ -0,0 +1,166 @@
require "advent"
tiles = input(2020, 20).split "\n\n"
tiles.pop
thash = {} of Int32 => Array(Array(Char))
tiles = tiles.map do |t|
tl = t.lines
tid = tl[0].match(/Tile (\d+):/).not_nil![1].to_i32
tls = tl[1..]
thash[tid] = tls.map &.chars
end
class Array(T)
def matches?(other)
zip_with(other) { |l,r| l == r }.all?
end
def rotate
reverse.transpose
end
end
def check(side_a, side_b)
return :normal if side_a.matches?(side_b)
return :flip if side_a.matches?(side_b.reverse)
return nil
end
def check_all(side_a, other, other_t)
check(side_a, other.first) || check(side_a, other.last) || check(side_a, other_t.first) || check(side_a, other_t.last)
end
MONSTER = [ {0, 1}, {1, 0}, {4, 0}, {5, 1}, {6, 1}, {7, 0}, {10, 0}, {11,1}, {12, 1}, {13, 0}, {16, 0},
{17, 1}, {18, 1}, {18, 2}, {19, 1} ]
def stitch(m, thash, corner)
image = Array(Array(Char)).new(12*8) do |y|
Array(Char).new(12*8) do |x|
'.'
end
end
tile = thash[corner]
tile_id = corner
tile.reverse! if m[corner].has_key? :top
tile.each &.reverse! if m[corner].has_key? :left
12.times do |row|
tile = tile.not_nil!
row_tile = tile
row_id = tile_id
12.times do |col|
row_tile = row_tile.not_nil!
(0..7).each do |y|
(0..7).each do |x|
image[col*8+y][row*8+x] = row_tile[y+1][x+1]
end
end
matches = nil
thash.each do |other_id, other_tile|
next if matches
next if other_id == row_id
4.times do
if row_tile.last.matches? other_tile.first
row_id = other_id
matches = other_tile
elsif row_tile.last.matches? other_tile.first.reverse
row_id = other_id
matches = other_tile.map &.reverse
end
other_tile = other_tile.rotate
end
end
row_tile = matches
end
rot = tile.rotate
matches = nil
thash.each do |other_id, other_tile|
next if matches
next if other_id == tile_id
4.times do
if rot.last.matches? other_tile.first
tile_id = other_id
matches = other_tile.rotate.rotate.rotate
elsif rot.last.matches? other_tile.first.reverse
tile_id = other_id
matches = other_tile.map(&.reverse).rotate.rotate.rotate
end
other_tile = other_tile.rotate
end
end
tile = matches
end
image
end
def find_dragons(stitch)
dragons = [] of {Int32,Int32}
stitch.each_with_index do |row, y|
row.each_with_index do |c, x|
is_dragon = MONSTER.all? do |dx, dy|
(stitch[y+dy]?.try &.[x+dx]?) == '#'
end
dragons << {x,y} if is_dragon
end
end
dragons
end
def find_all_dragons(stitch)
dragons = [] of {Int32,Int32}
4.times do
dragons.concat find_dragons(stitch)
dragons.concat find_dragons(stitch.reverse)
stitch = stitch.rotate
end
dragons
end
def match(thash, tile)
matches = {} of Symbol => {Int32, Symbol}
cs = thash[tile]
tcs = cs.transpose
thash.each do |t, ocs|
next if t == tile
tocs = ocs.transpose
top = check_all(cs.first, ocs, tocs)
bottom = check_all(cs.last, ocs, tocs)
left = check_all(tcs.first, ocs, tocs)
right = check_all(tcs.last, ocs, tocs)
matches[:top] = {t, top} if top
matches[:left] = {t, left} if left
matches[:bottom] = {t, bottom} if bottom
matches[:right] = {t, right} if right
end
matches
end
def part1(input)
corners = input.select do |t, i|
match(input, t).size == 2
end
corners.keys.map(&.to_i64).product
end
def part2(input)
matches = {} of Int32 => Hash(Symbol, {Int32, Symbol})
corners = input.select do |t, i|
matches[t] = match(input, t)
match(input, t).size == 2
end
stitched = stitch(matches, input, corners.first[0])
dragons = find_all_dragons(stitched)
stitched.sum(&.count(&.==('#'))) - (dragons.size * MONSTER.size)
end
puts part1(thash.clone)
puts part2(thash.clone)

31
day21.cr Normal file
View File

@@ -0,0 +1,31 @@
require "advent"
input = input(2020, 21).lines.map do |line|
data = line.match(/^([a-z ]+) \(contains (.+)\)$/).not_nil!
{data[1].split(" ").to_set, data[2].split(", ").to_set}
end
allergens = input.flat_map(&.last.to_a).to_set
ingredients = input.flat_map(&.first.to_a).to_set
allergen_sets = {} of String => Set(String)
allergens.each do |a|
input.each do |ings, als|
next unless als.includes? a
allergen_sets[a] ||= ings
allergen_sets[a] &= ings
end
end
safe = ingredients.reject { |i| allergen_sets.any? &.last.includes?(i) }
puts "Part 1: #{input.sum &.first.count { |i| safe.includes? i }}"
known_allergens = {} of String => String
while allergen_sets.size > known_allergens.size
allergen_sets.each do |a, s|
next unless s.size == 1
new_known = s.first
known_allergens[a] = new_known
allergen_sets.each &.last.delete(new_known)
end
end
puts "Part 2: #{known_allergens.to_a.sort_by(&.first).map(&.last).join(",")}"

66
day22.cr Normal file
View File

@@ -0,0 +1,66 @@
require "advent"
first, second = input(2020, 22).split("\n\n")
first = first.lines[1..].map &.to_i32
second = second.lines[1..].map &.to_i32
class Array(T)
def pop_left
delete_at(0)
end
def score
total = 0
s = size
each_with_index do |n, i|
total += n * (s - i)
end
total
end
end
def no_rec(deck1, deck2)
while !deck1.empty? && !deck2.empty?
f, s = deck1.delete_at(0), deck2.delete_at(0)
if f > s
deck1 << f << s
else
deck2 << s << f
end
end
return !deck1.empty?
end
def rec(deck1, deck2)
seen = Set({Int32, Int32}).new
while !deck1.empty? && !deck2.empty?
key = {deck1.score, deck2.score}
return true if seen.includes?(key)
seen << key
f = deck1.delete_at(0)
s = deck2.delete_at(0)
p1wins = (f <= deck1.size) && (s <= deck2.size) ? rec(deck1[0..f-1], deck2[0..s-1]) : f > s
if p1wins
deck1 << f << s
else
deck2 << s << f
end
end
return !deck1.empty?
end
def run(first, second, proc)
((proc.call(first, second)) ? first : second).score
end
def part1(input)
run(input[0], input[1], ->no_rec(Array(Int32), Array(Int32)))
end
def part2(input)
run(input[0], input[1], ->rec(Array(Int32), Array(Int32)))
end
puts part1({first, second}.clone)
puts part2({first, second}.clone)

117
day23.cr Normal file
View File

@@ -0,0 +1,117 @@
require "advent"
input = input(2020, 23).lines[0].chars.map &.to_i32
class Array(T)
def get(i)
self[i % size]
end
def del(i)
delete_at(i % size)
end
end
class Node
property next : Node
property int : Int32
def initialize(@int, @next)
end
def insert(other : Node, n = 1)
set_next = other
(n-1).times { set_next = set_next.@next }
set_next.next = @next
@next = other
end
def remove(n = 1)
curr = self
to_return = self.next
n.times { curr = curr.@next }
@next = curr.@next
to_return
end
def find(n)
start = self
while start.int != n
start = start.next
end
return start
end
def to_s(n)
return "" if n < 0
return "#{@int} -> #{@next.to_s(n-1)}"
end
def includes?(n, count)
return false if count <= 0
return @int == n || @next.includes?(n, count-1)
end
end
class Cups
getter size : Int32
getter head : Node
def initialize(list : Array(Int32))
@cache = {} of Int32 => Node
h = list.delete_at(0)
temp = uninitialized Node
@cache[h] = @head = Node.new(h, temp)
@size = list.size
@local_min = list.min.as(Int32)
@local_max = list.max.as(Int32)
@head.next = @head
curr = @head
list.each do |n|
@cache[n] = new = Node.new(n, curr.next)
curr.insert(new)
curr = new
end
end
def to_s
@head.to_s(@size)
end
def step
first = @head
after = first.remove(3)
m = first.int - 1
while after.includes?(m, 3) || m < @local_min
m = (m < @local_min) ? @local_max : (m - 1)
end
@cache[m].insert(after, 3)
@head = @head.next
end
end
def play(input, count)
cups = Cups.new input
count.times { |n| cups.step }
cups
end
def part1(input)
cups = play(input.clone, 100)
cups.head.find(1).next.to_s(cups.size - 1).gsub(" -> ", "")
end
def part2(input)
list = input.clone
max = input.max
(1000000 - input.size).times do
max += 1
list << max
end
cups = play(list, 10000000)
one = cups.head.find(1)
one.next.int.to_i64 * one.next.next.int
end
puts "Part 1: #{part1(input)}"
puts "Part 2: #{part2(input)}"

78
day24.cr Normal file
View File

@@ -0,0 +1,78 @@
require "advent"
INPUT = input(2020, 24).lines.map &.tiles.map { |t| MATCHES[t] }
MATCHES = { "se" => {0.5, -1.0}, "sw" => {-0.5, -1.0}, "ne" => {0.5, 1.0}, "nw" => {-0.5, 1.0}, "e" => {1.0, 0.0}, "w" => {-1.0, 0.0} }
class String
def tiles
i = 0
tiles = [] of String
while i < size
if self[i] == 's' || self[i] == 'n'
tiles << self[i..i+1]
i += 2
else
tiles << self[i..i]
i += 1
end
end
tiles
end
end
class Array(T)
def pos
curr = {0.0, 0.0}
each do |c|
curr = curr.add c
end
curr
end
end
def part1(input)
counts = Hash({Float64,Float64}, Int32).new(0)
input.each do |i|
counts[i.pos] += 1
end
counts.count &.[1].%(2).==(1)
end
struct Tuple(*T)
def neighbors
MATCHES.values.map &.add(self)
end
end
def part2(input)
state = Hash({Float64,Float64}, Bool).new(false)
counts = Hash({Float64,Float64}, Int32).new(0)
input.each do |i|
state[i.pos] ^= true
end
100.times do |i|
counts.clear
state.each do |t, f|
next unless f
t.neighbors.each do |n|
counts[n] += 1
end
end
new_state = state.clone.clear
state.each do |k, v|
next unless v
new_state[k] = !(counts[k] == 0 || (counts[k] > 2))
end
counts.each do |k, v|
new_state[k] = true if (!state[k]) && v == 2
end
state = new_state
end
state.count &.[1]
end
puts part1(INPUT.clone)
puts part2(INPUT.clone)

28
day25.cr Normal file
View File

@@ -0,0 +1,28 @@
require "advent"
INPUT = input(2020, 25).lines.map(&.to_i64)
def transform(s)
i = 1_i64
c = 0
loop do
c += 1
i = (i * s) % 20201227
yield i, c
end
end
def find_size(s, goal)
transform(s) do |n, c|
return c if n == goal
end
end
def part1(input)
goal = find_size(7, input[0])
puts goal
transform(input[1]) do |n, c|
return n if c == goal
end
end
puts part1(INPUT.clone)

View File

@@ -1,4 +1,5 @@
INPUT = File.read("day3.txt").lines.map(&.chomp)
require "advent"
INPUT = input(2020, 3).lines.map(&.chomp)
def run(input, slopes)
prod = 1_i64

5
day3.ijs Normal file
View File

@@ -0,0 +1,5 @@
r =: > cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day3.txt.cache'
rn =: 4 : '+/''#''=((#|:y)|x*i.#y){"0 1 y'
rnx =: 3 : '(1{y) rn 0{"2((0{-y)]\r)'
rnx 1 3
*/ rnx"1 (_2 [\ 1 1 1 3 1 5 1 7 2 1)

View File

@@ -1,5 +1,6 @@
require "./passports.cr"
INPUT = File.read("day4.txt")
require "advent"
INPUT = input(2020, 4)
def part1
input = INPUT.clone

View File

@@ -1,4 +1,5 @@
INPUT = File.read("day5.txt").lines.map(&.chomp)
require "advent"
INPUT = input(2020, 5).lines.map(&.chomp)
def partition(choices, list)
b = 0

4
day5.ijs Normal file
View File

@@ -0,0 +1,4 @@
bs2n =: 3 : '+/ "1(|.*/\(1,(_1+#y)$2)) *"(1) 0 "."0 y'
ns =: bs2n "1 'F0B1L0R1' charsub > cutopen 1!:1 < jpath '~/projects/AoC/2020/year2020day5.txt.cache'
>./ns
(<./ns)+I.-.((<./ns)+i.#ns)e.ns

2
day5g.cr Normal file
View File

@@ -0,0 +1,2 @@
i=File.read_lines("day5.txt").map &.tr("FBLR", "0101").to_i32(2);
puts({i.max,(i.min..i.max).select{|n|!i.includes? n}})

13
day6.cr Normal file
View File

@@ -0,0 +1,13 @@
require "advent"
INPUT = input(2020, 6).split("\n\n")
def part1
INPUT.sum &.chars.uniq!.count(&.ascii_letter?)
end
def part2
INPUT.sum &.lines.map(&.chars.to_set).intersect.size
end
puts part1
puts part2

45
day7.cr Normal file
View File

@@ -0,0 +1,45 @@
require "advent"
INPUT = input(2020, 7).lines.map(&.chomp).map do |s|
data = s.match(/^([a-z ]+) bags contain (.*).$/).not_nil!
contents = data[2].split(", ").compact_map do |s|
s.match(/(\d+) (.*) bags?/).try { |m| {m[1].to_i32, m[2]} }
end
{data[1], contents}
end
def build_graph(input)
gr = Graph(String).new
seen = Set(String).new
input.each do |i|
seen << i[0]
i[1].each do |to|
n, m = to
gr.add_edge(i[0], m, n)
end
end
{seen, gr}
end
def part1
seen, gr = build_graph(INPUT)
seen.count do |s|
gr.find_path(s, "shiny gold") && s != "shiny gold"
end
end
def count_bags(gr, current)
return 1 unless es = gr.@edges[current]?
1 + es.sum do |e|
t, k = e
k * count_bags(gr, t)
end
end
def part2
seen, gr = build_graph(INPUT)
count_bags(gr, "shiny gold") - 1
end
puts part1
puts part2

24
day8.cr Normal file
View File

@@ -0,0 +1,24 @@
require "advent"
require "./console.cr"
INPUT = input(2020, 8)
def part1
run(parse(INPUT))[1].receive[1]
end
def part2
input = parse(INPUT)
jnp = input.find_indices { |e| e[0] == "jmp" || e[0] == "nop" }
jnp.each do |i|
prog = input.clone
op, int = prog[i]
prog[i] = {op.tr("jmpnop", "nopjmp"), int}
code, acc = run(prog)[1].receive
return acc if code == :term
end
end
puts part1
puts part2

255
day8.v Normal file
View File

@@ -0,0 +1,255 @@
Require Import Coq.ZArith.Int.
Require Import Coq.Lists.ListSet.
Require Import Coq.Vectors.VectorDef.
Require Import Coq.Vectors.Fin.
Require Import Coq.Program.Equality.
Require Import Coq.Logic.Eqdep_dec.
Require Import Coq.Arith.Peano_dec.
Require Import Coq.Program.Wf.
Require Import Lia.
Module DayEight (Import M:Int).
(* We need to coerce natural numbers into integers to add them. *)
Parameter nat_to_t : nat -> t.
(* We need a way to convert integers back into finite sets. *)
Parameter clamp : forall {n}, t -> option (Fin.t n).
Definition fin := Fin.t.
(* The opcode of our instructions. *)
Inductive opcode : Type :=
| add
| nop
| jmp.
(* The result of running a program is either the accumulator
or an infinite loop error. In the latter case, we return the
set of instructions that we tried. *)
Inductive run_result {n : nat} : Type :=
| Ok : t -> run_result
| Fail : set (fin n) -> run_result.
(* A single program state .*)
Definition state n : Type := (fin (S n) * set (fin n) * t).
(* An instruction is a pair of an opcode and an argument. *)
Definition inst : Type := (opcode * t).
(* An input is a bounded list of instructions. *)
Definition input (n : nat) := VectorDef.t inst n.
(* 'indices' represents the list of instruction
addresses, which are used for calculating jumps. *)
Definition indices (n : nat) := VectorDef.t (fin n) n.
(* Change a jump to a nop, or a nop to a jump. *)
Definition swap (i : inst) : inst :=
match i with
| (add, t) => (add, t)
| (nop, t) => (jmp, t)
| (jmp, t) => (nop, t)
end.
Inductive swappable : inst -> Prop :=
| swap_nop : forall t, swappable (nop, t)
| swap_jmp : forall t, swappable (jmp, t).
(* Compute the destination jump index, an integer. *)
Definition jump_t {n} (pc : fin n) (off : t) : t :=
M.add (nat_to_t (proj1_sig (to_nat pc))) off.
(* Compute a destination index that's valid.
Not all inputs are valid, so this may fail. *)
Definition valid_jump_t {n} (pc : fin n) (off : t) : option (fin (S n)) := @clamp (S n) (jump_t pc off).
(* Cast a fin n to a fin (S n). *)
Fixpoint weaken_one {n} (f : fin n) : fin (S n) :=
match f with
| F1 => F1
| FS f' => FS (weaken_one f')
end.
(* Convert a nat to fin. *)
Fixpoint nat_to_fin (n : nat) : fin (S n) :=
match n with
| O => F1
| S n' => FS (nat_to_fin n')
end.
(* A finite natural is either its maximum value (aka nat_to_fin n),
or it's not thatbig, which means it can be cast down to
a fin (pred n). *)
Lemma fin_big_or_small : forall {n} (f : fin (S n)),
(f = nat_to_fin n) \/ (exists (f' : fin n), f = weaken_one f').
Proof.
(* Hey, looks like the creator of Fin provided
us with nice inductive principles. Using Coq's
default `induction` breaks here.
Merci, Pierre! *)
apply Fin.rectS.
- intros n. destruct n.
+ left. reflexivity.
+ right. exists F1. auto.
- intros n p IH.
destruct IH.
+ left. rewrite H. reflexivity.
+ right. destruct H as [f' Heq].
exists (FS f'). simpl. rewrite Heq.
reflexivity.
Qed.
(* One modification: we really want to use 'allowed' addresses,
a set that shrinks as the program continues, rather than 'visited'
addresses, a set that increases as the program continues. *)
Inductive step_noswap {n} : inst -> (fin n * t) -> (fin (S n) * t) -> Prop :=
| step_noswap_add : forall pc acc t,
step_noswap (add, t) (pc, acc) (FS pc, M.add acc t)
| step_noswap_nop : forall pc acc t,
step_noswap (nop, t) (pc, acc) (FS pc, acc)
| step_noswap_jmp : forall pc pc' acc t,
valid_jump_t pc t = Some pc' ->
step_noswap (jmp, t) (pc, acc) (pc', acc).
Inductive done {n} : input n -> state n -> Prop :=
| done_prog : forall inp v acc, done inp (nat_to_fin n, v, acc).
Inductive stuck {n} : input n -> state n -> Prop :=
| stuck_prog : forall inp pc' v acc,
~ set_In pc' v -> stuck inp (weaken_one pc', v, acc).
Inductive run_noswap {n} : input n -> state n -> state n -> Prop :=
| run_noswap_ok : forall inp st, done inp st -> run_noswap inp st st
| run_noswap_fail : forall inp st, stuck inp st -> run_noswap inp st st
| run_noswap_trans : forall inp pc pc' v acc acc' st',
set_In pc v ->
step_noswap (nth inp pc) (pc, acc) (pc', acc') ->
run_noswap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
run_noswap inp (weaken_one pc, v, acc) st'.
Inductive run_swap {n} : input n -> state n -> state n -> Prop :=
| run_swap_normal : forall inp pc pc' v acc acc' st',
set_In pc v ->
~ swappable (nth inp pc) ->
step_noswap (nth inp pc) (pc, acc) (pc', acc') ->
run_swap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
run_swap inp (weaken_one pc, v, acc) st'
| run_swap_swapped_ok : forall inp pc pc' v acc acc' st',
set_In pc v ->
swappable (nth inp pc) ->
step_noswap (swap (nth inp pc)) (pc, acc) (pc', acc') ->
run_noswap inp (pc', set_remove Fin.eq_dec pc v, acc') st' ->
done inp st' ->
run_swap inp (weaken_one pc, v, acc) st'
| run_swap_swapped_next : forall inp pc pc'w pc'n v acc acc'w acc'n st'w st'n,
set_In pc v ->
swappable (nth inp pc) ->
step_noswap (swap (nth inp pc)) (pc, acc) (pc'w, acc'w) ->
run_noswap inp (pc'w, set_remove Fin.eq_dec pc v, acc'w) st'w ->
stuck inp st'w ->
step_noswap (nth inp pc) (pc, acc) (pc'n, acc'n) ->
run_swap inp (pc'n, set_remove Fin.eq_dec pc v, acc'n) st'n ->
run_swap inp (weaken_one pc, v, acc) st'n.
Inductive valid_inst {n} : inst -> fin n -> Prop :=
| valid_inst_add : forall t f, valid_inst (add, t) f
| valid_inst_nop : forall t f f',
valid_jump_t f t = Some f' -> valid_inst (nop, t) f
| valid_inst_jmp : forall t f f',
valid_jump_t f t = Some f' -> valid_inst (jmp, t) f.
(* An input is valid if all its instructions are valid. *)
Definition valid_input {n} (inp : input n) : Prop := forall (pc : fin n),
valid_inst (nth inp pc) pc.
Section ValidInput.
Variable n : nat.
Variable inp : input n.
Hypothesis Hv : valid_input inp.
Theorem valid_input_can_step : forall pc acc, exists pc' acc',
step_noswap (nth inp pc) (pc, acc) (pc', acc').
Proof.
intros pc acc.
destruct (nth inp pc) eqn:Hop.
destruct o.
- exists (FS pc). exists (M.add acc t0). apply step_noswap_add.
- exists (FS pc). exists acc. eapply step_noswap_nop.
- specialize (Hv pc). rewrite Hop in Hv. inversion Hv; subst.
exists f'. exists acc. eapply step_noswap_jmp. apply H0.
Qed.
(* A program is either done, stuck (at an invalid/visited address), or can step. *)
Theorem valid_input_progress : forall pc v acc,
(pc = nat_to_fin n /\ done inp (pc, v, acc)) \/
(exists pcs, pc = weaken_one pcs /\
((~ set_In pcs v /\ stuck inp (pc, v, acc)) \/
(exists pc' acc', set_In pcs v /\
step_noswap (nth inp pcs) (pcs, acc) (pc', acc')))).
Proof.
intros pc v acc.
(* Have we reached the end? *)
destruct (fin_big_or_small pc).
(* We're at the end, so we're done. *)
left. rewrite H. split. reflexivity. apply done_prog.
(* We're not at the end. *)
right. destruct H as [pcs H]. exists pcs. rewrite H. split. reflexivity.
(* We're not at the end. Is the PC valid? *)
destruct (set_In_dec Fin.eq_dec pcs v).
- (* It is. *)
right.
destruct (valid_input_can_step pcs acc) as [pc' [acc' Hstep]].
exists pc'; exists acc'; auto.
- (* It is not. *)
left. split; auto. apply stuck_prog; auto.
Qed.
Theorem list_length_induction {X : Type} (P : list X -> Prop) :
(forall l, (forall l', length l' < length l -> P l') -> P l) ->
forall l, P l.
Proof.
intros Hrec.
assert (forall (l l' : list X), length l' <= length l -> P l').
{ induction l; intros l' Hlen; apply Hrec; intros l'0 Hlen0.
- simpl in Hlen. lia.
- apply IHl. simpl in Hlen. lia. }
intros l. apply H with l. lia.
Qed.
Theorem set_remove_length : forall (f : fin n) (s : set (fin n)),
set_In f s ->
length (set_remove Fin.eq_dec f s) < length s.
Proof.
intros f s Hin.
induction s.
- inversion Hin.
- simpl. destruct (Fin.eq_dec f a) eqn:Heq.
+ unfold lt. apply le_n. (* Why couldn't lia get this one? *)
+ inversion Hin; subst. exfalso. apply n0. auto.
apply IHs in H. simpl. lia.
Qed.
Theorem valid_input_terminates : forall (pc : fin (S n)) (v : set (fin n)) (acc : t),
(exists pc', run_noswap inp (pc, v, acc) pc').
Proof.
intros pc v. generalize dependent pc.
induction v using list_length_induction.
intros pc acc.
destruct (valid_input_progress pc l acc) as [[_ Hd]|[pc' [Hw [[_ Hst]|[pc'' [acc'' [Hin Hst]]]]]]].
- (* We're done. *)
eexists. apply run_noswap_ok. assumption.
- (* We're stuck. *)
eexists. apply run_noswap_fail. assumption.
- (* We can make a step. This will remove our current PC from the valid list, *)
edestruct (H (set_remove Fin.eq_dec pc' l)).
(* Since the PC must be in the list, removing it makes the list smaller. *)
apply (set_remove_length _ _ Hin).
(* Without the current PC, our valid set shrinks.
Since this is the inductive step, we have assumed
that programs with smaller sets of valid PCs always
terminate. Thus, after we make the step, we're done. *)
exists x. subst. eapply run_noswap_trans.
+ auto.
+ apply Hst.
+ apply H0.
Qed.
End ValidInput.
End DayEight.

37
day9.cr Normal file
View File

@@ -0,0 +1,37 @@
require "advent"
INPUT = input(2020, 9).lines.map(&.to_i64)
def is_sum(is, from, to, n)
if to <= from
return n == 0_i64
end
return is_sum(is, from, to-1, n) || is_sum(is, from, to-1, n-is[to-1])
end
def part1(input)
is = input
i = 25
loop do
return is[i] unless is_sum(is, i-25, i, is[i])
i += 1
end
end
def part2(input, i)
input.each_with_index do |e, j|
next if e == i
acc = i-e
k = j
while acc > 0
k += 1
acc -= input[k]
end
if acc == 0
min, max = input[j..k].minmax
return min+max
end
end
end
p1 = part1(INPUT.clone)
puts part2(INPUT.clone, p1)

View File

@@ -1,48 +0,0 @@
class Graph(A)
def initialize
@edges = {} of A => Set({A, Int32})
end
def add_edge(f, t, c = 1)
@edges[f] ||= Set({A, Int32}).new
@edges[f] << {t, c}
end
def add_biedge(f, t, c = 1)
add_edge(f, t, c)
add_edge(t, f, c)
end
def find_path(f, t)
visited = Set(A).new
candidates = Set { f }
distances = {f => 0}
prev = {} of A => A
while !candidates.empty?
candidate = candidates.min_by { |c| distances[c] }
break if candidate == t
visited << candidate
candidates.delete candidate
dist = distances[candidate]
@edges.fetch(candidate, Set({A, Int32}).new).each do |e|
node, cost = e
new_dist = dist + cost
candidates << node unless visited.includes? node
next if (old_dist = distances[node]?) && old_dist < new_dist
distances[node] = new_dist
prev[node] = candidate
end
end
backtrack = t
path = [t] of A
while backtrack != f
return nil unless prev_bt = prev[backtrack]?
path << prev_bt
backtrack = prev_bt
end
{path.reverse!, distances[t]}
end
end

83
heap.cr
View File

@@ -1,83 +0,0 @@
class Array(T)
def bubble_up(i, &cmp)
return if i >= size
while i != 0
j = (i-1)//2
break if yield self[i], self[j]
self[i], self[j] = self[j], self[i]
i = j
end
end
def percalate_down(i, &cmp)
while i*2+1 < size
j1, j2 = i*2+1, i*2+2
v1 = self[j1]
v2 = self[j2]?
if v2 && (yield v1, v2) && (yield self[i], v2)
self[j2], self[i] = self[i], v2
i = j2
elsif yield self[i], v1
self[j1], self[i] = self[i], v1
i = j1
else
break
end
end
end
def heapify(&cmp : T,T -> Bool)
size.times do |i|
i = size - i - 1
bubble_up(i, &cmp)
end
self
end
def heapify
heapify do |i,j|
i < j
end
end
def heap_push(v, &cmp : T,T -> Bool)
self << v
bubble_up(size - 1, &cmp)
end
def heap_push(v)
heap_push(v) do |i,j|
i < j
end
end
def heap_pop(&cmp : T,T -> Bool)
self[0], self[size-1] = self[size-1], self[0]
v = pop
percalate_down(0, &cmp)
v
end
def heap_pop
heap_pop do |i, j|
i < j
end
end
def is_heap?(&cmp : T,T -> Bool)
(size-1).times do |i|
i = size - i - 1
vi = self[i]
vp = self[(i-1)//2]
return false unless (yield self[i], self[(i-1)//2]) || vi == vp
end
return true
end
def is_heap?
is_heap? do |i,j|
i < j
end
end
end

13
shard.yml Normal file
View File

@@ -0,0 +1,13 @@
name: advent-2020
version: 0.1.0
authors:
- your-name-here <your-email-here>
dependencies:
advent:
git: https://dev.danilafe.com/Advent-of-Code/advent
crystal: 0.35.1
license: MIT

View File

@@ -1,12 +1,11 @@
INPUT = File.read("dayN.txt")#.lines.map(&.chomp)
require "advent"
INPUT = input(2020, n)#.lines.map(&.to_i32)
def part1
input = INPUT.clone
def part1(input)
end
def part2
input = INPUT.clone
def part2(input)
end
part1
part2
puts part1(INPUT.clone)
puts part2(INPUT.clone)