aoc-2023/src/day5.zig

259 lines
7.7 KiB
Zig

const aoc = @import("./aoc.zig");
const std = @import("std");
const MappingRange = struct {
src_start: u64,
dest_start: u64,
size: u64,
};
const MappingSet = std.BoundedArray(MappingRange, 256);
const SeedsArray = std.BoundedArray(u64, 32);
const Input = struct {
seeds: SeedsArray,
seed_to_soil : MappingSet,
soil_to_fertilizer : MappingSet,
fertilizer_to_water : MappingSet,
water_to_light : MappingSet,
light_to_temperature : MappingSet,
temperature_to_humidity: MappingSet,
humidity_to_location : MappingSet,
fn get_mappings(self: *const Input) [7]*const MappingSet {
return [_]*const MappingSet{
&self.seed_to_soil,
&self.soil_to_fertilizer,
&self.fertilizer_to_water,
&self.water_to_light,
&self.light_to_temperature,
&self.temperature_to_humidity,
&self.humidity_to_location,
};
}
};
fn parse_seeds(result: *SeedsArray, text: []const u8) !void {
var i: usize = 0;
var tokens = std.mem.tokenizeScalar(u8, text, ' ');
while (tokens.next()) |token| : (i += 1) {
const number = try std.fmt.parseInt(u32, token, 10);
try result.append(number);
}
}
fn parse_mapping_range(line: []const u8) !MappingRange {
const first_space = std.mem.indexOfScalar(u8, line, ' ') orelse @panic("invalid format");
const last_space = std.mem.lastIndexOfScalar(u8, line, ' ') orelse @panic("invalid format");
return MappingRange{
.dest_start = try std.fmt.parseInt(u64, line[0..first_space], 10),
.src_start = try std.fmt.parseInt(u64, line[(first_space+1)..last_space], 10),
.size = try std.fmt.parseInt(u64, line[(last_space+1)..], 10)
};
}
fn parse_input(lines: []const []const u8) !Input {
var seeds = try SeedsArray.init(0);
var mapping_sets: [7]MappingSet = undefined;
for (&mapping_sets) |*set| {
set.* = try MappingSet.init(0);
}
const seeds_colon = std.mem.indexOfScalar(u8, lines[0], ':') orelse @panic("invalid format");
try parse_seeds(&seeds, lines[0][(seeds_colon+1)..]);
var mappign_set_idx: u32 = 0;
for (lines[2..]) |line| {
if (std.mem.endsWith(u8, line, "map:")) continue;
if (std.mem.eql(u8, line, "")) {
mappign_set_idx += 1;
continue;
}
try mapping_sets[mappign_set_idx].append(try parse_mapping_range(line));
}
return Input{
.seeds = seeds,
.seed_to_soil = mapping_sets[0],
.soil_to_fertilizer = mapping_sets[1],
.fertilizer_to_water = mapping_sets[2],
.water_to_light = mapping_sets[3],
.light_to_temperature = mapping_sets[4],
.temperature_to_humidity = mapping_sets[5],
.humidity_to_location = mapping_sets[6],
};
}
fn is_in_range(x: u64, range_start: u64, range_size: u64) bool {
return range_start <= x and x < (range_start + range_size);
}
fn apply_mapping(src: u64, mapping_set: *const MappingSet) u64 {
for (mapping_set.slice()) |mapping| {
if (is_in_range(src, mapping.src_start, mapping.size)) {
return mapping.dest_start + (src - mapping.src_start);
}
}
return src;
}
fn apply_seed_to_location(seed: u64, input: *const Input) u64 {
const mapping_sequence = input.get_mappings();
var value = seed;
inline for (mapping_sequence) |mapping_set| {
value = apply_mapping(value, mapping_set);
}
return value;
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const parsed = try parse_input(input.lines);
var lowest_location: ?u64 = null;
for (parsed.seeds.slice()) |seed| {
const value = apply_seed_to_location(seed, &parsed);
if (lowest_location) |location| {
lowest_location = @min(location, value);
} else {
lowest_location = value;
}
}
return .{ .uint = @intCast(lowest_location.?) };
}
fn remove_range(mapping_set: *MappingSet, from: u64, to: u64) !void {
for (0.., mapping_set.slice()) |i, *range| {
const range_start = range.src_start;
const range_end = range.src_start + range.size-1;
if (range_start == from and to == range_end) {
_ = mapping_set.swapRemove(i);
break;
} else if (range_start < from and to == range_end) {
range.size -= (to-from)+1;
break;
} else if (range_start == from and to < range_end) {
range.src_start = to;
range.dest_start += to - range_start;
range.size -= (to-from)+1;
break;
} else if (range_start < from and to < range_end) {
range.size = from - range_start + 1;
try mapping_set.append(MappingRange{
.src_start = to,
.dest_start = range.dest_start + (to - range.src_start),
.size = range_end - to + 1
});
break;
}
}
}
fn apply_mapping_to_range_set(src_set: *MappingSet, mapping_set: *const MappingSet) !void {
var new_src_set = try MappingSet.init(0);
var unmapped_ranges = try MappingSet.init(0);
try unmapped_ranges.appendSlice(src_set.slice());
for (src_set.slice()) |*src| {
for (0.., mapping_set.slice()) |i, mapping| {
_ = i;
const overlap_start = @max(src.dest_start, mapping.src_start);
const overlap_end = @min(src.dest_start+src.size-1, mapping.src_start+mapping.size-1);
if (overlap_start > overlap_end) { continue; }
const src_start = src.src_start + (overlap_start - src.dest_start);
const overlap_size = (overlap_end - overlap_start) + 1;
try remove_range(&unmapped_ranges, src_start, src_start+overlap_size-1);
try new_src_set.append(MappingRange{
.src_start = src_start,
.dest_start = mapping.dest_start + (overlap_start - mapping.src_start),
.size = overlap_size,
});
}
}
try new_src_set.appendSlice(unmapped_ranges.slice());
src_set.* = new_src_set;
}
pub fn part2(input: *aoc.Input) !aoc.Result {
const parsed = try parse_input(input.lines);
var seed_to_location = try MappingSet.init(0);
for (0..(parsed.seeds.len/2)) |i| {
const start_seed = parsed.seeds.get(2*i);
const seed_count = parsed.seeds.get(2*i+1);
try seed_to_location.append(MappingRange{
.src_start = start_seed,
.dest_start = start_seed,
.size = seed_count
});
}
inline for (parsed.get_mappings()) |mapping_set| {
try apply_mapping_to_range_set(&seed_to_location, mapping_set);
}
var lowest_location: ?u64 = null;
for (seed_to_location.slice()) |range| {
if (lowest_location == null) {
lowest_location = range.dest_start;
} else {
lowest_location = @min(lowest_location.?, range.dest_start);
}
}
return .{ .uint = @intCast(lowest_location.?) };
}
const example_input = [_][]const u8{
"seeds: 79 14 55 13",
"",
"seed-to-soil map:",
"50 98 2",
"52 50 48",
"",
"soil-to-fertilizer map:",
"0 15 37",
"37 52 2",
"39 0 15",
"",
"fertilizer-to-water map:",
"49 53 8",
"0 11 42",
"42 0 7",
"57 7 4",
"",
"water-to-light map:",
"88 18 7",
"18 25 70",
"",
"light-to-temperature map:",
"45 77 23",
"81 45 19",
"68 64 13",
"",
"temperature-to-humidity map:",
"0 69 1",
"1 0 69",
"",
"humidity-to-location map:",
"60 56 37",
"56 93 4",
};
test "part 1 example" {
try aoc.expectAnswerUInt(part1, 35, &example_input);
}
test "part 2 example" {
try aoc.expectAnswerUInt(part2, 46, &example_input);
}