From 5c2f1eb45f480ba487d075832c59199a5b2fb2ff Mon Sep 17 00:00:00 2001 From: Alpha Chen Date: Thu, 23 Nov 2023 14:20:48 -0800 Subject: [PATCH] mu --- .gitmodules | 3 + 2021/j/day_01.j | 4 + 2021/ruby/day_19.rb | 52 ++++++++ 2021/ruby/day_23.rb | 35 ++++++ 2021/ruby/day_24.rb | 58 +++++++++ 2021/ruby/day_24.x.rb | 262 ++++++++++++++++++++++++++++++++++++++++ 2022/bqn/lib | 1 + 2022/fnl/day_01.fnl | 19 +++ 2022/ruby/.ruby-version | 2 +- 2022/ruby/day_17.rb | 89 ++++++++++++++ 2022/ruby/day_19.rb | 92 ++++++++++++++ 2022/ruby/day_22.rb | 78 ++++++++++++ 2022/ruby/day_24.rb | 93 ++++++++++++++ etc/total-time.rb | 12 ++ 14 files changed, 799 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100755 2021/j/day_01.j create mode 100644 2021/ruby/day_19.rb create mode 100644 2021/ruby/day_23.rb create mode 100644 2021/ruby/day_24.rb create mode 100644 2021/ruby/day_24.x.rb create mode 160000 2022/bqn/lib create mode 100644 2022/fnl/day_01.fnl create mode 100644 2022/ruby/day_17.rb create mode 100644 2022/ruby/day_19.rb create mode 100644 2022/ruby/day_22.rb create mode 100644 2022/ruby/day_24.rb create mode 100644 etc/total-time.rb diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f621a5c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "2022/bqn/lib"] + path = 2022/bqn/lib + url = https://github.com/mlochbaum/bqn-libs.git diff --git a/2021/j/day_01.j b/2021/j/day_01.j new file mode 100755 index 0000000..30e9560 --- /dev/null +++ b/2021/j/day_01.j @@ -0,0 +1,4 @@ +#!/usr/bin/env jconsole + +echo stdin +exit'' diff --git a/2021/ruby/day_19.rb b/2021/ruby/day_19.rb new file mode 100644 index 0000000..1cc64e2 --- /dev/null +++ b/2021/ruby/day_19.rb @@ -0,0 +1,52 @@ +require "matrix" +require "set" + +scanners = ARGF.read.strip.split("\n\n").to_h {|scanner| + id, *rest = scanner.split("\n") + id = id.scan(/\d+/)[0].to_i + coords = rest.map { _1.split(?,).map(&:to_i) } + [id, coords.map { Matrix.column_vector(_1) }] +} + +first_scanner = scanners.shift +origin_beacon = first_scanner[1].first +origin_beacon = Matrix.column_vector([-618,-824,-621]) + +known_scanners = { + first_scanner[0] => [ origin_beacon.map { -_1 }, Matrix.identity(3) ], +} +known_beacons = Set.new(first_scanner[1].map {|beacon| beacon - origin_beacon }) + +rot_x = Matrix[ [1, 0, 0], [0, 0, -1], [0, 1, 0] ] +rot_y = Matrix[ [0, 0, 1], [0, 1, 0], [-1, 0, 0] ] +rot_z = Matrix[ [0, -1, 0], [1, 0, 0], [0, 0, 1] ] +id = Matrix.identity(3) + +ROTATIONS = Set.new( + Array.new(4) { rot_x ** _1 }.flat_map {|x| + Array.new(4) { rot_y ** _1 }.flat_map {|y| + Array.new(4) { rot_z ** _1 }.map {|z| x * y * z }}} +) + +def find_overlapping_scanner(known_beacons, scanners) + scanners.filter_map {|id, beacons| + haystack = ROTATIONS.flat_map {|r| beacons.map {|b| [ r*b, r ] }} + # haystack.select { _2 == Matrix.identity(3) }.map(&:first).each { p _1 } + haystack.find {|position, rotation| + translated_beacons = beacons.map { (rotation * _1) - position } + (known_beacons & translated_beacons).size >= 12 + }&.then {|p,o| [id, p, o] } + }.first +end + +until scanners.empty? + id, position, orientation = find_overlapping_scanner(known_beacons, scanners) + + p [id, position, orientation] + + known_scanners[id] = [position, orientation] + translated_beacons = scanners[id].map {|b| b.zip(position).map { _2 - _1 }.zip(orientation).map { _1 * _2 }} + known_beacons.merge(translated_beacons) + scanners.delete(id) +end + diff --git a/2021/ruby/day_23.rb b/2021/ruby/day_23.rb new file mode 100644 index 0000000..10e624a --- /dev/null +++ b/2021/ruby/day_23.rb @@ -0,0 +1,35 @@ +############# +#...........# +###B#C#B#D### + #A#D#C#A# + ######### + +############# +#...........# +###D#D#A#A### + #C#C#B#B# + ######### + +State = Struct.new(:spaces, :energy) do + ENERGY = { A: 1, B: 10, C: 100, D: 1000 }.transform_keys(&:to_s) + ROOMS = [2, 4, 6, 8] + + def rooms = ROOMS.to_h { [_1, spaces.fetch(_1)] } + + def valid_moves + + end +end + +spaces = Array.new(11) { [] } +# input = "DC DC AB AB" +input = "BA CD BC DA" +input.split(" ").map(&:chars).each.with_index do |amphipods, i| + spaces[(i+1) * 2] = amphipods +end + +start = State.new(spaces, 0) + +p start +p start.rooms +p start.valid_moves diff --git a/2021/ruby/day_24.rb b/2021/ruby/day_24.rb new file mode 100644 index 0000000..0d78cc7 --- /dev/null +++ b/2021/ruby/day_24.rb @@ -0,0 +1,58 @@ +INSTRUCTIONS = ARGF.read.split("\n").map { _1.split(/\s+/) } + +# INSTRUCTIONS.each do |instruction, *args| +# a, b = *args +# case instruction +# when "inp" +# puts "#{a} = input.shift" +# when "add" +# puts "#{a} += #{b}" +# when "mul" +# puts "#{a} *= #{b}" +# when "div" +# puts "#{a} = (#{a} / #{b}.to_f).floor" +# when "mod" +# puts "#{a} %= #{b}" +# when "eql" +# puts "#{a} = #{a} == #{b} ? 1 : 0" +# else +# fail +# end +# end +# exit + +VARS = %w[ w x y z ] +def run(input) + vars = Hash.new(0) + + INSTRUCTIONS.each do |instruction, *args| + a, b = *args + b = VARS.include?(b) ? vars[b] : b.to_i + case instruction + when "inp" + vars[a] = input.shift + when "add" + vars[a] += b + when "mul" + vars[a] *= b + when "div" + vars[a] = (vars[a] / b.to_f).floor + when "mod" + vars[a] %= b + when "eql" + vars[a] = vars[a] == b ? 1 : 0 + else + fail + end + end + + vars +end + +99999999999999.downto(11111111111111).lazy.map(&:to_s).reject { _1.include?(?0) }.each do |input| + vars = run(input.chars.map(&:to_i)) + if vars[?z].zero? + puts input + exit + end +end diff --git a/2021/ruby/day_24.x.rb b/2021/ruby/day_24.x.rb new file mode 100644 index 0000000..2f0ea2d --- /dev/null +++ b/2021/ruby/day_24.x.rb @@ -0,0 +1,262 @@ +def run(input) + w, x, y, z = 0, 0, 0, 0 + + # w = input.shift + # x = z % 26 + # z = (z / 1.to_f).floor + # x += 13 + # x = x == w ? 0 : 1 + # y = 25 * x + 1 # either y = 26 or y = 1 + # z *= y # 0 + # # p [w, x, y, z] + + # y = (w + 13) * x + # z += y + # # p [w, x, y, z] + # # puts + + # w = input.shift + # x = z % 26 + # z = (z / 1.to_f).floor + # x += 11 + # x = x == w ? 0 : 1 + # y = 25 * x + 1 + # z *= y + # # p [w, x, y, z] + + # y = (w + 10) * x + # z += y + # # p [w, x, y, z] + # # puts + + # w = input.shift + # x = z % 26 + # z = (z / 1.to_f).floor + # x += 15 + # x = x == w ? 0 : 1 + # y = (25 * x) + 1 + # z *= y + # # p [w, x, y, z] + + # y = (w + 5) * x + # z += y + # # p [w, x, y, z] + # # puts + + # w = input.shift + # x = z % 26 + # z = (z / 26.to_f).floor + # x += -11 + # x = x == w ? 0 : 1 + # y = (25 * x) + 1 + # z *= y + # # p [w, x, y, z] + + # y = (w + 14) * x + # z += y + # # p [w, x, y, z] + # # puts + + # return [w, x, y, z] + + # w = input.shift + # x = z % 26 + # z = (z / 1.to_f).floor + # x += 14 + # x = x == w ? 0 : 1 + # y = 25 * x + 1 + # z *= y + # # p [w, x, y, z] + + # y = (w + 5) * x + # z += y + # # p [w, x, y, z] + # # puts + # # puts + + # w = input.shift + # x = z % 26 + # z = (z / 26.to_f).floor + # x += 0 + # x = x == w ? 0 : 1 + # y = 25 * x + 1 + # z *= y + # # p [w, x, y, z] + + # y = (w + 15) * x + # z += y + # # p [w, x, y, z] + # # puts + + # return [w, x, y, z] + + # w = input.shift + # x = z % 26 + # z = (z / 1.to_f).floor + # x += 12 + # x = x == w ? 0 : 1 + # y = 25 * x + 1 + # z *= y + # # p [w, x, y, z] + + # y *= 0 + # y += w + # y += 4 + # y *= x + # z += y + # # p [w, x, y, z] + + # w = input.shift + # x *= 0 + # x += z + # x %= 26 + # z = (z / 1.to_f).floor + # x += 12 + # x = x == w ? 1 : 0 + # x = x == 0 ? 1 : 0 + # y *= 0 + # y += 25 + # y *= x + # y += 1 + # z *= y + # # p [w, x, y, z] + + # y *= 0 + # y += w + # y += 11 + # y *= x + # z += y + # # p [w, x, y, z] + + # w = input.shift + # x *= 0 + # x += z + # x %= 26 + # z = (z / 1.to_f).floor + # x += 14 + # x = x == w ? 1 : 0 + # x = x == 0 ? 1 : 0 + # y *= 0 + # y += 25 + # y *= x + # y += 1 + # z *= y + # # p [w, x, y, z] + + # y *= 0 + # y += w + # y += 1 + # y *= x + # z += y + # # p [w, x, y, z] + # # puts + # # puts + + # w = input.shift + # x *= 0 + # x += z + # x %= 26 + # z = (z / 26.to_f).floor + # x += -6 + # x = x == w ? 1 : 0 + # x = x == 0 ? 1 : 0 + # y *= 0 + # y += 25 + # y *= x + # y += 1 + # z *= y + # # p [w, x, y, z] + + # y *= 0 + # y += w + # y += 15 + # y *= x + # z += y + # # p [w, x, y, z] + + # return [w, x, y, z] + + # w = input.shift + # x *= 0 + # x += z + # x %= 26 + # z = (z / 26.to_f).floor + # x += -10 + # x = x == w ? 1 : 0 + # x = x == 0 ? 1 : 0 + # y *= 0 + # y += 25 + # y *= x + # y += 1 + # z *= y + # # p [w, x, y, z] + + # y *= 0 + # y += w + # y += 12 + # y *= x + # z += y + # # p [w, x, y, z] + + # return [w, x, y, z] + + w = input.shift + x *= 0 + x += z + x %= 26 + z = (z / 26.to_f).floor + x += -12 + x = x == w ? 1 : 0 + x = x == 0 ? 1 : 0 + y *= 0 + y += 25 + y *= x + y += 1 + z *= y + # p [w, x, y, z] + + y *= 0 + y += w + y += 8 + y *= x + z += y + # p [w, x, y, z] + + return [w, x, y, z] + + w = input.shift + x = z % 26 + z = (z / 26.to_f).floor + x += -3 + x = x == w ? 0 : 1 + y = 25 * x + 1 + z *= y + # p [w, x, y, z] + + y = (w + 14) * x + z += y + # p [w, x, y, z] + + w = input.shift + x = z % 26 # z - 5 must be w + z = (z / 26.to_f).floor # z must be < 26 + x += -5 # w must be x - 5 + x = x == w ? 0 : 1 # x must be w + y = 25 * x + 1 + z *= y # z must be 0 + # p [w, x, y, z] + + y = (w + 9) * x # x must be 0 + z += y # y must be 0 + + [w, x, y, z] +end + +# 99999999999999.downto(11111111111111).lazy.map(&:to_s).reject { _1.include?(?0) }.each do |input| + vars = run(input.chars.map(&:to_i)) + p [input, vars[3]] if vars[3] < 400 + if vars[3].zero? + puts input + exit + end +end diff --git a/2022/bqn/lib b/2022/bqn/lib new file mode 160000 index 0000000..b90e26c --- /dev/null +++ b/2022/bqn/lib @@ -0,0 +1 @@ +Subproject commit b90e26c29b1742bf164b6121275b5e1ee8a56365 diff --git a/2022/fnl/day_01.fnl b/2022/fnl/day_01.fnl new file mode 100644 index 0000000..252a095 --- /dev/null +++ b/2022/fnl/day_01.fnl @@ -0,0 +1,19 @@ +(fn dbg [tbl] + (each [_ chunk (ipairs tbl)] + (print (accumulate [x "" _ i (ipairs chunk)] + (.. x ", " i))))) + +(local input (accumulate [input [[]] line (io.lines :../day_01.txt)] + (do + (if (= (length line) 0) + (table.insert input []) + (table.insert (. input (length input)) (tonumber line))) + input))) + +(fn sum [l] + (accumulate [sum 0 _ n (ipairs l)] + (+ sum n))) + +(local l (icollect [_ l (ipairs input)] (sum l))) +(table.sort l) +(print (. l (length l))) diff --git a/2022/ruby/.ruby-version b/2022/ruby/.ruby-version index 8c50098..a3ec5a4 100644 --- a/2022/ruby/.ruby-version +++ b/2022/ruby/.ruby-version @@ -1 +1 @@ -3.1 +3.2 diff --git a/2022/ruby/day_17.rb b/2022/ruby/day_17.rb new file mode 100644 index 0000000..6609b7c --- /dev/null +++ b/2022/ruby/day_17.rb @@ -0,0 +1,89 @@ +require "set" + +rocks = <<~ROCKS.split("\n\n") + #### + + .#. + ### + .#. + + ..# + ..# + ### + + # + # + # + # + + ## + ## +ROCKS +rocks = rocks + .map {|rock| rock.lines(chomp: true).reverse.map(&:chars) } + .map {|rock| + rock.each.with_index.with_object(Set.new) {|(row, y), rocks| + row.each.with_index do |c, x| + rocks << [x, y] if c == ?# + end + } + } +rocks = rocks.cycle + +jet_pattern = ARGF.read.strip.chars +jet_pattern = jet_pattern.cycle + +chamber = Hash.new {|_,(x,y)| !(0...7).cover?(x) || y.negative? } + +def move_rock(chamber, rock, dx, dy) + next_rock = rock.map {|x,y| [x+dx, y+dy] } + + if next_rock.any? {|x,y| chamber[[x, y]] } + rock + else + next_rock + end +end + +def debug(chamber, rock) + max_y = [chamber.keys.map(&:last).max || 0, rock.map(&:last).max || 0].max + + puts + puts max_y.downto(0).map {|y| + (0...7).map {|x| + (chamber[[x,y]] || rock.include?([x,y])) ? ?# : ?. + }.join + }.join("\n") +end + +2022.times do + rock = rocks.next + + y = chamber.empty? ? 3 : chamber.keys.map(&:last).max + 4 + x = 2 + rock = move_rock(chamber, rock, x, y) + + loop do + rock = case jet = jet_pattern.next + when ?< then move_rock(chamber, rock, -1, 0) + when ?> then move_rock(chamber, rock, 1, 0) + else fail "invalid jet pattern: #{jet}" + end + + next_rock = move_rock(chamber, rock, 0, -1) + break if rock == next_rock + rock = next_rock + end + + chamber.merge!(rock.to_h { [_1, true] }) +end + +# debug(chamber, Set.new) +max_y = chamber.keys.map(&:last).max + 1 +p max_y + +candidates = (0..max_y).select {|y| (0...7).count {|x| chamber[[x,y]] } == 6 } +# max_delta = candidates.each_cons(2).map { _2 - _1 }.max +candidates. +p candidates +p max_delta diff --git a/2022/ruby/day_19.rb b/2022/ruby/day_19.rb new file mode 100644 index 0000000..c787d46 --- /dev/null +++ b/2022/ruby/day_19.rb @@ -0,0 +1,92 @@ +SEEN = [] + +class Factory + attr_reader :time, :robots, :resources + + def initialize( + blueprint, + time=0, + robots={ ore: 1, clay: 0, obsidian: 0, geode: 0 }, + resources={ ore: 0, clay: 0, obsidian: 0, geode: 0 } + ) + @blueprint = blueprint + @time = time + @robots = robots + @resources = resources + end + + def max_geodes + if @time == 20 + pp self if @robots.values_at(:ore, :clay, :obsidian, :geode) == [1, 4, 2, 1] + return @resources.fetch(:geode) + end + + resources = @resources.merge(@robots) { _2 + _3 } + branches = @blueprint.filter_map {|robot, cost| + if cost.all? {|res, qty| @resources.fetch(res) >= qty } + Factory.new( + @blueprint, + @time + 1, + @robots.merge({robot => 1}) { _2 + _3 }, + resources.merge(cost) { _2 - _3 }, + ) + else + nil + end + } + + if @blueprint.any? {|_, cost| cost.any? {|res, qty| @robots.fetch(res) > 0 && @resources.fetch(res) < qty }} + pp self + branches << Factory.new(@blueprint, @time + 1, @robots, resources) + end + + branches = branches.reject {|factory| + SEEN.any? {|other| + %i[ ore clay obsidian geode ].all? {|res| + factory.robots.fetch(res) <= other.robots.fetch(res) && + factory.resources.fetch(res) <= other.resources.fetch(res) && + factory.time >= other.time + } + } + } + SEEN.concat(branches) + + return @resources.fetch(:geode) if branches.empty? + + branches.map(&:max_geodes).max + end + + def to_s + "" + end + alias_method :inspect, :to_s +end + +blueprints = ARGF.read + .split("\n\n").map { _1.lines(chomp: true).join } + .map { _1.scan(/\d+/).map(&:to_i) } + .map { + _1.shift # id + { + ore: {ore: _1.shift }, + clay: {ore: _1.shift }, + obsidian: {ore: _1.shift, clay: _1.shift }, + geode: {ore: _1.shift, obsidian: _1.shift }, + } + } + +p Factory.new(blueprints[0]).max_geodes +p SEEN.count + +__END__ +Blueprint 1: + Each ore robot costs 4 ore. + Each clay robot costs 2 ore. + Each obsidian robot costs 3 ore and 14 clay. + Each geode robot costs 2 ore and 7 obsidian. + +Blueprint 2: + Each ore robot costs 2 ore. + Each clay robot costs 3 ore. + Each obsidian robot costs 3 ore and 8 clay. + Each geode robot costs 3 ore and 12 obsidian. diff --git a/2022/ruby/day_22.rb b/2022/ruby/day_22.rb new file mode 100644 index 0000000..faa42b4 --- /dev/null +++ b/2022/ruby/day_22.rb @@ -0,0 +1,78 @@ +require "strscan" + +include Math + +map, path = ARGF.read.chomp.split("\n\n") + +map = map.split("\n").each.with_index.with_object({}) {|(row,y),map| + row.chars.each.with_index do |tile,x| + map[[y,x]] = tile if tile != " " + end +} +max_y = map.keys.map(&:first).max +max_x = map.keys.map(&:last).max + +start_x = map.keys.select { _1.first.zero? }.map(&:last).min + +current = [0, start_x] +facing = 0 + +ss = StringScanner.new(path) +until ss.eos? + case + when n = ss.scan(/\d+/) + n = n.to_i + n.times do + y, x = current.zip([-sin(facing),cos(facing)]).map { _1 + _2 }.map(&:to_i) + next_tile = map[[y,x]] + + unless next_tile + puts + p [y,x, facing] + y, x = case facing + when 0 then [y, 0] + when PI/2 then [max_y, x] + when PI then [y, max_x] + when 3*PI/2 then [0, x] + else fail "unexpected facing: #{facing}" + end + next_tile = map[[y,x]] + while next_tile.nil? + y, x = [y, x].zip([-sin(facing),cos(facing)]).map { _1 + _2 }.map(&:to_i) + next_tile = map[[y,x]] + end + + p [y,x,next_tile] + end + + case next_tile + when ?. + current = [y,x] + when ?# + break + else + fail "unexpected tile: #{next_tile}" + end + end + when dir = ss.scan(/R|L/) + facing += case dir + when ?R then -PI/2 + when ?L then PI/2 + else fail "unexpected dir: #{dir}" + end + facing %= 2*PI + else + fail ss.rest + end +end + +row, col = current.map { _1 + 1 } +facing = case facing + when 0 then 0 + when PI/2 then 3 + when PI then 2 + when 3*PI/2 then 2 + else + fail "unexpected facing: #{facing}" + end +p [1000, 4, 1].zip([row, col, facing]).sum { _1 * _2 } diff --git a/2022/ruby/day_24.rb b/2022/ruby/day_24.rb new file mode 100644 index 0000000..d13a393 --- /dev/null +++ b/2022/ruby/day_24.rb @@ -0,0 +1,93 @@ +require "set" + +walls = Set.new +blizzards = Hash.new {|h,k| h[k] = Set.new } +clear_ground = Set.new + +ARGF.read.lines(chomp: true).each.with_index do |row, y| + row.chars.each.with_index do |pos,x| + case pos + when ?# + walls.add([y,x]) + when /[v^<>]/ + blizzards[pos].add([y,x]) + when ?. + clear_ground.add([y,x]) + else + fail pos + end + end +end + +x_range = Range.new(*walls.map(&:last).minmax) +y_range = Range.new(*walls.map(&:first).minmax) + +debug = ->() do + puts + puts y_range.map {|y| + x_range.map {|x| + if walls.include?([y,x]) + ?# + elsif blizzards.any? {|_,pos| pos.include?([y,x])} + b = blizzards.select {|_,pos| pos.include?([y,x])} + (b.size == 1) ? b.keys[0] : b.size + else + ?. + end + }.join + }.join("\n") +end + +start = [0, clear_ground.find {|y,x| y == 0 }.last] +goal = [y_range.end, clear_ground.find {|y,x| y == y_range.end }.last] + +dir_deltas = { + ?^ => [-1, 0], + ?v => [ 1, 0], + ?< => [ 0, -1], + ?> => [ 0, 1], +} +moves = [*dir_deltas.values, [0, 0]] + +frontier = [start] +(1..).each do |time| + if (time % 1).zero? + puts time + # p frontier + # debug.() + end + + blizzards = blizzards.to_h {|dir,positions| + delta = dir_deltas.fetch(dir) + edges, positions = positions + .map {|pos| pos.zip(delta).map { _1 + _2 }} + .partition { walls.include?(_1) } + positions.concat(edges.map {|(y,x)| + case dir + when ?^ then [y_range.end - 1, x] + when ?v then [y_range.begin + 1, x] + when ?< then [y, x_range.end - 1] + when ?> then [y, x_range.end + 1] + else fail dir + end + }) + [dir, positions] + } + + clear_ground = y_range.map {|y| x_range.map {|x| [y, x] }}.flatten(1) + .reject {|pos| walls.include?(pos) || blizzards.any? { _2.include?(pos) }} + + options = frontier.map {|current| + moves.filter_map {|delta| + pos = current.zip(delta).map { _1 + _2 } + clear_ground.include?(pos) && pos + } + }.flatten(1) + + frontier = options.uniq + + if frontier.include?(goal) + puts time + 1 + exit + end +end diff --git a/etc/total-time.rb b/etc/total-time.rb new file mode 100644 index 0000000..0193583 --- /dev/null +++ b/etc/total-time.rb @@ -0,0 +1,12 @@ +seconds = ARGF.read.strip.lines(chomp: true) + .sum {|line| + line.strip.split(/\s+/).fetch(4) + .split(?:).map(&:to_i) + .zip([60*60, 60, 1]) + .sum { _1 * _2 } + } + +hours, seconds = seconds.divmod(60 * 60) +minutes, seconds = seconds.divmod(60) + +puts "#{hours} hours, #{minutes} minutes, #{seconds} seconds"