From 92662ddb7fa18a2827a0abd9c23cc78f60e94356 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sat, 16 Dec 2023 12:18:54 +0200 Subject: [PATCH] solve day 5 --- src/cli.zig | 2 + src/day5.zig | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 2 + 3 files changed, 269 insertions(+) create mode 100644 src/day5.zig diff --git a/src/cli.zig b/src/cli.zig index 34fc05c..fdf6356 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -20,6 +20,8 @@ pub fn init(allocator: Allocator) !Self { var day: u32 = 0; var part: u32 = 0; var input_file = try allocator.dupe(u8, "input.txt"); + errdefer allocator.free(input_file); + var i: u32 = 0; while (args.next()) |arg| { if (i == 0) { diff --git a/src/day5.zig b/src/day5.zig new file mode 100644 index 0000000..e4b490c --- /dev/null +++ b/src/day5.zig @@ -0,0 +1,265 @@ +const aoc = @import("./aoc.zig"); +const std = @import("std"); +const expect = std.testing.expect; + +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| { + var 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" { + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; + var actual = try part1(&input); + var expected: u32 = 35; + try std.testing.expectEqual(expected, actual.uint); +} + +test "part 2 example" { + var input = aoc.Input{ .lines = &example_input, .allocator = std.testing.allocator }; + var actual = try part2(&input); + var expected: u32 = 46; + try std.testing.expectEqual(expected, actual.uint); +} + diff --git a/src/main.zig b/src/main.zig index 3aee4dd..1debd7e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,6 +24,7 @@ const Days = [_]aoc.Day{ create_day(@import("day2.zig")), create_day(@import("day3.zig")), create_day(@import("day4.zig")), + create_day(@import("day5.zig")), }; pub fn main() !u8 { @@ -102,4 +103,5 @@ test { _ = @import("day2.zig"); _ = @import("day3.zig"); _ = @import("day4.zig"); + _ = @import("day5.zig"); }