Solutions to 09
This commit is contained in:
parent
c2955cd03e
commit
4fdbce7204
1
NOTES.md
1
NOTES.md
@ -24,6 +24,7 @@ Hmmmm, right now it seems even worse than GoLang. Though the Error handling is _
|
|||||||
* Switching on strings
|
* Switching on strings
|
||||||
* [Iterating over values of an enum](https://zig.guide/language-basics/enums) - [this](https://ziggit.dev/t/iterating-over-a-packed-enum/6530) suggests that it's possible, but testing indicates that that only works at comptime.
|
* [Iterating over values of an enum](https://zig.guide/language-basics/enums) - [this](https://ziggit.dev/t/iterating-over-a-packed-enum/6530) suggests that it's possible, but testing indicates that that only works at comptime.
|
||||||
* [Sets](https://github.com/ziglang/zig/issues/6919) (though, as the top comment points out, that's not _too_ bad as you can abuse a HashMap for it)
|
* [Sets](https://github.com/ziglang/zig/issues/6919) (though, as the top comment points out, that's not _too_ bad as you can abuse a HashMap for it)
|
||||||
|
* [Descending ranges](https://github.com/ziglang/zig/issues/14701) - i.e. `for (10..0)`. Not a big deal, tbf, but annoying when needed!
|
||||||
|
|
||||||
## Not "missing", but...
|
## Not "missing", but...
|
||||||
|
|
||||||
|
1
inputs/09/real.txt
Normal file
1
inputs/09/real.txt
Normal file
File diff suppressed because one or more lines are too long
1
inputs/09/test.txt
Normal file
1
inputs/09/test.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2333133121414131402
|
@ -251,11 +251,11 @@ fn divide(num: i32, denom: u32) i32 {
|
|||||||
|
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
// test "part_one" {
|
test "part_one" {
|
||||||
// const part_one_response = try part_one(true);
|
const part_one_response = try part_one(true);
|
||||||
// print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
||||||
// try expect(part_one_response == 14);
|
try expect(part_one_response == 14);
|
||||||
// }
|
}
|
||||||
|
|
||||||
test "greatest_common_factor" {
|
test "greatest_common_factor" {
|
||||||
try expect(gcf(18, 27) == 9);
|
try expect(gcf(18, 27) == 9);
|
||||||
|
197
solutions/09.zig
Normal file
197
solutions/09.zig
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const print = std.debug.print;
|
||||||
|
const util = @import("util.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
const response = try part_two(false);
|
||||||
|
print("{}\n", .{response});
|
||||||
|
}
|
||||||
|
|
||||||
|
// There's _probably_ a super-clever way of doing this without actually expanding out the disk map, using dual-pointers
|
||||||
|
// and keeping track of how much of each "chunk" has been consumed at each step - but, after thinking it over for a bit,
|
||||||
|
// I couldn't figure out how to determine when to terminate (i.e. when enough blocks have been moved to make everything
|
||||||
|
// contiguous), so I'm going for the naive approach - expand out the disk-map, then swap blocks one-by-one.
|
||||||
|
//
|
||||||
|
// ...huh, that executed _way_ faster than I feared.
|
||||||
|
fn part_one(is_test_case: bool) !u64 {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const input_file = try util.getInputFile("09", is_test_case);
|
||||||
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
||||||
|
defer allocator.free(data);
|
||||||
|
|
||||||
|
var disk = std.ArrayList(?u32).init(allocator);
|
||||||
|
defer disk.deinit();
|
||||||
|
|
||||||
|
var is_in_disk = true;
|
||||||
|
var disk_id: u32 = 0;
|
||||||
|
for (data) |c| {
|
||||||
|
if (c == '\n') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const count = c - 48; // Why 48? Just because, I guess :shrug: ASCII for `2` is 50, and so...
|
||||||
|
for (0..count) |_| {
|
||||||
|
if (is_in_disk) {
|
||||||
|
try disk.append(disk_id);
|
||||||
|
} else {
|
||||||
|
try disk.append(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_in_disk) {
|
||||||
|
disk_id += 1;
|
||||||
|
}
|
||||||
|
is_in_disk = !is_in_disk;
|
||||||
|
}
|
||||||
|
print("DEBUG - actual disk is {any}\n", .{disk.items});
|
||||||
|
|
||||||
|
// We could _probably_ iterate over the disk from both ends and _just_ sum up the calculated values to get the
|
||||||
|
// answer (which would be more space-efficient) - but that way I'd have no chance of debugging if something goes
|
||||||
|
// wrong. I'll instead construct the _actually_ defragged disk, so that I can visually compare it with how it's
|
||||||
|
// supposed to look.
|
||||||
|
var left_pointer: usize = 0;
|
||||||
|
var right_pointer: usize = disk.items.len - 1;
|
||||||
|
var defragged_disk = std.ArrayList(u32).init(allocator);
|
||||||
|
defer defragged_disk.deinit();
|
||||||
|
while (true) : (left_pointer += 1) {
|
||||||
|
const value_in_original_disk = disk.items[left_pointer];
|
||||||
|
if (value_in_original_disk != null) {
|
||||||
|
try defragged_disk.append(value_in_original_disk.?);
|
||||||
|
} else {
|
||||||
|
// Position of left_pointer is inside a space in the original disk =>
|
||||||
|
// Move the right_pointer left until it finds a block, then insert that into the defragged disk
|
||||||
|
while (disk.items[right_pointer] == null) {
|
||||||
|
right_pointer -= 1;
|
||||||
|
}
|
||||||
|
try defragged_disk.append(disk.items[right_pointer].?);
|
||||||
|
right_pointer -= 1;
|
||||||
|
}
|
||||||
|
if (right_pointer == left_pointer) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("DEBUG - defragged disk is {any}\n", .{defragged_disk.items});
|
||||||
|
|
||||||
|
var total: u64 = 0;
|
||||||
|
var idx: u32 = 0;
|
||||||
|
while (idx < defragged_disk.items.len) : (idx += 1) {
|
||||||
|
total += idx * defragged_disk.items[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part_two(is_test_case: bool) !u64 {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
const input_file = try util.getInputFile("09", is_test_case);
|
||||||
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
||||||
|
defer allocator.free(data);
|
||||||
|
|
||||||
|
var disk = std.ArrayList(?u32).init(allocator);
|
||||||
|
defer disk.deinit();
|
||||||
|
|
||||||
|
var is_in_file = true;
|
||||||
|
var file_id: u32 = 0;
|
||||||
|
var file_start_indices = std.AutoHashMap(u32, usize).init(allocator);
|
||||||
|
defer file_start_indices.deinit();
|
||||||
|
// If we wanted, we could probably store "DiskInfo" in a struct and thus in a single HashMap, but...ehhh...
|
||||||
|
var file_sizes = std.AutoHashMap(u32, usize).init(allocator);
|
||||||
|
defer file_sizes.deinit();
|
||||||
|
for (data) |c| {
|
||||||
|
if (c == '\n') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const count = c - 48; // Why 48? Just because, I guess :shrug: ASCII for `2` is 50, and so...
|
||||||
|
if (is_in_file) {
|
||||||
|
try file_start_indices.put(file_id, disk.items.len);
|
||||||
|
}
|
||||||
|
for (0..count) |_| {
|
||||||
|
if (is_in_file) {
|
||||||
|
try disk.append(file_id);
|
||||||
|
try file_sizes.put(file_id, count);
|
||||||
|
} else {
|
||||||
|
try disk.append(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!is_in_file) {
|
||||||
|
file_id += 1;
|
||||||
|
}
|
||||||
|
is_in_file = !is_in_file;
|
||||||
|
}
|
||||||
|
const highest_file_id = file_id;
|
||||||
|
print("DEBUG - actual disk is {any}\n", .{disk.items});
|
||||||
|
print("DEBUG - highest_file_id is {}\n", .{highest_file_id});
|
||||||
|
print("DEBUG - file_start_indices is:\n", .{});
|
||||||
|
var it_dsi = file_start_indices.iterator();
|
||||||
|
while (it_dsi.next()) |kv| {
|
||||||
|
print(" {}: {}\n", .{ kv.key_ptr.*, kv.value_ptr.* });
|
||||||
|
}
|
||||||
|
print("DEBUG - file_sizes is:\n", .{});
|
||||||
|
var it_ds = file_sizes.iterator();
|
||||||
|
while (it_ds.next()) |kv| {
|
||||||
|
print(" {}: {}\n", .{ kv.key_ptr.*, kv.value_ptr.* });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zig does not support decreasing ranges :'(
|
||||||
|
// for (highest_file_id..0) |file_id_to_move| {
|
||||||
|
for (0..highest_file_id) |file_i| {
|
||||||
|
const file_id_to_move = highest_file_id - file_i;
|
||||||
|
print("DEBUG - trying to move file with id {}\n", .{file_id_to_move});
|
||||||
|
const file_id_as_u32: u32 = @intCast(file_id_to_move);
|
||||||
|
const original_start_index = file_start_indices.get(file_id_as_u32).?;
|
||||||
|
const file_size = file_sizes.get(file_id_as_u32).?;
|
||||||
|
var left_pointer: usize = 0;
|
||||||
|
// I don't _think_ we can be smarter about jumping forward more than 1 at a time - at least, not without
|
||||||
|
// returning information from within `hasSpaceToMove`. In particular, we can't jump forward `file_size` -
|
||||||
|
// consider trying to move a file of size 4 into the following disk:
|
||||||
|
// X..X....X
|
||||||
|
// if we jumped forward 4 from index 1, we'd land partway through the following 4-gap, and miss it.
|
||||||
|
while (left_pointer < original_start_index) : (left_pointer += 1) {
|
||||||
|
if (hasSpaceToMove(disk, left_pointer, file_size)) {
|
||||||
|
for (0..file_size) |i| {
|
||||||
|
disk.items[left_pointer + i] = file_id_as_u32;
|
||||||
|
disk.items[original_start_index + i] = null;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("DEBUG - defragged disk is {any}\n", .{disk.items});
|
||||||
|
|
||||||
|
var total: u64 = 0;
|
||||||
|
var idx: u32 = 0;
|
||||||
|
while (idx < disk.items.len) : (idx += 1) {
|
||||||
|
total += idx * (disk.items[idx] orelse 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hasSpaceToMove(disk: std.ArrayList(?u32), pointer: usize, file_size: usize) bool {
|
||||||
|
for (0..file_size) |i| {
|
||||||
|
if (disk.items[pointer + i] != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("DEBUG - found space to move - to location {}\n", .{pointer});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expect = std.testing.expect;
|
||||||
|
|
||||||
|
// test "part_one" {
|
||||||
|
// const part_one_response = try part_one(true);
|
||||||
|
// print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
||||||
|
// try expect(part_one_response == 1928);
|
||||||
|
// }
|
||||||
|
|
||||||
|
test "part_two" {
|
||||||
|
const part_two_response = try part_two(true);
|
||||||
|
print("DEBUG - part_two_response is {}\n", .{part_two_response});
|
||||||
|
try expect(part_two_response == 2858);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user