Compare commits
No commits in common. "689bf15df4b649064b39af8dfc515526b89fbc42" and "141b6af2034e44e9029162e416c277d6331f2b81" have entirely different histories.
689bf15df4
...
141b6af203
7 changed files with 0 additions and 766 deletions
180
day17/main.exs
180
day17/main.exs
|
|
@ -1,180 +0,0 @@
|
||||||
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
112
day20/main.exs
|
|
@ -1,112 +0,0 @@
|
||||||
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
181
day21/main.exs
|
|
@ -1,181 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
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()
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
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