Compare commits
No commits in common. "94ddabc590beb8095c66cdfe830ac29d5d55fc13" and "c17cf38795863224c36a4bbec47cc621dfb30fd7" have entirely different histories.
94ddabc590
...
c17cf38795
27
day15.cr
27
day15.cr
@ -2,14 +2,27 @@ require "advent"
|
|||||||
INPUT = input(2020, 15).split(",").map(&.to_i32)
|
INPUT = input(2020, 15).split(",").map(&.to_i32)
|
||||||
|
|
||||||
def run(input, times)
|
def run(input, times)
|
||||||
ls = {} of Int32 => Int32
|
ls = {} of Int32 => {Int32,Int32}
|
||||||
temp = 0
|
last = 0
|
||||||
(times-1).times do |i|
|
input.each_with_index do |n, i|
|
||||||
n = input[i]? || temp
|
ls[n] ||= {-1,-1}
|
||||||
temp = i - (ls[n]? || i)
|
f,s = ls[n]
|
||||||
ls[n] = i
|
ls[n] = {i,f}
|
||||||
|
last = n
|
||||||
end
|
end
|
||||||
return temp
|
count = input.size
|
||||||
|
while count < times
|
||||||
|
n = 0
|
||||||
|
if ls[last][1] != -1
|
||||||
|
n = ls[last][0] - ls[last][1]
|
||||||
|
end
|
||||||
|
last = n
|
||||||
|
ls[n] ||= {-1,-1}
|
||||||
|
f,s = ls[n]
|
||||||
|
ls[n] = {count,f}
|
||||||
|
count += 1
|
||||||
|
end
|
||||||
|
last
|
||||||
end
|
end
|
||||||
|
|
||||||
def part1(input)
|
def part1(input)
|
||||||
|
65
day16.cr
65
day16.cr
@ -1,65 +0,0 @@
|
|||||||
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
47
day17.cr
@ -1,47 +0,0 @@
|
|||||||
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
54
day18.cr
@ -1,54 +0,0 @@
|
|||||||
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
68
day19.cr
@ -1,68 +0,0 @@
|
|||||||
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})
|
|
166
day20.cr
166
day20.cr
@ -1,166 +0,0 @@
|
|||||||
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
31
day21.cr
@ -1,31 +0,0 @@
|
|||||||
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
66
day22.cr
@ -1,66 +0,0 @@
|
|||||||
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)
|
|
Loading…
Reference in New Issue
Block a user