advent-of-code/2023/day-05/common.zig

176 lines
4.6 KiB
Zig

const std = @import("std");
const alloc = std.heap.page_allocator;
const List = std.ArrayList;
pub const Mappings = std.AutoHashMap(MappingKey, Mapping);
pub const Idx = u64;
pub const SeedList = List(Idx);
pub const MappingType = enum {
Seed,
Soil,
Fertilizer,
Water,
Light,
Temperature,
Humidity,
Location,
};
pub const MappingKey = struct {
MappingType,
MappingType,
};
pub const Mapping = struct {
dest_ranges: []MappingRange,
source_ranges: []MappingRange,
pub fn get(self: Mapping, val: Idx) Idx {
for (self.source_ranges, 0..) |src_range, i| {
if (val >= src_range.start and val < src_range.end) {
const dest_range = self.dest_ranges[i];
const dest_offset = (val - src_range.start);
return dest_range.start + dest_offset;
}
}
return val;
}
};
pub const Input = struct {
mappings: Mappings,
seeds: SeedList,
};
pub const MappingRange = struct {
start: Idx,
end: Idx,
};
pub fn parse(data: []const u8) !Input {
var lines = std.mem.tokenizeSequence(u8, data, "\n");
var mappings = Mappings.init(alloc);
var seeds: SeedList = undefined;
var mapping_types: [2]MappingType = undefined;
var dest_ranges = List(MappingRange).init(alloc);
var source_ranges = List(MappingRange).init(alloc);
var collect_mapping = false;
while (lines.next()) |line| {
if (std.mem.startsWith(u8, line, "seeds:")) {
seeds = try parse_seed_list(line);
} else if (std.mem.endsWith(u8, line, "map:")) {
mapping_types = parse_mapping_header(line);
} else {
const range = try parse_mapping_body(line);
try dest_ranges.append(range[0]);
try source_ranges.append(range[1]);
}
if (lines.peek()) |next_line| {
const new_mapping = std.mem.endsWith(u8, next_line, "map:");
collect_mapping = new_mapping and dest_ranges.items.len != 0;
} else {
collect_mapping = true;
}
if (collect_mapping) {
const dranges = try dest_ranges.toOwnedSlice();
const sranges = try source_ranges.toOwnedSlice();
const mapping = .{
.dest_ranges = dranges,
.source_ranges = sranges,
};
const key = .{
mapping_types[0],
mapping_types[1],
};
try mappings.put(key, mapping);
}
}
const parsed = .{ .mappings = mappings, .seeds = seeds };
return parsed;
}
fn parse_seed_list(line: []const u8) !SeedList {
var list = SeedList.init(alloc);
var seeds = std.mem.tokenizeSequence(u8, line, " ");
_ = seeds.next();
while (seeds.next()) |seed| {
const seed_num = try std.fmt.parseInt(Idx, seed, 10);
try list.append(seed_num);
}
return list;
}
fn parse_mapping_header(line: []const u8) [2]MappingType {
const map_types = std.mem.trimRight(u8, line, " map:");
var map_types_iter = std.mem.tokenizeSequence(u8, map_types, "-to-");
var types: [2]MappingType = undefined;
var i: u8 = 0;
while (map_types_iter.next()) |map_type| {
if (std.mem.eql(u8, map_type, "seed")) {
types[i] = MappingType.Seed;
} else if (std.mem.eql(u8, map_type, "soil")) {
types[i] = MappingType.Soil;
} else if (std.mem.eql(u8, map_type, "fertilizer")) {
types[i] = MappingType.Fertilizer;
} else if (std.mem.eql(u8, map_type, "water")) {
types[i] = MappingType.Water;
} else if (std.mem.eql(u8, map_type, "light")) {
types[i] = MappingType.Light;
} else if (std.mem.eql(u8, map_type, "temperature")) {
types[i] = MappingType.Temperature;
} else if (std.mem.eql(u8, map_type, "humidity")) {
types[i] = MappingType.Humidity;
} else if (std.mem.eql(u8, map_type, "location")) {
types[i] = MappingType.Location;
} else {
unreachable;
}
i += 1;
}
return types;
}
fn parse_mapping_body(line: []const u8) ![2]MappingRange {
var values = std.mem.tokenizeScalar(u8, line, ' ');
var parsed: [3]Idx = undefined;
var i: u8 = 0;
while (values.next()) |val| {
const num = try std.fmt.parseInt(Idx, val, 10);
parsed[i] = num;
i += 1;
}
const dest_range = .{
.start = parsed[0],
.end = parsed[0] + parsed[2],
};
const source_range = .{
.start = parsed[1],
.end = parsed[1] + parsed[2],
};
return .{ dest_range, source_range };
}