Playlist https://www.youtube.com/playlist?list=PLS6F722u-R6KYlGyUv65EFpGKl2Esmurr
Last active
December 18, 2023 16:04
-
-
Save cjavdev/d15a2a4ffed6c840c2fb28a093e9f927 to your computer and use it in GitHub Desktop.
Advent of Code 2023
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Part 1 | |
# input = <<~INPUT | |
# 1abc2 | |
# pqr3stu8vwx | |
# a1b2c3d4e5f | |
# treb7uchet | |
# INPUT | |
# | |
# result = DATA.readlines.map do |line| | |
# digits = line.scan(/\d/) | |
# (digits.first + digits.last).to_i | |
# end.sum | |
# p result | |
# Part 2 | |
input = <<~INPUT | |
two1nine | |
eightwothree | |
abcone2threexyz | |
xtwone3four | |
4nineeightseven2 | |
zoneight234 | |
7pqrstsixteen | |
INPUT | |
WORD_TO_DIGIT = { | |
"one" => "1", | |
"two" => "2", | |
"three" => "3", | |
"four" => "4", | |
"five" => "5", | |
"six" => "6", | |
"seven" => "7", | |
"eight" => "8", | |
"nine" => "9", | |
"1" => "1", | |
"2" => "2", | |
"3" => "3", | |
"4" => "4", | |
"5" => "5", | |
"6" => "6", | |
"7" => "7", | |
"8" => "8", | |
"9" => "9", | |
"0" => "0", | |
} | |
words = WORD_TO_DIGIT.keys | |
r_words = words.map(&:reverse) | |
result = DATA.readlines.map do |line| | |
digits = line.match(/(#{words.join("|")})/, 0) | |
first = WORD_TO_DIGIT[digits[0]] | |
digits = line.reverse.match(/(#{r_words.join("|")})/, 0) | |
last = WORD_TO_DIGIT[digits[0].reverse] | |
(first + last).to_i | |
end.sum | |
p result | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green | |
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue | |
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red | |
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red | |
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green | |
INPUT | |
class Game | |
attr_reader :id, :rounds | |
def self.parse(line) | |
first, *last = line.chomp.split(/[:;]/) | |
id = first.split.last.to_i | |
rounds = last.map do |str_round| | |
str_round.split(',').map do |str_card| | |
count, color = str_card.split | |
[color.to_sym, count.to_i] | |
end.to_h | |
end | |
new(id, rounds) | |
end | |
def initialize(id, rounds) | |
@id = id | |
@rounds = rounds | |
end | |
def max_cubes | |
rounds | |
.each_with_object({ | |
red: 0, | |
blue: 0, | |
green: 0 | |
}) do |round, counts| | |
round.each do |color, count| | |
counts[color] = count if count > counts[color] | |
end | |
end | |
end | |
def max_power | |
max_cubes.values.inject(:*) | |
end | |
def possible? | |
max_cubes in { | |
red: ..12, | |
green: ..13, | |
blue: ..14, | |
} | |
end | |
end | |
data = input.each_line | |
r = data.map do |line| | |
g = Game.parse(line) | |
g.max_power | |
# p line | |
# p g.max_cubes | |
# p g.max_power | |
# if g.possible? | |
# g.id | |
# else | |
# 0 | |
# end | |
end | |
p r.sum | |
__END__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "set" | |
input = <<~INPUT | |
467..114.. | |
...*...... | |
..35..633. | |
......#... | |
617*...... | |
.....+.58. | |
..592..... | |
......755. | |
...$.*.... | |
.664.598.. | |
INPUT | |
puts input | |
rows = input.each_line.map(&:chomp) | |
# rows = DATA.readlines.map(&:chomp) | |
syms = [] | |
rows.each_with_index do |row, x| | |
row.each_char.with_index do |char, y| | |
# next if char == "." | |
# next if char =~ /\d/ | |
# p [char, x, y] | |
syms << [x, y] if char == "*" | |
end | |
end | |
sum = 0 | |
syms.each do |(x, y)| | |
number_starts = Set.new | |
[ | |
[-1, -1], [-1, 0], [-1, 1], | |
[0, -1], [0, 1], | |
[1, -1], [1, 0], [1, 1], | |
].each do |(dx, dy)| | |
nx = x + dx | |
ny = y + dy | |
if rows[nx][ny] =~ /\d/ | |
nny = ny | |
while rows[nx][nny - 1] =~ /\d/ | |
nny -= 1 | |
end | |
number_starts << [nx, nny] | |
end | |
end | |
if number_starts.length == 2 | |
result = number_starts.map do |(x, y)| | |
rows[x][y..].to_i | |
end.inject(:*) | |
sum += result | |
end | |
end | |
p sum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<<~INPUT => input | |
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 | |
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 | |
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 | |
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 | |
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 | |
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11 | |
INPUT | |
# input.each_line => data | |
DATA.readlines => data | |
card_counts = Hash.new {|h, k| h[k] = 1} | |
data.map do |card| | |
card.split => _, id, *numbers | |
id.to_i => id | |
numbers => *winning, "|", *ours | |
(winning & ours).size => match_count | |
card_counts[id].times do | |
match_count.times do |i| | |
card_counts[id + i + 1] += 1 | |
end | |
end | |
match_count > 0 ? 2 ** (match_count - 1) : 0 | |
end => scores | |
p card_counts.values.sum | |
p scores.sum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'set' | |
input = <<~INPUT | |
seeds: 79 14 55 13 | |
seed-to-soil map: | |
50 98 2 | |
52 50 48 | |
soil-to-fertilizer map: | |
0 15 37 | |
37 52 2 | |
39 0 15 | |
fertilizer-to-water map: | |
49 53 8 | |
0 11 42 | |
42 0 7 | |
57 7 4 | |
water-to-light map: | |
88 18 7 | |
18 25 70 | |
light-to-temperature map: | |
45 77 23 | |
81 45 19 | |
68 64 13 | |
temperature-to-humidity map: | |
0 69 1 | |
1 0 69 | |
humidity-to-location map: | |
60 56 37 | |
56 93 4 | |
INPUT | |
input = DATA.read | |
# class Range | |
# def overlaps?(other) | |
# cover?(other.first) || other.cover?(first) | |
# end | |
# | |
# def intersection(other) | |
# return nil if (self.max < other.begin or other.max < self.begin) | |
# [self.begin, other.begin].max..[self.max, other.max].min | |
# end | |
# | |
# alias_method :&, :intersection | |
# end | |
s, *maps = input.split(/\n\n/) | |
_, *seeds = s.split.map(&:to_i) | |
maps = maps.map do|m| | |
label, *rows = m.split(/\n/) | |
rows.map {_1.split.map(&:to_i)} | |
end | |
def find(page, target) | |
page.each do |ds, ss, len| | |
if target >= ss && target < ss + len | |
return ds + (target - ss) | |
end | |
end | |
target | |
end | |
seeds.map do |seed| | |
s = seed | |
maps.each do |m| | |
s = find(m, s) | |
end | |
s | |
end => r | |
p r.min | |
### PART 2 | |
class Range | |
def overlaps?(other) | |
cover?(other.first) || other.cover?(first) | |
end | |
def intersection(other) | |
return nil if (self.max < other.begin or other.max < self.begin) | |
[self.begin, other.begin].max..[self.max, other.max].min | |
end | |
alias_method :&, :intersection | |
end | |
s, *maps = input.split(/\n\n/) | |
_, *seeds = s.split.map(&:to_i) | |
maps = maps.map do|m| | |
label, *rows = m.split(/\n/) | |
rows.map {_1.split.map(&:to_i)} | |
end | |
maps = maps.map do |m| | |
m2 = m.map do |dest, src, len| | |
[ | |
src, | |
src + len - 1, | |
dest - src | |
] | |
end.sort | |
if m2[0][0] != 0 | |
m2.unshift([0, m2[0][0] - 1, 0]) | |
else | |
m2 | |
end | |
end | |
maps = maps.map do |m| | |
src_end = m[-1][1] | |
end_cap = [ | |
src_end + 1, | |
src_end + 1_000_000_000_000, | |
0 | |
] | |
m + [end_cap] | |
end | |
def convert(page, seed_range) | |
page.filter_map do |src_start, src_end, diff| | |
if seed_range.overlaps?(src_start...src_end) | |
intersection = seed_range & (src_start...src_end) | |
rng_start = intersection.begin + diff | |
rng_end = intersection.end + diff | |
(rng_start..rng_end) | |
end | |
end | |
end | |
current_ranges = Array.new(8) { Set.new } | |
seeds.each_slice(2).map do |seed_start, len| | |
seed_range = (seed_start...seed_start + len) | |
current_ranges[0] << seed_range | |
maps.each_with_index do |m, i| | |
current_ranges[i].each do |current_range| | |
current_ranges[i + 1] += convert(m, current_range) | |
end | |
end | |
end | |
p current_ranges.last.map(&:min).min | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
time = %w(7 15 30).map(&:to_i) | |
distance = %w(9 40 200).map(&:to_i) | |
races = time.zip(distance) | |
races.reduce(1) do |acc, (time, record_distance)| | |
ways = 0 | |
time.times do |seconds_held| | |
if seconds_held * (time - seconds_held) > record_distance | |
ways += 1 | |
end | |
end | |
acc * ways | |
end => result | |
p result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
32T3K 765 | |
T55J5 684 | |
KK677 28 | |
KTJJT 220 | |
QQQJA 483 | |
INPUT | |
data = input.each_line | |
# data = DATA.readlines | |
class Hand | |
include Comparable | |
attr_reader :name, :cards, :bid | |
def self.parse(line) | |
c, b = line.split | |
bid = b.to_i | |
cards = c.chars.map do |char| | |
{ | |
"A" => 14, | |
"K" => 13, | |
"Q" => 12, | |
# "J" => 11, | |
"J" => 1, | |
"T" => 10, | |
}[char] || char.to_i | |
end | |
new(cards, c, bid) | |
end | |
def initialize(cards, name, bid) | |
@name = name | |
@cards = cards | |
@bid = bid | |
end | |
def <=>(other) | |
[type, cards] <=> [other.type, other.cards] | |
end | |
def type | |
joker_count = cards.count(1) | |
non_joker_cards = cards.select {|c| c != 1 } | |
card_counts = non_joker_cards | |
.group_by(&:itself) | |
.transform_values(&:count) | |
if card_counts.values.any? {|v| v == 5 } | |
5 | |
elsif card_counts.values.any? {|v| v == 4 } | |
4 + joker_count | |
elsif card_counts.values.any? {|v| v == 3 } && card_counts.values.any? {|v| v == 2 } | |
3.5 | |
elsif card_counts.values.any? {|v| v == 3 } | |
3 + joker_count | |
elsif card_counts.values.count {|v| v == 2 } == 2 | |
2.5 + joker_count | |
elsif card_counts.values.any? {|v| v == 2 } | |
2 + joker_count | |
else | |
[1 + joker_count, 5].min | |
end | |
end | |
end | |
p data | |
.map { Hand.parse(_1) } | |
.sort | |
.map | |
.with_index {_1.bid * (_2 + 1)} | |
.sum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
LR | |
11A = (11B, XXX) | |
11B = (XXX, 11Z) | |
11Z = (11B, XXX) | |
22A = (22B, XXX) | |
22B = (22C, 22C) | |
22C = (22Z, 22Z) | |
22Z = (22B, 22B) | |
XXX = (XXX, XXX) | |
INPUT | |
data = input.each_line.map(&:chomp) | |
data = DATA.readlines.map(&:chomp) | |
turns, _, *n = data | |
turns = turns.chars | |
nodes = n.map do |node| | |
name, left, right = node.scan(/\w+/) | |
[name, [left, right]] | |
end.to_h | |
def find(cur, nodes, turns, &blk) | |
step = 0 | |
while !blk.call(cur) | |
if turns[step % turns.length] == "L" | |
cur, _ = nodes[cur] | |
else | |
_, cur = nodes[cur] | |
end | |
step += 1 | |
end | |
step | |
end | |
p find("AAA", nodes, turns) {|c| c == "ZZZ"} | |
p nodes | |
.keys | |
.select {|k| k.end_with?("A") } | |
.map {|k| find(k, nodes, turns) {|c| c.end_with?("Z")} } | |
.inject(&:lcm) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
0 3 6 9 12 15 | |
1 3 6 10 15 21 | |
10 13 16 21 30 45 | |
INPUT | |
data = input.each_line | |
#data = DATA.readlines | |
stats = data.map do |line| | |
line.split.map(&:to_i) | |
end | |
def predict(stat) | |
layers = [stat] | |
until stat.all?(&:zero?) | |
stat = stat.each_cons(2).map { _2 - _1 } | |
layers.unshift(stat) | |
end | |
layers.each_cons(2) do |(p1, *, p2), nxt| | |
nxt.unshift(nxt.first - p1) | |
nxt.push(nxt.last + p2) | |
end | |
layers.last | |
end | |
# p stats.map {|stat| predict(stat)}.map(&:last).sum | |
p stats.map {|stat| predict(stat)}.map(&:first).sum | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'set' | |
input = <<~INPUT | |
FF7FSF7F7F7F7F7F---7 | |
L|LJ||||||||||||F--J | |
FL-7LJLJ||||||LJL-77 | |
F--JF--7||LJLJ7F7FJ- | |
L---JF-JLJ.||-FJLJJ7 | |
|F|F-JF---7F7-L7L|7| | |
|FFJF7L7F-JF7|JL---7 | |
7-L-JL7||F7|L7F-7F7| | |
L.L7LFJ|||||FJL7||LJ | |
L7JLJL-JLJLJL--JLJ.L | |
INPUT | |
data = input.each_line.map(&:chomp) | |
data = DATA.readlines.map(&:chomp) | |
grid = data.map(&:chars) | |
start = nil | |
grid.each_with_index do |row, x| | |
row.each_with_index do |cell, y| | |
if cell == 'S' | |
start = [x, y] | |
end | |
end | |
end | |
p start | |
DIRS = { | |
"J" => [[-1, 0], [0, -1]], | |
"L" => [[-1, 0], [0, 1]], | |
"7" => [[0, -1], [1, 0]], | |
"F" => [[0, 1], [1, 0]], | |
"|" => [[-1, 0], [1, 0]], | |
"-" => [[0, -1], [0, 1]], | |
"." => [], | |
"S" => [], | |
} | |
def neighbors(grid, point) | |
x, y = point | |
pipe = grid[x][y] | |
DIRS[pipe].map do |dx, dy| | |
[x + dx, y + dy] | |
end | |
end | |
start_exits = [] | |
[[-1, 0], [1, 0], [0, -1], [0, 1]].each do |dx, dy| | |
s_neighbor = [start[0] + dx, start[1] + dy] | |
if neighbors(grid, s_neighbor).include?(start) | |
start_exits << s_neighbor | |
end | |
end | |
start_point = start_exits.first | |
pipe = [start, start_point].to_set | |
while start_point != start_exits.last | |
ns = neighbors(grid, start_point) | |
ns.each do |neighbor| | |
if !pipe.include?(neighbor) | |
pipe << neighbor | |
start_point = neighbor | |
end | |
end | |
end | |
puts "Part 1, #{ pipe.size / 2 }" | |
clean_grid = Array.new(grid.size) { Array.new(grid.first.size, ' ') } | |
pipe.each do |(x, y)| | |
clean_grid[x][y] = grid[x][y] | |
end | |
inside_cells = [] | |
inside = 0 | |
clean_grid.each_with_index do |row, x| | |
print "#{ x }: " | |
row.each_with_index do |cell, y| | |
print cell | |
# skip cells on the pipeline | |
next if pipe.include?([x, y]) | |
north = 0 | |
south = 0 | |
(y..row.size).each do |y2| | |
# Count north facing blockers (hacked S!) | |
if ["J", "L", "|", "S"].include?(clean_grid[x][y2]) | |
north += 1 | |
end | |
# Count south facing blockers (hacked S!) | |
if ["F", "7", "|", "S"].include?(clean_grid[x][y2]) | |
south += 1 | |
end | |
end | |
if [north, south].min.odd? | |
inside += 1 | |
inside_cells << [x, y] | |
end | |
end | |
print "$\n" | |
end | |
p inside |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
...#...... | |
.......#.. | |
#......... | |
.......... | |
......#... | |
.#........ | |
.........# | |
.......... | |
.......#.. | |
#...#..... | |
INPUT | |
data = input.each_line.map(&:chomp) | |
data = DATA.readlines.map(&:chomp) | |
grid = data.map { |line| line.chars } | |
empty_rows = grid | |
.filter_map | |
.with_index do |row, x| | |
if row.all? { |cell| cell == '.' } | |
x | |
end | |
end | |
empty_cols = grid | |
.transpose | |
.filter_map | |
.with_index do |col, y| | |
if col.all? { |cell| cell == '.' } | |
y | |
end | |
end | |
galaxies = [] | |
grid.each_with_index do |row, x| | |
row.each_with_index do |cell, y| | |
if cell == '#' | |
galaxies << [x, y] | |
end | |
end | |
end | |
def distance(a, b, empty_rows, empty_cols) | |
ax, ay = a | |
bx, by = b | |
d = (ax - bx).abs + (ay - by).abs | |
x_min, x_max = [ax, bx].minmax | |
y_min, y_max = [ay, by].minmax | |
(x_min...x_max).each do |x| | |
if empty_rows.include?(x) | |
# part 1 | |
# d += 2 - 1 | |
d += 1_000_000 - 1 | |
end | |
end | |
(y_min...y_max).each do |y| | |
if empty_cols.include?(y) | |
d += 1_000_000 - 1 | |
end | |
end | |
d | |
end | |
result = galaxies | |
.combination(2) | |
.inject(0) do |sum, (a, b)| | |
sum + distance(a, b, empty_rows, empty_cols) | |
end | |
p result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rspec/autorun' | |
input = <<~INPUT | |
???.### 1,1,3 | |
.??..??...?##. 1,1,3 | |
?#?#?#?#?#?#?#? 1,3,1,6 | |
????.#...#... 4,1,1 | |
????.######..#####. 1,6,5 | |
?###???????? 3,2,1 | |
INPUT | |
def expand(record) | |
([record] * 5).join("?") | |
end | |
def parse2(line) | |
record, sizes = line.split(' ') | |
sizes = sizes.split(',').map(&:to_i) * 5 | |
record = "#{expand(record)}.".gsub(/\.+/, '.') | |
[record, sizes] | |
end | |
def parse(line) | |
record, sizes = line.split(' ') | |
sizes = sizes.split(',').map(&:to_i) | |
record = "#{record}." | |
[record, sizes] | |
end | |
def count(record, sizes, group_size = 0, cache = {}) | |
key = [record, sizes, group_size] | |
if cache[key] | |
return cache[key] | |
end | |
if sizes.any? { |s| s - group_size > record.length } | |
return cache[key] = 0 | |
end | |
if sizes.empty? | |
if !record.include?("#") | |
return cache[key] = 1 | |
else | |
return cache[key] = 0 | |
end | |
end | |
current, *rest = record.chars | |
s = sizes.first | |
case [current, group_size] | |
in ['?', _] | |
# with both the `.` and the `#` instead of the question mark | |
return cache[key] = count('#' + rest.join, sizes, group_size, cache) + count('.' + rest.join, sizes, group_size, cache) | |
in ['#', _] | |
# keep moving forward through the group of brokens # | |
return cache[key] = count(rest.join, sizes, group_size + 1, cache) | |
in ['.', ^s] | |
# If we found the end of the group because group_size == sizes.first | |
return cache[key] = count(rest.join, sizes[1..], 0, cache) | |
in ['.', (1..)] | |
# Invalid group size / not enough #s | |
return cache[key] = 0 | |
in ['.', 0] | |
# no-op - keep moving forward | |
return cache[key] = count(rest.join, sizes, 0, cache) | |
end | |
end | |
data = input.each_line | |
# data = DATA.readlines | |
puts "Record Count: #{data.count}" | |
counter = 0 | |
data.inject(0) do |sum, line| | |
if counter % 5 == 0 | |
puts counter | |
end | |
counter += 1 | |
record, sizes = parse2(line) | |
sum + count(record, sizes) | |
end => r | |
puts "Part 2: #{r}" | |
describe 'count' do | |
it 'parses as expected' do | |
expect(parse2("???.### 1,1,3")).to eq([ | |
"???.###????.###????.###????.###????.###.", | |
[1,1,3,1,1,3,1,1,3,1,1,3,1,1,3] | |
]) | |
end | |
it 'works on basic cases' do | |
cases = [ | |
["# 1", 1], | |
[".# 1", 1], | |
["#. 1", 1], | |
["#. 2", 0], | |
["???.### 1,1,3", 1], | |
[".??..??...?##. 1,1,3", 4], | |
["?#?#?#?#?#?#?#? 1,3,1,6", 1], | |
["????.#...#... 4,1,1", 1], | |
["????.######..#####. 1,6,5", 4], | |
["?###???????? 3,2,1", 10], | |
] | |
cases.each do |input, expected| | |
expect(count(*parse(input))).to eq(expected), -> { p input } | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rspec/autorun' | |
require 'byebug' | |
puts 'Hello, World!' | |
input = <<~INPUT | |
#.##..##. | |
..#.##.#. | |
##......# | |
##......# | |
..#.##.#. | |
..##..##. | |
#.#.##.#. | |
#...##..# | |
#....#..# | |
..##..### | |
#####.##. | |
#####.##. | |
..##..### | |
#....#..# | |
INPUT | |
def around(line, i) | |
return false if i == 0 | |
left, right = line.partition.with_index {|x, j| j < i } | |
len = [left.length, right.length].min | |
return false if len == 0 | |
left.reverse[0...len].zip(right[0...len]).count {|l, r| l != r } == 0 | |
end | |
# Go through the first row and see if it has any lines of symetry | |
# check to see if those lines of symetry match any lines from the next | |
# row and narrow down to a smaller list. | |
def points_of_symetry(line) | |
(0...line.length).filter_map do |i| | |
if around(line, i) | |
i | |
end | |
end | |
end | |
def line_of_symetry(rows) | |
rows.map do |line| | |
points_of_symetry(line) | |
end.inject(&:&) | |
end | |
input = DATA.read | |
patterns = input.split("\n\n").map {|pat| pat.split("\n").map(&:chars)} | |
answer1 = 0 | |
answer2 = 0 | |
patterns.each_with_index do |pattern, k| | |
# Part 1 | |
un_cols = line_of_symetry(pattern).first | |
un_rows = line_of_symetry(pattern.transpose).first | |
answer1 += un_cols.to_i + (un_rows.to_i * 100) | |
# Part 2 | |
col_candidates = Set.new | |
row_candidates = Set.new | |
pattern.each_with_index do |row, i| | |
row.each_with_index do |col, j| | |
sub_pattern = pattern.map {|r| r.dup } | |
sub_pattern[i][j] = pattern[i][j] == '#' ? '.' : '#' | |
if cols = line_of_symetry(sub_pattern) | |
col_candidates += cols | |
end | |
if rows = line_of_symetry(sub_pattern.transpose) | |
row_candidates += rows | |
end | |
end | |
end | |
rcs = row_candidates - [un_rows] | |
ccs = col_candidates - [un_cols] | |
r = rcs.first | |
c = ccs.first | |
answer2 += c.to_i + (r.to_i * 100) | |
end | |
p "Part 1 answer: #{answer1}" | |
p "Part 2 answer: #{answer2}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
O....#.... | |
O.OO#....# | |
.....##... | |
OO.#O....O | |
.O.....O#. | |
O.#..O.#.# | |
..O..#O..O | |
.......O.. | |
#....###.. | |
#OO..#.... | |
INPUT | |
def weight(grid) | |
answer = 0 | |
grid.each_with_index do |row, i| | |
row.each_with_index do |cell, j| | |
if cell == 'O' | |
answer += grid.length - i | |
end | |
end | |
end | |
answer | |
end | |
def roll(row) | |
swap = false | |
(row.length - 1).times do |i| | |
if row[i] == '.' && row[i + 1] == 'O' | |
row[i], row[i+1] = row[i+1], row[i] | |
swap = true | |
end | |
end | |
swap ? roll(row) : row | |
end | |
def tilt(grid) | |
grid.map { |row| roll(row.dup) } | |
end | |
def cycle(grid) | |
grid = grid.map(&:dup) | |
grid = n(grid) | |
grid = w(grid) | |
grid = s(grid) | |
grid = e(grid) | |
grid | |
end | |
def n(grid) | |
tilt(grid.transpose).transpose | |
end | |
def s(grid) | |
tilt(grid.reverse.transpose).transpose.reverse | |
end | |
def e(grid) | |
tilt(grid.map(&:reverse)).map(&:reverse) | |
end | |
def w(grid) | |
tilt(grid) | |
end | |
grid = input.each_line.map(&:chomp).map(&:chars) | |
#grid = DATA.readlines.map(&:chomp).map(&:chars) | |
pattern_counter = Hash.new{|h,k| h[k] = 0} | |
weights = {} | |
200.times do |n| | |
grid = cycle(grid.map(&:dup)) | |
key = grid.join | |
pattern_counter[key] += 1 | |
weights[n] = weight(grid) | |
break if pattern_counter[key] > 2 | |
end | |
offset = pattern_counter.values.count(1) | |
cycle_length = pattern_counter.values.count {_1 > 1} | |
puts "offset: #{offset}" | |
puts "cycle_length: #{cycle_length}" | |
run_cycles = 1000000000 | |
index = (run_cycles - offset - 1) % cycle_length | |
puts "Answer #{weights[offset + index]}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def h(input) | |
input | |
.chars | |
.map(&:ord) | |
.inject(0) do |c, ascii| | |
c += ascii | |
c *= 17 | |
c % 256 | |
end | |
end | |
examples = [ | |
["rn=1", 30], | |
["cm-", 253], | |
["qp=3", 97], | |
["cm=2", 47], | |
["qp-", 14], | |
["pc=4", 180], | |
["ot=9", 9], | |
["ab=5", 197], | |
["pc-", 48], | |
["pc=6", 214], | |
["ot=7", 231], | |
] | |
# data = "your input" | |
# instructions = data.split(",") | |
instructions = examples.map(&:first) | |
# Part 1 | |
# instructions.map do |ins| | |
# h(ins) | |
# end.sum => r | |
# p r | |
boxes = Hash.new { |h, k| h[k] = [] } | |
def parse(ins) | |
*label, a, b = ins.chars | |
if b == "-" | |
[ins[0...-1], "-", nil] | |
else | |
[ins[0...-2], a, b.to_i] | |
end | |
end | |
instructions | |
.map {|ins| parse(ins)} | |
.each do |label, ins, focal| | |
lenses = boxes[h(label)] | |
if ins == "-" | |
lenses.delete_if {_1.first == label} | |
else | |
if lense = lenses.find {|(l, f)| l == label} | |
lense[1] = focal | |
else | |
lenses << [label, focal] | |
end | |
end | |
end | |
sum = 0 | |
boxes.each do |k, lenses| | |
lenses.each_with_index do |(label, focal), slot| | |
sum += [k + 1, slot + 1, focal].inject(&:*) | |
end | |
end | |
p sum |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = File.read("./example") | |
input = File.read("./input") | |
grid = input.split("\n").map(&:chars) | |
DIR = { | |
right: [0, 1], | |
left: [0, -1], | |
up: [-1, 0], | |
down: [1, 0] | |
} | |
TURN = { | |
"/" => { | |
right: :up, | |
left: :down, | |
up: :right, | |
down: :left | |
}, | |
"\\" => { | |
right: :down, | |
left: :up, | |
up: :left, | |
down: :right | |
} | |
} | |
def energy(beam, grid) | |
beams = [beam] | |
seen = Set.new | |
while !beams.empty? | |
x, y, direction = beams.shift | |
dx, dy = DIR[direction] | |
if x + dx < 0 || x + dx >= grid.size || y + dy < 0 || y + dy >= grid[0].size | |
next | |
end | |
new_beams = [] | |
front = grid[x + dx][y + dy] | |
case [front, direction] | |
in ["|", :right | :left] | |
new_beams << [x + dx, y + dy, :up] | |
new_beams << [x + dx, y + dy, :down] | |
in ["-", :up | :down] | |
new_beams << [x + dx, y + dy, :left] | |
new_beams << [x + dx, y + dy, :right] | |
in ["/" | "\\", _] | |
new_beams << [x + dx, y + dy, TURN[front][direction]] | |
in [_, _] | |
new_beams << [x + dx, y + dy, direction] | |
end | |
new_beams.each do |beam| | |
beams << beam if !seen.include?(beam) | |
end | |
seen += beams | |
end | |
seen.map { |x, y, _| [x, y] }.to_set.length | |
end | |
max = 0 | |
(0...grid.size).each do |x| | |
max = [energy([x, -1, :right], grid), max].max | |
max = [energy([x, grid.first.length, :left], grid), max].max | |
p max | |
end | |
(0...grid.first.size).each do |y| | |
max = [energy([-1, y, :down], grid), max].max | |
max = [energy([grid.length, y, :up], grid), max].max | |
p max | |
end | |
# grid.each_with_index do |row, x| | |
# row.each_with_index do |cell, y| | |
# if energized.include?([x, y]) | |
# print "#" | |
# else | |
# print "." | |
# end | |
# end | |
# print "\n" | |
# end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rspec/autorun' | |
require 'set' | |
require 'rb_heap' | |
input = <<~INPUT | |
2413432311323 | |
3215453535623 | |
3255245654254 | |
3446585845452 | |
4546657867536 | |
1438598798454 | |
4457876987766 | |
3637877979653 | |
4654967986887 | |
4564679986453 | |
1224686865563 | |
2546548887735 | |
4322674655533 | |
INPUT | |
# input = <<~INPUT | |
# 1111 | |
# 9991 | |
# 9991 | |
# 9991 | |
# INPUT | |
DIRS = [ | |
[0, 1], | |
[1, 0], | |
[0, -1], | |
[-1, 0], | |
] | |
def find(blocks, min_in_dir, max_in_dir) | |
# states = [] | |
states = Heap.new {|a, b| (a <=> b) == -1 } | |
states << [0, 0, 0, 0, 1, 1] # heat, x, y, dx, dy, count_in_dir | |
states << [0, 0, 0, 1, 0, 1] # heat, x, y, dx, dy, count_in_dir | |
visited = Set.new | |
while !states.empty? | |
# Find the current state | |
# Slow: | |
# current = states.sort!.shift | |
current = states.pop | |
heat, x, y, dx, dy, count_in_dir = current | |
# Check if we've already been there | |
if visited.include?([x, y, dx, dy, count_in_dir]) | |
next | |
end | |
visited << [x, y, dx, dy, count_in_dir] | |
nx, ny = x + dx, y + dy | |
# Make sure the new x and new y are on the grid | |
if nx < 0 || nx >= blocks.size || ny < 0 || ny >= blocks.first.size | |
next | |
end | |
new_heat = heat + blocks[nx][ny] | |
# Check if we've reached the end | |
if nx == blocks.length - 1 && ny == blocks.first.length - 1 | |
puts "Found it!" | |
return new_heat | |
end | |
DIRS.each do |ddx, ddy| | |
# Don't go back the way we came | |
if [ddx + dx, ddy + dy] == [0, 0] | |
next | |
end | |
# If going the same direction, increase count in dir | |
if [ddx, ddy] == [dx, dy] | |
new_count_in_dir = count_in_dir + 1 | |
else | |
if count_in_dir < min_in_dir | |
next | |
end | |
new_count_in_dir = 1 | |
end | |
# If we've reached the max in dir, don't go that way | |
if new_count_in_dir > max_in_dir | |
next | |
end | |
states << [new_heat, nx, ny, ddx, ddy, new_count_in_dir] | |
end | |
end | |
end | |
# input = DATA.read | |
blocks = input | |
.split("\n") | |
.map {|line| line.chars.map(&:to_i) } | |
# p blocks | |
# p find(blocks, 1, 3) | |
p find(blocks, 4, 10) | |
# describe 'find' do | |
# it 'finds the shortest path without limitation' do | |
# blocks = [ | |
# [1, 1, 1, 1], | |
# [9, 9, 9, 1], | |
# [9, 9, 9, 1], | |
# [9, 9, 9, 1], | |
# ] | |
# expect(find(blocks, 10)).to eq(6) | |
# end | |
# | |
# it 'finds the shortest path with max 2 in dir' do | |
# blocks = [ | |
# [1, 1, 1, 1], | |
# [9, 9, 9, 1], | |
# [9, 9, 9, 1], | |
# [9, 9, 9, 1], | |
# ] | |
# expect(find(blocks, 2)).to eq(14) | |
# end | |
# end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input = <<~INPUT | |
R 6 (#70c710) | |
D 5 (#0dc571) | |
L 2 (#5713f0) | |
D 2 (#d2c081) | |
R 2 (#59c680) | |
D 2 (#411b91) | |
L 5 (#8ceee2) | |
U 2 (#caa173) | |
L 1 (#1b58a2) | |
U 2 (#caa171) | |
R 2 (#7807d2) | |
U 3 (#a77fa3) | |
L 2 (#015232) | |
U 2 (#7a21e3) | |
INPUT | |
DIRS = { | |
"R" => [0, 1], | |
"L" => [0, -1], | |
"U" => [-1, 0], | |
"D" => [1, 0] | |
} | |
# 0 means R, 1 means D, 2 means L, and 3 means U. | |
DIRS2 = { | |
"0" => [0, 1], | |
"2" => [0, -1], | |
"3" => [-1, 0], | |
"1" => [1, 0] | |
} | |
input = DATA.read | |
instructions = input.split("\n").map do |line| | |
dir, steps, color = line.split(' ') | |
color = color.gsub(/[()#]/, '') | |
# PART 1 | |
# steps = steps.to_i | |
# dir = DIRS[dir] | |
# PART 2 | |
steps = color[0...5].to_i(16) | |
dir = DIRS2[color[5]] | |
[dir, steps] | |
end | |
# Array implementation which is too slow for part 2 | |
# current = [0, 0] | |
# wall = [] | |
# | |
# instructions.each do |dir, steps| | |
# steps.times do | |
# current = [current[0] + dir[0], current[1] + dir[1]] | |
# wall << current | |
# end | |
# end | |
# | |
# wall.each_cons(2).sum do |(x1, y1), (x2, y2)| | |
# x1 * y2 - x2 * y1 | |
# end.abs.fdiv(2) => area | |
# puts area | |
# Enumerator implementation which is fast enough for part 2 | |
def gen(instructions, counter) | |
current = [0, 0] | |
Enumerator.new do |enum| | |
instructions.each do |dir, steps| | |
steps.times do | |
counter.call | |
current = [current[0] + dir[0], current[1] + dir[1]] | |
enum << current | |
end | |
end | |
end | |
end | |
wall_length = 0 | |
gen(instructions, -> { wall_length += 1 }) | |
.each_cons(2) # shoelace formula | |
.sum do |(x1, y1), (x2, y2)| | |
x1 * y2 - x2 * y1 | |
end | |
.abs | |
.fdiv(2) => area | |
puts area | |
# Pick's theorem | |
# A = i + (b / 2) - 1 | |
# I = A - (b / 2) + 1 | |
interior_points = area - (wall_length / 2) + 1 | |
# Add the boundary points back in with the wall points | |
puts interior_points + wall_length |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment