### Add updated solutions, including day 20.

master
parent 77c91f8386
commit 32895b3e17
5 changed files with 320 additions and 44 deletions
1. 27
day15.cr
2. 49
day17.cr
3. 54
day18.cr
4. 68
day19.cr
5. 166
day20.cr

#### 27 day15.cr Unescape Escape View File

 `@ -2,27 +2,14 @@ require "advent"` `INPUT = input(2020, 15).split(",").map(&.to_i32) ` ``` ``` `def run(input, times)` ` ls = {} of Int32 => {Int32,Int32}` ` last = 0` ` input.each_with_index do |n, i|` ` ls[n] ||= {-1,-1}` ` f,s = ls[n]` ` ls[n] = {i,f}` ` last = n` ` ls = {} of Int32 => Int32` ` temp = 0` ` (times-1).times do |i|` ` n = input[i]? || temp` ` temp = i - (ls[n]? || i)` ` ls[n] = i` ` end` ` 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` ` return temp` `end` ``` ``` `def part1(input)`

#### 49 day17.cr Unescape Escape View File

 `@ -1,46 +1,47 @@` `require "advent"` `require "benchmark"` ``` ``` `INPUT = input(2020, 17).lines.map(&.chars) ` ``` ``` `def part1(input)` `def solve(input, dim)` ` step = input.clone` ` cubes = Set({Int32,Int32,Int32,Int32}).new` ` new_cubes = Set({Int32,Int32,Int32,Int32}).new` ` 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,0,0} if c == '#'` ` cubes << [x,y].concat([0] * (dim-2)) if c == '#'` ` end` ` end` ``` ``` ` 6.times do |i|` ` neighbor_count = {} of {Int32,Int32,Int32,Int32} => Int32` ` cubes.each do |c|` ` x,y,z,w = c` ` (-1..1).each do |dx|` ` (-1..1).each do |dy|` ` (-1..1).each do |dz|` ` (-1..1).each do |dw|` ` next if dx == 0 && dy == 0 && dz == 0 && dw == 0` ` neighbor_count[{x+dx,y+dy,z+dz,w+dw}] = (neighbor_count[{x+dx,y+dy,z+dz,w+dw}]? || 0) + 1` ` end` ` 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|` ` if cubes.includes?(n)` ` new_cubes << n if (i == 2 || i == 3)` ` elsif i == 3` ` new_cubes << n` ` end` ` 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` ``` ``` `puts part1(INPUT.clone)` `puts part2(INPUT.clone)` `(3..).each do |i|` ` print "Dim #{i} "` ` bm = Benchmark.measure { puts " #{solve(INPUT, i)}" }` ` puts bm.real * 1000` `end`

#### 54 day18.cr Unescape Escape 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 Unescape Escape 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})`

#### 166 day20.cr Unescape Escape 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)`