day 9. takes 1 minute to run
parent
d8d4825d52
commit
9a3da1227f
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…
Reference in New Issue