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

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