day 9. takes 1 minute to run

master
Dustin Swan 3 weeks ago
parent d8d4825d52
commit 9a3da1227f
Signed by: dustinswan
GPG Key ID: AB49BD6B2B3A6377

File diff suppressed because one or more lines are too long

@ -0,0 +1,162 @@
defmodule Day9 do
def run do
memory =
File.read!("data.txt")
|> String.trim()
|> String.graphemes()
|> Enum.map(&String.to_integer(&1))
|> Enum.with_index()
|> Enum.reduce([], fn {num, idx}, acc ->
new_num = if rem(idx, 2) == 0, do: trunc(idx / 2), else: nil
Enum.concat(acc, List.duplicate(new_num, num))
end)
dbg(memory)
part1 = memory |> defrag1() |> checksum()
dbg(part1)
part2 = memory |> defrag2() |> checksum()
dbg(part2)
end
defp checksum(memory) do
Enum.with_index(memory)
|> Enum.reduce(0, fn {el, idx}, acc ->
if el, do: acc + idx * el, else: acc
end)
end
defp defrag1(memory) do
defrag1_step(%{memory: memory, free_index: 0, block_index: length(memory) - 1})
end
# 2 pointers, one tracking free space, one tracking blocks
# increase the free one until we find a spot, decrease the block one until we find a block
# if we have a free spot and a block, swap em
defp defrag1_step(acc) do
free = Enum.at(acc.memory, acc.free_index)
block = Enum.at(acc.memory, acc.block_index)
# if we've met in the middle, we're done recursing
if acc.free_index >= acc.block_index do
acc.memory
else
case {free, block} do
{_, nil} ->
%{acc | block_index: acc.block_index - 1}
{f, _} when f != nil ->
%{acc | free_index: acc.free_index + 1}
{nil, block} ->
memory =
List.replace_at(
List.replace_at(acc.memory, acc.free_index, block),
acc.block_index,
nil
)
%{
acc
| memory: memory,
block_index: acc.block_index - 1,
free_index: acc.free_index + 1
}
end
|> defrag1_step()
end
end
defp defrag2(memory) do
# break the memory up into chunks. Each time the char changes, new chunk
chunks =
Enum.chunk_while(
memory,
[],
fn el, acc ->
if length(acc) >= 1 and el != hd(acc) do
{:cont, Enum.reverse(acc), [el]}
else
{:cont, [el | acc]}
end
end,
fn
[] -> {:cont, []}
acc -> {:cont, Enum.reverse(acc), []}
end
)
# run it through the defragger and then flatten it
defrag2_step(%{chunks: chunks, index: length(chunks) - 1})
|> List.flatten()
end
defp defrag2_step(args) do
chunks = args.chunks
index = args.index
# if we are at 0, stop recursing
if index == 0 do
chunks
else
file = Enum.at(chunks, index)
# if this file is nils (empty space) skip it. recruse, picking next lowest chunk
if is_nil(Enum.at(file, 0)) do
defrag2_step(%{chunks: chunks, index: index - 1})
else
file_size = length(file)
# find a chunk of empty space big enough starting from the beginning
empty_chunk_index =
Enum.find_index(chunks, fn chunk ->
# if it's an empty space chunk and its big enough
is_nil(Enum.at(chunk, 0)) and length(chunk) >= file_size
end)
if empty_chunk_index && empty_chunk_index < index do
empty_chunk = Enum.at(chunks, empty_chunk_index)
new_free_space_size = length(empty_chunk) - file_size
# first replace the file with free space
temp1 =
List.replace_at(chunks, index, List.duplicate(nil, file_size))
|> List.replace_at(empty_chunk_index, file)
# if our file was smaller than the free space
if new_free_space_size > 0 do
# check the chunk _after_ the free space.
next_chunk = Enum.at(chunks, empty_chunk_index + 1)
new_list =
if is_nil(Enum.at(next_chunk, 0)) do
# if it is also free space, tack this on to it
List.replace_at(
temp1,
empty_chunk_index + 1,
Enum.concat(next_chunk, List.duplicate(nil, new_free_space_size))
)
else
# otherwise, create a new chunk after this one, filling up the space
List.insert_at(
temp1,
empty_chunk_index + 1,
List.duplicate(nil, new_free_space_size)
)
end
defrag2_step(%{chunks: new_list, index: index})
else
defrag2_step(%{chunks: temp1, index: index})
end
else
# no free space for this file. recurse, check the next chunk
defrag2_step(%{chunks: chunks, index: index - 1})
end
end
end
end
end
Day9.run()

@ -0,0 +1 @@
2333133121414131402
Loading…
Cancel
Save