You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
4.4 KiB
Zig
142 lines
4.4 KiB
Zig
12 months ago
|
const std = @import("std");
|
||
|
|
||
|
const Range = struct { destination: u64, source: u64, length: u64 };
|
||
|
const Map = struct { from: []const u8, to: []const u8, ranges: std.ArrayList(Range) };
|
||
|
|
||
|
pub fn main() !void {
|
||
|
const content = @embedFile("data.txt");
|
||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||
|
const allocator = gpa.allocator();
|
||
|
|
||
|
// split the file content on empty lines
|
||
|
var sections = std.mem.split(u8, content, "\n\n");
|
||
|
|
||
|
// parse out the first line, collecting seeds into an ArrayList
|
||
|
var seedSection = std.mem.split(u8, sections.next().?, " ");
|
||
|
var seeds = std.ArrayList(u64).init(allocator);
|
||
|
defer seeds.deinit();
|
||
|
_ = seedSection.next();
|
||
|
while (seedSection.next()) |s| {
|
||
|
try seeds.append(try std.fmt.parseInt(u64, s, 10));
|
||
|
}
|
||
|
|
||
|
var maps = std.ArrayList(Map).init(allocator);
|
||
|
defer maps.deinit();
|
||
|
|
||
|
// iterate over each section, parse into Map structs, append them onto the maps ArrayList
|
||
|
while (sections.next()) |s| {
|
||
|
var lines = std.mem.split(u8, s, "\n");
|
||
|
var firstLine = lines.next().?;
|
||
|
var words = std.mem.split(u8, firstLine, " ");
|
||
|
// grab the first "word" of the first line, and split it on -
|
||
|
var parts = std.mem.split(u8, words.next().?, "-");
|
||
|
const from = parts.next().?;
|
||
|
_ = parts.next();
|
||
|
const to = parts.next().?;
|
||
|
|
||
|
var ranges = std.ArrayList(Range).init(allocator);
|
||
|
// TODO: do I need to deinit ranges... somewhere?
|
||
|
while (lines.next()) |line| {
|
||
|
if (line.len == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
var nums = std.mem.split(u8, line, " ");
|
||
|
var destination = try std.fmt.parseInt(u64, nums.next().?, 10);
|
||
|
var source = try std.fmt.parseInt(u64, nums.next().?, 10);
|
||
|
var length = try std.fmt.parseInt(u64, nums.next().?, 10);
|
||
|
const range = Range{ .destination = destination, .source = source, .length = length };
|
||
|
try ranges.append(range);
|
||
|
}
|
||
|
const map = Map{ .from = from, .to = to, .ranges = ranges };
|
||
|
try maps.append(map);
|
||
|
}
|
||
|
|
||
|
var lowestLocation: ?u64 = null;
|
||
|
for (seeds.items) |seed| {
|
||
|
// loop starting from the "seed" map until we have a location
|
||
|
var from: []const u8 = "seed";
|
||
|
var val = seed;
|
||
|
|
||
|
while (!std.mem.eql(u8, from, "location")) {
|
||
|
const map = findMap(from, maps).?;
|
||
|
val = findNewValue(val, map);
|
||
|
from = map.to;
|
||
|
}
|
||
|
if (lowestLocation) |l| {
|
||
|
if (val < l) {
|
||
|
lowestLocation = val;
|
||
|
}
|
||
|
} else {
|
||
|
lowestLocation = val;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std.debug.print("Part 1 {?d}\n", .{lowestLocation});
|
||
|
|
||
|
// Part 2
|
||
|
// Can we go backwards?
|
||
|
var i: u64 = 0;
|
||
|
outer: while (true) {
|
||
|
var to: []const u8 = "location";
|
||
|
var val = i;
|
||
|
|
||
|
while (!std.mem.eql(u8, to, "seed")) {
|
||
|
const map = findMapReverse(to, maps).?;
|
||
|
val = findNewValueReverse(val, map);
|
||
|
to = map.from;
|
||
|
}
|
||
|
|
||
|
var idx: u8 = 0;
|
||
|
while (idx < seeds.items.len) : (idx += 2) {
|
||
|
const lower = seeds.items[idx];
|
||
|
const upper = lower + seeds.items[idx + 1];
|
||
|
if (val >= lower and val < upper) {
|
||
|
break :outer;
|
||
|
}
|
||
|
}
|
||
|
i += 1;
|
||
|
}
|
||
|
|
||
|
std.debug.print("Part 2 {?d}\n", .{i});
|
||
|
}
|
||
|
|
||
|
// given the "from" type, find the map that we should follow
|
||
|
fn findMap(from: []const u8, maps: std.ArrayList(Map)) ?Map {
|
||
|
for (maps.items) |map| {
|
||
|
if (std.mem.eql(u8, map.from, from)) {
|
||
|
return map;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
fn findNewValue(val: u64, map: Map) u64 {
|
||
|
for (map.ranges.items) |r| {
|
||
|
if (val >= r.source and val < r.source + r.length) {
|
||
|
return r.destination + val - r.source;
|
||
|
}
|
||
|
}
|
||
|
// if no range fits, return the original value
|
||
|
return val;
|
||
|
}
|
||
|
|
||
|
// given the "to" type, find the map that we should follow
|
||
|
fn findMapReverse(to: []const u8, maps: std.ArrayList(Map)) ?Map {
|
||
|
for (maps.items) |map| {
|
||
|
if (std.mem.eql(u8, map.to, to)) {
|
||
|
return map;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
fn findNewValueReverse(val: u64, map: Map) u64 {
|
||
|
for (map.ranges.items) |r| {
|
||
|
if (val >= r.destination and val < r.destination + r.length) {
|
||
|
return r.source + val - r.destination;
|
||
|
}
|
||
|
}
|
||
|
// if no range fits, return the original value
|
||
|
return val;
|
||
|
}
|