Compare commits
10 commits
141b6af203
...
689bf15df4
| Author | SHA1 | Date | |
|---|---|---|---|
| 689bf15df4 | |||
| 65a54ef8b8 | |||
| cadb944182 | |||
| 791d6d9c69 | |||
| 2cd1da3932 | |||
| c489f6e77a | |||
| 141e4761d3 | |||
| 2a3ca7c393 | |||
| dc0f6144a9 | |||
| fc2cde6509 |
7 changed files with 766 additions and 0 deletions
180
day17/main.exs
Normal file
180
day17/main.exs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
defmodule Day17 do
|
||||
def run do
|
||||
[registers, program] =
|
||||
File.read!("data.txt")
|
||||
|> String.trim()
|
||||
|> String.split("\n\n")
|
||||
|> Enum.with_index()
|
||||
|> Enum.map(fn {lines, idx} ->
|
||||
if idx == 0, do: parseRegisters(lines), else: parseProgram(lines)
|
||||
end)
|
||||
|
||||
# registers = %{A: 2024, B: 0, C: 0}
|
||||
# program = [0, 1, 5, 4, 3, 0]
|
||||
|
||||
initial_state = %{
|
||||
program: program,
|
||||
registers: registers,
|
||||
pointer: 0,
|
||||
output: []
|
||||
}
|
||||
|
||||
dbg(initial_state)
|
||||
|
||||
state = run(initial_state)
|
||||
output = Enum.reverse(state.output)
|
||||
dbg(output)
|
||||
|
||||
# part 2
|
||||
# target is [2, 4, 1, 6, 7, 5, 4, 4, 1, 7, 0, 3, 5, 5, 3, 0]
|
||||
# noticing every power of 8 it adds a digit
|
||||
# the new big digit goes [1], 0, 3, 2, 4, 4, 1, 5
|
||||
# when there's 1 digit, the biggest digit changes every 1
|
||||
# when there are 2 digits, the biggest digit changes every 8
|
||||
# when there are 3 digits, the biggest digit changes every 8 * 8
|
||||
# etc.
|
||||
# when there are 16 digits, the biggest digit changes every 8 ^ 15
|
||||
# if the first digit is 2, that's the 3rd in the sequence, so 3x that
|
||||
#
|
||||
# 1 0 3 2 4 4 1 5
|
||||
# when 2 digits, the smaller digit goes:
|
||||
# 0: 1 0 3 2 6 5 1 1
|
||||
# 3: 1 0 2 2 0 6 1 5
|
||||
# 2: 1 0 2 2 2 7 1 1
|
||||
# 4: 1 0 1 3 4 0 1 5
|
||||
# 4: 1 0 1 3 6 1 1 1
|
||||
# 1: 1 0 0 3 0 2 1 5
|
||||
# 5: 1 0 0 3 2 3 1 1
|
||||
#
|
||||
# when 3 digits, smallest digit goes
|
||||
# 0 0 7 0 4 4 1 5
|
||||
#
|
||||
# starter = 3 * 8 ** 15 + 6 * 8 ** 14 + 6 * 8 ** 13 # interesting, all ones
|
||||
starter = 2 * 8 ** 15
|
||||
increment = 8 ** 10
|
||||
|
||||
try(starter, initial_state, increment)
|
||||
end
|
||||
|
||||
defp try(a, state, increment) do
|
||||
# IO.gets("")
|
||||
new_state = %{state | registers: %{state.registers | A: a}}
|
||||
val = run(new_state)
|
||||
|
||||
# if Integer.mod(a, 1000) == 0 do
|
||||
IO.puts(a)
|
||||
dbg(a)
|
||||
dbg(val)
|
||||
# end
|
||||
|
||||
if Enum.reverse(val.output) == state.program do
|
||||
a
|
||||
else
|
||||
try(a + increment, state, increment)
|
||||
end
|
||||
end
|
||||
|
||||
defp run(
|
||||
%{
|
||||
program: program,
|
||||
registers: registers,
|
||||
pointer: pointer,
|
||||
output: output
|
||||
} = state
|
||||
) do
|
||||
opcode = Enum.at(program, pointer)
|
||||
operand = Enum.at(program, pointer + 1)
|
||||
# dbg({"run", state})
|
||||
|
||||
if is_nil(opcode) do
|
||||
state
|
||||
else
|
||||
new_state =
|
||||
case opcode do
|
||||
# adv
|
||||
0 ->
|
||||
num = registers[:A]
|
||||
den = Integer.pow(2, getComboOperandValue(operand, registers))
|
||||
new_regs = %{registers | A: trunc(num / den)}
|
||||
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
|
||||
# bxl
|
||||
1 ->
|
||||
new_regs = %{registers | B: Bitwise.bxor(registers[:B], operand)}
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
|
||||
# bst
|
||||
2 ->
|
||||
new_regs = %{
|
||||
registers
|
||||
| B: Integer.mod(getComboOperandValue(operand, registers), 8)
|
||||
}
|
||||
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
|
||||
# jnz
|
||||
3 ->
|
||||
if registers[:A] == 0 do
|
||||
%{state | pointer: pointer + 2}
|
||||
else
|
||||
%{state | pointer: operand}
|
||||
end
|
||||
|
||||
# bxc
|
||||
4 ->
|
||||
new_regs = %{registers | B: Bitwise.bxor(registers[:B], registers[:C])}
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
|
||||
# out
|
||||
5 ->
|
||||
val = Integer.mod(getComboOperandValue(operand, registers), 8)
|
||||
%{state | pointer: pointer + 2, output: [val | output]}
|
||||
|
||||
# bdv
|
||||
6 ->
|
||||
num = registers[:A]
|
||||
den = Integer.pow(2, getComboOperandValue(operand, registers))
|
||||
new_regs = %{registers | B: trunc(num / den)}
|
||||
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
|
||||
# cdv
|
||||
7 ->
|
||||
num = registers[:A]
|
||||
den = Integer.pow(2, getComboOperandValue(operand, registers))
|
||||
new_regs = %{registers | C: trunc(num / den)}
|
||||
|
||||
%{state | registers: new_regs, pointer: pointer + 2}
|
||||
end
|
||||
|
||||
run(new_state)
|
||||
end
|
||||
end
|
||||
|
||||
defp parseProgram(line) do
|
||||
[_header, digits] = String.split(line, " ")
|
||||
digits |> String.split(",") |> Enum.map(&String.to_integer/1)
|
||||
end
|
||||
|
||||
defp getComboOperandValue(op, registers) do
|
||||
case op do
|
||||
o when o <= 3 -> op
|
||||
4 -> registers[:A]
|
||||
5 -> registers[:B]
|
||||
6 -> registers[:C]
|
||||
end
|
||||
end
|
||||
|
||||
defp parseRegisters(lines) do
|
||||
lines
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&String.split(&1, " "))
|
||||
|> Enum.reduce(%{}, fn [_header, label, value], acc ->
|
||||
atom = String.trim(label, ":") |> String.to_atom()
|
||||
Map.put(acc, atom, String.to_integer(value))
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
Day17.run()
|
||||
112
day20/main.exs
Normal file
112
day20/main.exs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
defmodule Day20 do
|
||||
def run do
|
||||
{map, width, height, start, dest} = parse_input("data.txt")
|
||||
|
||||
# create a map of visited nodes an a queue
|
||||
visited = %{start => 100_000_000}
|
||||
q = :queue.from_list([{start, 0, []}])
|
||||
|
||||
{path, shortest} = walk(map, q, dest, visited, [], 100_000_000_000, width, height)
|
||||
path = Enum.reverse(path)
|
||||
|
||||
part1 = run(2, path)
|
||||
part2 = run(20, path)
|
||||
dbg({part1, part2})
|
||||
end
|
||||
|
||||
# walk every step along the path
|
||||
# find coordinates that we can get to within <cheat> number of steps, that save us 100 picoseconds
|
||||
defp run(cheats, path) do
|
||||
Enum.with_index(path)
|
||||
|> Enum.reduce(0, fn {el, idx}, acc ->
|
||||
res =
|
||||
path
|
||||
|> Enum.with_index()
|
||||
|> Enum.filter(fn {el2, idx2} ->
|
||||
dist = distance(el, el2)
|
||||
dist <= cheats and idx + dist + 100 <= idx2
|
||||
end)
|
||||
|> length
|
||||
|
||||
acc + res
|
||||
end)
|
||||
end
|
||||
|
||||
defp distance({x1, y1}, {x2, y2}) do
|
||||
abs(x2 - x1) + abs(y2 - y1)
|
||||
end
|
||||
|
||||
defp walk(map, q, {dx, dy} = dest, visited, paths, shortest_path, width, height) do
|
||||
# dequeue the node with the lowest distnce
|
||||
# no priority queue, so just find the lowest one and delete it from the queue
|
||||
ql = :queue.to_list(q)
|
||||
|
||||
if length(ql) == 0 do
|
||||
nil
|
||||
else
|
||||
# lowest = next_from_queue(ql, dest)
|
||||
lowest = Enum.min_by(ql, fn {_, dist, _} -> dist end)
|
||||
{{x, y}, dist, path} = lowest
|
||||
new_q = :queue.delete(lowest, q)
|
||||
|
||||
# if we reached the destination
|
||||
if x == dx and y == dy do
|
||||
{[{x, y} | path], dist}
|
||||
else
|
||||
dirs = [{1, 0}, {0, 1}, {-1, 0}, {0, -1}]
|
||||
|
||||
# find neighbors to enqueue
|
||||
neighbors =
|
||||
dirs
|
||||
|> Enum.map(fn {dx, dy} -> {x + dx, y + dy} end)
|
||||
|> Enum.map(fn key -> {key, Map.get(map, key, ".")} end)
|
||||
|> Enum.filter(fn {{x, y}, el} ->
|
||||
el != "#" and x >= 0 and y >= 0 and x < width and y < height
|
||||
end)
|
||||
|> Enum.reject(fn {{nx, ny}, _el} -> Map.has_key?(visited, {nx, ny}) end)
|
||||
|> Enum.reject(fn {{nx, ny}, _el} ->
|
||||
Enum.any?(:queue.to_list(q), fn {pos, _, _} -> pos == {nx, ny} end)
|
||||
end)
|
||||
|> Enum.map(fn {key, _el} -> key end)
|
||||
|
||||
new_q2 =
|
||||
Enum.reduce(neighbors, new_q, fn {nx, ny}, acc ->
|
||||
:queue.in({{nx, ny}, dist + 1, [{x, y} | path]}, acc)
|
||||
end)
|
||||
|
||||
new_visited = Map.put(visited, {x, y}, dist)
|
||||
walk(map, new_q2, dest, new_visited, paths, shortest_path, width, height)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_input(file) do
|
||||
map_list =
|
||||
File.read!(file)
|
||||
|> String.trim()
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&String.graphemes/1)
|
||||
|
||||
height = length(map_list)
|
||||
width = length(hd(map_list))
|
||||
|
||||
# convert the map List to a map Map, grabbing start and dest along the way
|
||||
{map, start, dest} =
|
||||
Enum.concat(map_list)
|
||||
|> Enum.with_index()
|
||||
|> Enum.reduce({%{}, nil, nil}, fn {el, idx}, {map, start, dest} ->
|
||||
row = div(idx, height)
|
||||
col = rem(idx, width)
|
||||
|
||||
map = Map.put(map, {row, col}, el)
|
||||
start = if el == "S", do: {row, col}, else: start
|
||||
dest = if el == "E", do: {row, col}, else: dest
|
||||
|
||||
{map, start, dest}
|
||||
end)
|
||||
|
||||
{map, width, height, start, dest}
|
||||
end
|
||||
end
|
||||
|
||||
Day20.run()
|
||||
181
day21/main.exs
Normal file
181
day21/main.exs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
defmodule Day21 do
|
||||
def run do
|
||||
lines =
|
||||
File.read!("data.txt")
|
||||
|> String.trim()
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&String.graphemes/1)
|
||||
|
||||
numpad = %{
|
||||
nil => {0, 0},
|
||||
"0" => {1, 0},
|
||||
"A" => {2, 0},
|
||||
"1" => {0, 1},
|
||||
"2" => {1, 1},
|
||||
"3" => {2, 1},
|
||||
"4" => {0, 2},
|
||||
"5" => {1, 2},
|
||||
"6" => {2, 2},
|
||||
"7" => {0, 3},
|
||||
"8" => {1, 3},
|
||||
"9" => {2, 3}
|
||||
}
|
||||
|
||||
dpad = %{
|
||||
"<" => {0, 0},
|
||||
"v" => {1, 0},
|
||||
">" => {2, 0},
|
||||
nil => {0, 1},
|
||||
"^" => {1, 1},
|
||||
"A" => {2, 1}
|
||||
}
|
||||
|
||||
# doing some testing...
|
||||
# go([[">", "v"], ["v", ">"]], dpad) |> go(dpad) |> dbg
|
||||
# go([["<", "v"], ["v", "<"]], dpad) |> go(dpad) |> dbg
|
||||
# go([[">", "^"], ["^", ">"]], dpad) |> go(dpad) |> dbg
|
||||
# go([["<", "^"], ["^", "<"]], dpad) |> go(dpad) |> dbg
|
||||
# reveals that:
|
||||
# <, v is better than v, <
|
||||
# v, > is better than >, v
|
||||
# >, ^ is better than ^, >
|
||||
# <, ^ is better than ^, <
|
||||
|
||||
part1 =
|
||||
lines
|
||||
|> go(numpad)
|
||||
|> go(dpad)
|
||||
|> go(dpad)
|
||||
|> Enum.zip(lines)
|
||||
|> Enum.map(fn {sequence, line} ->
|
||||
numeric_part =
|
||||
line |> Enum.drop(-1) |> Enum.map(&String.to_integer/1) |> Integer.undigits()
|
||||
|
||||
numeric_part * length(sequence)
|
||||
end)
|
||||
|> Enum.sum()
|
||||
|
||||
dbg(part1, charlists: :as_lists)
|
||||
|
||||
part2 =
|
||||
lines
|
||||
|> go(numpad)
|
||||
|> Enum.zip(lines)
|
||||
# for each line
|
||||
|> Enum.reduce({%{}, 0}, fn {line, original_line}, {cache, sum} ->
|
||||
# for each key
|
||||
{new_cache, _, len} =
|
||||
Enum.reduce(line, {cache, "A", 0}, fn key, {cache, previous, sum} ->
|
||||
{num, cache} = num_steps_with_depth(previous, key, dpad, 25, cache)
|
||||
{cache, key, sum + num}
|
||||
end)
|
||||
|
||||
numeric_part =
|
||||
original_line |> Enum.drop(-1) |> Enum.map(&String.to_integer/1) |> Integer.undigits()
|
||||
|
||||
{new_cache, sum + numeric_part * len}
|
||||
end)
|
||||
|
||||
dbg(part2)
|
||||
end
|
||||
|
||||
# finds the number of steps needed to get from a to b, over and over, depth times
|
||||
defp num_steps_with_depth(a, b, keypad, depth, cache) do
|
||||
key = {a, b, depth}
|
||||
|
||||
if Map.has_key?(cache, key) do
|
||||
res = Map.get(cache, key)
|
||||
{res, cache}
|
||||
else
|
||||
new_steps = steps(a, b, keypad)
|
||||
|
||||
case depth do
|
||||
0 ->
|
||||
new_cache = Map.put(cache, key, 2)
|
||||
{2, new_cache}
|
||||
|
||||
1 ->
|
||||
len = length(new_steps)
|
||||
new_cache = Map.put(cache, key, len)
|
||||
{len, new_cache}
|
||||
|
||||
d ->
|
||||
# for each pair, find the length of depth - 1
|
||||
{new_cache, el, steps} =
|
||||
Enum.reduce(new_steps, {cache, "A", 0}, fn el, {cache, previous, num_steps} ->
|
||||
{num, new_cache} = num_steps_with_depth(previous, el, keypad, depth - 1, cache)
|
||||
new_cache = Map.put(new_cache, {previous, el, depth - 1}, num)
|
||||
{new_cache, el, num + num_steps}
|
||||
end)
|
||||
|
||||
{steps, new_cache}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# maps over each line, then reduces over each keypress, gathering the new steps needed
|
||||
defp go(lines, pad) do
|
||||
Enum.map(lines, fn line ->
|
||||
Enum.reduce(line, {"A", []}, fn el, {previous, list} ->
|
||||
{el, Enum.concat(list, steps(previous, el, pad))}
|
||||
end)
|
||||
|> elem(1)
|
||||
end)
|
||||
end
|
||||
|
||||
# returns the steps from a to b on the given keypad, including hitting A at the end
|
||||
defp steps(a, b, keypad) do
|
||||
{x1, y1} = Map.get(keypad, a)
|
||||
{x2, y2} = Map.get(keypad, b)
|
||||
|
||||
y_char = if y2 > y1, do: "^", else: "v"
|
||||
x_char = if x2 > x1, do: ">", else: "<"
|
||||
|
||||
xs =
|
||||
if x2 == x1,
|
||||
do: [],
|
||||
else: Enum.reduce(1..abs(x2 - x1), [], fn _el, acc -> [x_char | acc] end)
|
||||
|
||||
ys =
|
||||
if y2 == y1,
|
||||
do: [],
|
||||
else: Enum.reduce(1..abs(y2 - y1), [], fn _el, acc -> [y_char | acc] end)
|
||||
|
||||
{dx, dy} = Map.get(keypad, nil)
|
||||
|
||||
# avoid hole
|
||||
res =
|
||||
if x2 < x1 do
|
||||
# if i'm going left, and going left _first_ hits the empty spot
|
||||
if {x2, y1} == {dx, dy} do
|
||||
# then go up/down first
|
||||
Enum.concat([ys, xs, ["A"]])
|
||||
end
|
||||
else
|
||||
# if i'm going up or down, and going up/down _first_ hits the empty spot
|
||||
if y1 != y2 and {x1, y2} == {dx, dy} do
|
||||
# then go left/right first
|
||||
Enum.concat([xs, ys, ["A"]])
|
||||
end
|
||||
end
|
||||
|
||||
# if the hole needed to be avoided, return that
|
||||
if !is_nil(res) do
|
||||
res
|
||||
else
|
||||
# otherwise use our heuristic
|
||||
cond do
|
||||
# if we're going down and left, go left first
|
||||
y2 < y1 and x2 < x1 -> Enum.concat([xs, ys, ["A"]])
|
||||
# if we're going down and right, go down first
|
||||
y2 < y1 and x2 > x1 -> Enum.concat([ys, xs, ["A"]])
|
||||
# if we're going up and left, go left first
|
||||
y2 > y1 and x2 < x1 -> Enum.concat([xs, ys, ["A"]])
|
||||
# if we're going up and right (or anything else), go up first
|
||||
true -> Enum.concat([ys, xs, ["A"]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Day21.run()
|
||||
73
day22/main.exs
Normal file
73
day22/main.exs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
defmodule Day22 do
|
||||
def run do
|
||||
lines =
|
||||
File.read!("data.txt")
|
||||
|> String.trim()
|
||||
|> String.split("\n")
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
|
||||
part1 =
|
||||
Enum.reduce(lines, 0, fn secret, acc ->
|
||||
acc + Enum.reduce(1..2000, secret, fn _, s -> evolve(s) end)
|
||||
end)
|
||||
|
||||
dbg(part1)
|
||||
|
||||
# build a map of all 4-digit sequences + list index to prices
|
||||
map =
|
||||
Enum.with_index(lines)
|
||||
|> Enum.reduce(%{}, fn {secret, idx}, map ->
|
||||
{_, prices, diffs, map2} =
|
||||
Enum.reduce(1..2000, {secret, [price(secret)], [], map}, fn _, {s, prices, diffs, m} ->
|
||||
new_secret = evolve(s)
|
||||
price = price(new_secret)
|
||||
diff = price - hd(prices)
|
||||
new_diff = [diff | diffs]
|
||||
sec = Enum.slice(new_diff, 0, 4)
|
||||
new_map = Map.put_new(m, {sec, idx}, price)
|
||||
{new_secret, [price | prices], new_diff, new_map}
|
||||
end)
|
||||
|
||||
map2
|
||||
end)
|
||||
|
||||
# for every possible sequence (there's only like 19 ^ 4 or something)
|
||||
all = for a <- -9..9, b <- -9..9, c <- -9..9, d <- -9..9, do: [a, b, c, d]
|
||||
|
||||
part2 =
|
||||
Enum.reduce(all, {0, []}, fn sec, {max, max_sec} ->
|
||||
# and for every buyer, get the price at this sequence
|
||||
val =
|
||||
Enum.reduce(0..length(lines), 0, fn idx, acc ->
|
||||
case Map.get(map, {sec, idx}, nil) do
|
||||
nil -> acc
|
||||
val -> val + acc
|
||||
end
|
||||
end)
|
||||
|
||||
if val > max, do: {val, sec}, else: {max, max_sec}
|
||||
end)
|
||||
|
||||
dbg(part2)
|
||||
end
|
||||
|
||||
defp evolve(secret) do
|
||||
step1 = secret |> Kernel.*(64) |> mix(secret) |> prune()
|
||||
step2 = step1 |> div(32) |> mix(step1) |> prune()
|
||||
step2 |> Kernel.*(2048) |> mix(step2) |> prune()
|
||||
end
|
||||
|
||||
defp mix(num, secret) do
|
||||
Bitwise.bxor(num, secret)
|
||||
end
|
||||
|
||||
defp prune(num) do
|
||||
Integer.mod(num, 16_777_216)
|
||||
end
|
||||
|
||||
defp price(num) do
|
||||
Integer.digits(num) |> Enum.take(-1) |> Integer.undigits()
|
||||
end
|
||||
end
|
||||
|
||||
Day22.run()
|
||||
81
day23/main.exs
Normal file
81
day23/main.exs
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
defmodule Day23 do
|
||||
def run do
|
||||
connections =
|
||||
File.read!("data.txt")
|
||||
|> String.trim()
|
||||
|> String.split("\n")
|
||||
|> Enum.map(fn el -> String.split(el, "-") end)
|
||||
|
||||
all_computers = connections |> Enum.concat() |> Enum.uniq()
|
||||
|
||||
# build a map of computers -> all their connected computers
|
||||
map =
|
||||
Enum.reduce(all_computers, %{}, fn el1, map ->
|
||||
friends =
|
||||
Enum.filter(all_computers, fn el2 ->
|
||||
Enum.member?(connections, [el1, el2]) || Enum.member?(connections, [el2, el1])
|
||||
end)
|
||||
|
||||
Map.put(map, el1, friends)
|
||||
end)
|
||||
|
||||
part1 =
|
||||
expand(map, connections)
|
||||
|> Enum.filter(fn set -> Enum.any?(set, fn el -> String.starts_with?(el, "t") end) end)
|
||||
|> length
|
||||
|
||||
dbg(part1)
|
||||
|
||||
part2 =
|
||||
loop_expand(map, connections)
|
||||
|> hd()
|
||||
|> Enum.join(",")
|
||||
|
||||
dbg(part2)
|
||||
end
|
||||
|
||||
defp loop_expand(map, sets) do
|
||||
case expand(map, sets) do
|
||||
[] -> sets
|
||||
x -> loop_expand(map, x)
|
||||
end
|
||||
end
|
||||
|
||||
defp expand(map, sets) do
|
||||
# look at an element in every set (e.g. the first element)
|
||||
# check all of its connections
|
||||
# if any of those connected computers are connected to all of the set members, you've increased the set size by 1!
|
||||
Enum.reduce(sets, [], fn set, acc ->
|
||||
connections = Map.get(map, hd(set), [])
|
||||
|
||||
res =
|
||||
Enum.reduce(connections, [], fn con, acc3 ->
|
||||
# if it is connected to each member of the set, we've enlarged the set
|
||||
if Enum.all?(set, fn set_el -> is_connected(map, set_el, con) end) do
|
||||
[[con | set] | acc3]
|
||||
else
|
||||
acc3
|
||||
end
|
||||
end)
|
||||
|
||||
if Enum.empty?(res) do
|
||||
acc
|
||||
else
|
||||
if is_list(Enum.at(res, 0)) do
|
||||
Enum.concat(res, acc)
|
||||
else
|
||||
[res | acc]
|
||||
end
|
||||
end
|
||||
end)
|
||||
|> Enum.map(fn l -> Enum.sort(l) end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.sort_by(fn a -> Enum.join(a) end)
|
||||
end
|
||||
|
||||
defp is_connected(map, a, b) do
|
||||
Map.get(map, a, []) |> Enum.member?(b)
|
||||
end
|
||||
end
|
||||
|
||||
Day23.run()
|
||||
88
day24/main.exs
Normal file
88
day24/main.exs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
defmodule Day24 do
|
||||
def run do
|
||||
{wires, gates} = parse_input("data.txt")
|
||||
|
||||
# part1
|
||||
part1 = run_loop(wires, gates) |> wire_value("z") |> b_to_d()
|
||||
dbg(part1)
|
||||
|
||||
# part2
|
||||
x = wire_value(wires, "x") |> b_to_d()
|
||||
y = wire_value(wires, "y") |> b_to_d()
|
||||
target = (x + y) |> d_to_b()
|
||||
dbg(target)
|
||||
dbg(b_to_d(target))
|
||||
end
|
||||
|
||||
defp b_to_d(bin) do
|
||||
:erlang.binary_to_integer(Integer.to_string(bin), 2)
|
||||
end
|
||||
|
||||
defp d_to_b(dec) do
|
||||
Integer.to_string(dec, 2) |> String.to_integer()
|
||||
end
|
||||
|
||||
defp wire_value(wires, char) do
|
||||
wires
|
||||
|> Enum.filter(fn {k, _} -> String.starts_with?(k, char) end)
|
||||
|> Enum.sort()
|
||||
|> Enum.reduce([], fn {_, v}, acc -> [v | acc] end)
|
||||
|> Integer.undigits()
|
||||
end
|
||||
|
||||
defp run_loop(wires, gates) do
|
||||
new_wires = run(wires, gates)
|
||||
|
||||
if map_size(new_wires) == map_size(wires) do
|
||||
wires
|
||||
else
|
||||
run_loop(new_wires, gates)
|
||||
end
|
||||
end
|
||||
|
||||
defp run(wires, gates) do
|
||||
Enum.reduce(gates, wires, fn %{left: left, right: right, output: output, op: op}, acc ->
|
||||
l = Map.get(wires, left, nil)
|
||||
r = Map.get(wires, right, nil)
|
||||
|
||||
if !is_nil(l) and !is_nil(r) do
|
||||
Map.put(acc, output, op.(l, r))
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp parse_input(file) do
|
||||
[wires, gates] =
|
||||
File.read!(file)
|
||||
|> String.trim()
|
||||
|> String.split("\n\n")
|
||||
|> Enum.map(&String.split(&1, "\n"))
|
||||
|
||||
wires =
|
||||
Enum.reduce(wires, %{}, fn el, map ->
|
||||
[wire, value] = String.split(el, ": ")
|
||||
Map.put(map, wire, String.to_integer(value))
|
||||
end)
|
||||
|
||||
gates =
|
||||
Enum.reduce(gates, [], fn el, map ->
|
||||
[logic, output] = String.split(el, " -> ")
|
||||
[left, op, right] = String.split(logic, " ")
|
||||
|
||||
op =
|
||||
case op do
|
||||
"AND" -> &Bitwise.band/2
|
||||
"OR" -> &Bitwise.bor/2
|
||||
"XOR" -> &Bitwise.bxor/2
|
||||
end
|
||||
|
||||
[%{left: left, right: right, op: op, output: output} | map]
|
||||
end)
|
||||
|
||||
{wires, gates}
|
||||
end
|
||||
end
|
||||
|
||||
Day24.run()
|
||||
51
day25/main.exs
Normal file
51
day25/main.exs
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
defmodule Day25 do
|
||||
def run do
|
||||
%{locks: locks, keys: keys} = parse_input("data.txt")
|
||||
|
||||
# compare every key against every lock
|
||||
good_keys =
|
||||
Enum.reduce(keys, 0, fn key, acc ->
|
||||
acc +
|
||||
Enum.reduce(locks, 0, fn lock, acc2 ->
|
||||
acc2 + if fits(lock, key), do: 1, else: 0
|
||||
end)
|
||||
end)
|
||||
|
||||
dbg(good_keys)
|
||||
end
|
||||
|
||||
defp fits(lock, key) do
|
||||
Enum.reduce(0..4, true, fn idx, acc ->
|
||||
acc and Enum.at(lock, idx) + Enum.at(key, idx) <= 5
|
||||
end)
|
||||
end
|
||||
|
||||
defp parse_input(file) do
|
||||
File.read!(file)
|
||||
|> String.trim()
|
||||
|> String.split("\n\n")
|
||||
|> Enum.reduce(%{locks: [], keys: []}, fn el, %{locks: locks, keys: keys} = acc ->
|
||||
grid = String.split(el, "\n") |> Enum.map(&String.graphemes/1)
|
||||
is_lock = grid |> hd() |> hd() |> String.graphemes() |> Enum.all?(fn el -> el == "#" end)
|
||||
|
||||
res =
|
||||
Enum.map(0..4, fn col ->
|
||||
Enum.reduce(0..6, 0, fn row, acc2 ->
|
||||
el = Enum.at(grid, row) |> Enum.at(col)
|
||||
|
||||
case el do
|
||||
"#" -> acc2 + 1
|
||||
_ -> acc2
|
||||
end
|
||||
end) - 1
|
||||
end)
|
||||
|
||||
case is_lock do
|
||||
true -> %{acc | locks: [res | locks]}
|
||||
false -> %{acc | keys: [res | keys]}
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
Day25.run()
|
||||
Loading…
Add table
Add a link
Reference in a new issue