Solve day 9 of AoC 2024

This commit is contained in:
Ivan R. 2024-12-14 22:41:37 +05:00
parent 0c6bf7e054
commit 4e7c94b473
Signed by: lumin
GPG key ID: E0937DC7CD6D3817
7 changed files with 333 additions and 0 deletions

1
advent-of-code/2024/day_09/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,19 @@
TARGET_EXEC := main
BUILD_DIR := ./build
CFLAGS := -Wall -Wextra -g
SRCS := $(shell find . -name '*.c')
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
$(CC) $(OBJS) -o $@
# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
mkdir -p $(dir $@)
$(CC) $(CFLAGS) -c $< -o $@
.PHONY: clean
clean:
rm -r $(BUILD_DIR)

View file

@ -0,0 +1,103 @@
# Day 9: Disk Fragmenter
[Advent of Code 2024](https://adventofcode.com/2024/day/9)
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 like 90909 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 map 12345 has three files: a one-block file with ID 0, a three-block file with ID 1, and a five-block file with ID 2.
Using one character for each block where digits are the file ID and . is free space, the disk map 12345 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?
## 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?

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,139 @@
#include <string.h>
#include <stdlib.h>
#include "lib.h"
struct Vec expand_disk_map(char* input) {
size_t input_length = strlen(input);
size_t required_length = 0;
for (size_t i = 0; i < input_length; i++) {
required_length += input[i] - '0';
}
int* data = malloc(sizeof(int) * required_length);
int id = 0;
int data_idx = 0;
for (size_t i = 0; i < input_length; i++) {
size_t digit = input[i] - '0';
for (size_t j = 0; j < digit; j++) {
if (i % 2 == 0) {
data[data_idx] = id;
} else {
data[data_idx] = -1;
}
data_idx++;
}
if (i % 2 == 0) {
id++;
}
}
struct Vec vec = { required_length, data };
return vec;
}
void compress_fs(struct Vec vec) {
size_t last_digit_search_start = vec.length - 1;
for (size_t i = 0; i < vec.length; i++) {
if (vec.data[i] != -1) {
continue;
}
size_t j;
for (j = last_digit_search_start; j > i; j--) {
if (vec.data[j] == -1) {
continue;
}
break;
}
if (j == i) {
break;
}
last_digit_search_start = j - 1;
vec.data[i] = vec.data[j];
vec.data[j] = -1;
}
}
/**
* Find a range of spaces of the required length.
* Returns the index of the start of the range.
* Returns 0 if the range is not found.
*/
size_t find_space(struct Vec vec, size_t max_idx, size_t len) {
size_t counter = 0;
for (size_t i = 0; i <= max_idx; i++) {
if (vec.data[i] != -1) {
counter = 0;
continue;
}
counter++;
if (counter == len) {
return i + 1 - counter;
}
}
return 0;
}
void compress_fs_by_blocks(struct Vec vec) {
int id = -1;
size_t block_end;
for (size_t i = vec.length - 1; i > 0; i--) {
if (vec.data[i] == id) {
continue;
}
// Last digit of the block was found.
if (id == -1) {
id = vec.data[i];
block_end = i;
continue;
}
// First digit of the block was found.
size_t block_start = i + 1;
size_t block_len = block_end - block_start + 1;
size_t space_idx = find_space(vec, block_start - 1, block_len);
// Space was found.
if (space_idx != 0) {
for (size_t si = space_idx; si < space_idx + block_len; si++) {
vec.data[si] = id;
}
for (size_t bi = block_start; bi <= block_end; bi++) {
vec.data[bi] = -1;
}
}
id = vec.data[i];
block_end = i;
}
}
long calc_checksum(struct Vec vec) {
long sum = 0;
for (size_t i = 0; i < vec.length; i++) {
if (vec.data[i] == -1) {
continue;
}
sum += i * vec.data[i];
}
return sum;
}
struct Vec copy_vec(struct Vec vec) {
int data_size = sizeof(int) * vec.length;
int* data_copy = malloc(data_size);
memcpy(data_copy, vec.data, data_size);
struct Vec vec_copy = { vec.length, data_copy };
return vec_copy;
}

View file

@ -0,0 +1,26 @@
#pragma once
struct Vec{
size_t length;
int* data;
};
/**
* Expand compressed disk map.
* For example, expand_blocks("123") returns [0, -1, -1, 1, 1, 1].
* The caller must free the memory of the returned array.
*/
struct Vec expand_disk_map(char*);
/**
* Compress the data in-place.
*/
void compress_fs(struct Vec);
/**
* Move the blocks in-place.
*/
void compress_fs_by_blocks(struct Vec);
long calc_checksum(struct Vec);
struct Vec copy_vec(struct Vec);

View file

@ -0,0 +1,44 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib.h"
#define LINE_LEN 20000
int main() {
FILE* file_ptr = fopen("input.txt", "r");
if (file_ptr == NULL) {
printf("Can't open input file\n");
return -1;
}
char input[LINE_LEN];
fread(input, LINE_LEN, 1, file_ptr);
size_t input_length = strlen(input);
if (input_length < 1) {
printf("Empty input.\n");
return -1;
}
if (input_length > 0 && input[input_length-1] == '\n') {
input[input_length-1] = '\0';
}
struct Vec blocks = expand_disk_map(input);
struct Vec blocks_copy = copy_vec(blocks);
compress_fs(blocks);
long checksum = calc_checksum(blocks);
printf("Part 1: %ld\n", checksum);
compress_fs_by_blocks(blocks_copy);
checksum = calc_checksum(blocks_copy);
printf("Part 2: %ld\n", checksum);
free(blocks.data);
free(blocks_copy.data);
fclose(file_ptr);
return 0;
}