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 }; }