--- Day 9: Disk Fragmenter ---
Another push of the button leaves you in the familiar hallways of some friendly amphipods! Good thing you each somehow got your own personal mini submarine. The Historians jet away in search of the Chief, mostly by driving directly into walls.
While The Historians quickly figure out how to pilot these things, you notice an amphipod in the corner struggling with his computer. He's trying to make more contiguous free space by compacting all of the files, but his program isn't working; you offer to help.
He shows you the disk map (your puzzle input) he's already generated. For example:
2333133121414131402
The disk map uses a dense format to represent the layout of files and free space on the disk. The digits alternate between indicating the length of a file and the length of free space.
So, a disk map like
12345
would represent a one-block file, two blocks of free space, a three-block file, four blocks of free space, and then a five-block file. A disk map like90909
would represent three nine-block files in a row (with no free space between them).Each file on disk also has an ID number based on the order of the files as they appear before they are rearranged, starting with ID
0
. So, the disk map12345
has three files: a one-block file with ID0
, a three-block file with ID1
, and a five-block file with ID2
. Using one character for each block where digits are the file ID and.
is free space, the disk map12345
represents these individual blocks:0..111....22222
The first example above,
2333133121414131402
, represents these individual blocks:00...111...2...333.44.5555.6666.777.888899
The amphipod would like to move file blocks one at a time from the end of the disk to the leftmost free space block (until there are no gaps remaining between file blocks). For the disk map
12345
, the process looks like this:0..111....22222 02.111....2222. 022111....222.. 0221112...22... 02211122..2.... 022111222......
The first example requires a few more steps:
00...111...2...333.44.5555.6666.777.888899 009..111...2...333.44.5555.6666.777.88889. 0099.111...2...333.44.5555.6666.777.8888.. 00998111...2...333.44.5555.6666.777.888... 009981118..2...333.44.5555.6666.777.88.... 0099811188.2...333.44.5555.6666.777.8..... 009981118882...333.44.5555.6666.777....... 0099811188827..333.44.5555.6666.77........ 00998111888277.333.44.5555.6666.7......... 009981118882777333.44.5555.6666........... 009981118882777333644.5555.666............ 00998111888277733364465555.66............. 0099811188827773336446555566..............
The final step of this file-compacting process is to update the filesystem checksum. To calculate the checksum, add up the result of multiplying each of these blocks' position with the file ID number it contains. The leftmost block is in position
0
. If a block contains free space, skip it instead.Continuing the first example, the first few blocks' position multiplied by its file ID number are
0 * 0 = 0
,1 * 0 = 0
,2 * 9 = 18
,3 * 9 = 27
,4 * 8 = 32
, and so on. In this example, the checksum is the sum of these,1928
.Compact the amphipod's hard drive using the process he requested. What is the resulting filesystem checksum? (Be careful copy/pasting the input for this puzzle; it is a single, very long line.)
show i:"J"$'first read0 `9.txt 2 3 3 3 1 3 3 1 2 1 4 1 4 1 3 1 4 0 2
Now append a 0 (to represent a gap of length 0) and split into two lists. One for file size, and one for gap size.
show (f;g): flip 0N 2# i,0 2 3 1 3 2 4 4 3 4 2 3 3 3 1 1 1 1 1 0 0
We can now construct a representation of the hard drive.
"."^.Q.n 0N!d:raze {(x#z),y#0N}'[f;g;til n:count f] 0 0 0N 0N 0N 1 1 1 0N 0N 0N 2 0N 0N 0N 3 3 3 0N 4 4 0N 5 5 5 5 0N 6 6 6 6 0N .. "00...111...2...333.44.5555.6666.777.888899"Here we're using each on 3 arrays.
Now, we need to fill the null values from the end. We can do this all at once using amend.
First of all, find where the null values are, and the total number of them.
C: count W: where N: null dNow, we amend
d
at the null values, with the new values being the appropriate number of non-null values starting at the end.
@[d; W; :; C#reverse d where not N] 0 0 9 9 8 1 1 1 8 8 8 2 7 7 7 3 3 3 6 4 4 6 5 5 5 5 6 6 6 6 6 6 7 7 7 5 8 8 8..
Now we take the number of not null values.
show r:(sum not N)#@[d; W; :; C#reverse d where not N] 0 0 9 9 8 1 1 1 8 8 8 2 7 7 7 3 3 3 6 4 4 6 5 5 5 5 6 6
The answer to part 1 is then the sum of each value multiplied by its index.
sum r*til count r 1928
--- Part Two ---
Upon completion, two things immediately become clear. First, the disk definitely has a lot more contiguous free space, just like the amphipod hoped. Second, the computer is running much more slowly! Maybe introducing all of that file system fragmentation was a bad idea?
The eager amphipod already has a new plan: rather than move individual blocks, he'd like to try compacting the files on his disk by moving whole files instead.
This time, attempt to move whole files to the leftmost span of free space blocks that could fit the file. Attempt to move each file exactly once in order of decreasing file ID number starting with the file with the highest file ID number. If there is no span of free space to the left of a file that is large enough to fit the file, the file does not move.
The first example from above now proceeds differently:
00...111...2...333.44.5555.6666.777.888899 0099.111...2...333.44.5555.6666.777.8888.. 0099.1117772...333.44.5555.6666.....8888.. 0099.111777244.333....5555.6666.....8888.. 00992111777.44.333....5555.6666.....8888..
The process of updating the filesystem checksum is the same; now, this example's checksum would be
2858
.Start over, now compacting the amphipod's hard drive using this new method instead. What is the resulting filesystem checksum?
Now instead of moving parts of files, we need to move the whole thing.
We will maintain a list of "blocks", representing blocks of contiguous files.
At first, this is just lists of the initial indices.
show b:1#'til n 0 1 2 3 4 5 6 7 8 9
Attempting to move a file with index x
has 4 parts:
Step 1 is w:first where (x#g)>=s:f x
.
The rest is is if[0N<>w; g[w]-:s; g[x-1]+:s; b[w],:x]
move:{w:first where (x#g)>=s:f x; if[0N<>w; g[w]-:s; g[x-1]+:s; b[w],:x]}
Now, we apply this to each index in reverse order. This should modify the blocks and gaps accordingly.
move each reverse til n; show (b;g) 0 9 2 1 7 2 4 ,3 ,4 ,5 ,6 ,7 ,8 ,9 0 1 1 3 1 1 4 1 2 0
We can construct the new disk similarly to the old one. The main difference is that we want to keep track of files we've already used, as some can be repeated.
s:(); show "."^.Q.n r:raze {s,:l:x except s;(l where f l),y#0N}'[b;g] "00992111777.44.333....5555.6666.....8888.."
The exact same code from part 1 can now be reused to find the result.
sum r*til count r 2858
i:"J"$'first read0 `:i/9.txt
(f;g): flip 0N 2# i,0
d:raze {(x#z),y#0N}'[f;g;til n:count f]
C: count W: where N: null d
r:(sum not N)#@[d; W; :; C#reverse d where not N]
sum r*til count r
/ part 2:
b: 1#'til n
move:{w:first where (x#g)>=s:f x; if[0N<>w; g[w]-:s; g[x-1]+:s; b[w],:x]}
move each reverse til n;
s:(); r:raze {s,:l:x except s;(l where f l),y#0N}'[b;g]
sum r*til count r