Compare commits
2 Commits
1d9a506f76
...
77ff15f997
Author | SHA1 | Date | |
---|---|---|---|
77ff15f997 | |||
92662ddb7f |
29
src/cli.zig
29
src/cli.zig
@ -2,13 +2,18 @@ const std = @import("std");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
const Action = enum { Run, GetInput };
|
||||||
|
|
||||||
allocator: Allocator,
|
allocator: Allocator,
|
||||||
|
|
||||||
|
action: Action,
|
||||||
day: u32,
|
day: u32,
|
||||||
part: u32,
|
part: u32,
|
||||||
input_file: []u8,
|
input_file: []u8,
|
||||||
|
|
||||||
fn print_usage(program: []const u8) void {
|
fn print_usage(program: []const u8) void {
|
||||||
std.debug.print("Usage: {s} <day> <part> [input.txt]\n", .{program});
|
std.debug.print("Usage: {s} <day> <part> [input.txt] Run solution\n", .{program});
|
||||||
|
std.debug.print(" {s} get-input <day> [input.txt] Get input. AOC_SESSSION environment variable needs to be defined\n", .{program});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(allocator: Allocator) !Self {
|
pub fn init(allocator: Allocator) !Self {
|
||||||
@ -17,11 +22,19 @@ pub fn init(allocator: Allocator) !Self {
|
|||||||
const program = try allocator.dupe(u8, args.next().?);
|
const program = try allocator.dupe(u8, args.next().?);
|
||||||
defer allocator.free(program);
|
defer allocator.free(program);
|
||||||
|
|
||||||
|
var action = Action.Run;
|
||||||
var day: u32 = 0;
|
var day: u32 = 0;
|
||||||
var part: u32 = 0;
|
var part: u32 = 0;
|
||||||
var input_file = try allocator.dupe(u8, "input.txt");
|
var input_file = try allocator.dupe(u8, "input.txt");
|
||||||
|
errdefer allocator.free(input_file);
|
||||||
|
|
||||||
var i: u32 = 0;
|
var i: u32 = 0;
|
||||||
while (args.next()) |arg| {
|
while (args.next()) |arg| {
|
||||||
|
if (i == 0 and std.mem.eql(u8, arg[0..], "get-input")) {
|
||||||
|
action = .GetInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == .Run) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
day = std.fmt.parseInt(u32, arg, 10) catch {
|
day = std.fmt.parseInt(u32, arg, 10) catch {
|
||||||
std.debug.print("ERROR: Invalid day '{s}'\n", .{arg});
|
std.debug.print("ERROR: Invalid day '{s}'\n", .{arg});
|
||||||
@ -41,10 +54,23 @@ pub fn init(allocator: Allocator) !Self {
|
|||||||
allocator.free(input_file);
|
allocator.free(input_file);
|
||||||
input_file = try allocator.dupe(u8, arg);
|
input_file = try allocator.dupe(u8, arg);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (i == 1) {
|
||||||
|
day = std.fmt.parseInt(u32, arg, 10) catch {
|
||||||
|
std.debug.print("ERROR: Invalid day '{s}'\n", .{arg});
|
||||||
|
return error.cli;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (i == 2) {
|
||||||
|
allocator.free(input_file);
|
||||||
|
input_file = try allocator.dupe(u8, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (i < 2) {
|
if (i < 2) {
|
||||||
print_usage(program);
|
print_usage(program);
|
||||||
return error.cli;
|
return error.cli;
|
||||||
@ -52,6 +78,7 @@ pub fn init(allocator: Allocator) !Self {
|
|||||||
|
|
||||||
return Self{
|
return Self{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.action = action,
|
||||||
.day = day,
|
.day = day,
|
||||||
.part = part,
|
.part = part,
|
||||||
.input_file = input_file
|
.input_file = input_file
|
||||||
|
265
src/day5.zig
Normal file
265
src/day5.zig
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
92
src/main.zig
92
src/main.zig
@ -2,6 +2,7 @@ const std = @import("std");
|
|||||||
const aoc = @import("./aoc.zig");
|
const aoc = @import("./aoc.zig");
|
||||||
const cli = @import("./cli.zig");
|
const cli = @import("./cli.zig");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
const StringArray = std.ArrayList([]const u8);
|
const StringArray = std.ArrayList([]const u8);
|
||||||
const DayHashMap = std.StringHashMap(aoc.Day);
|
const DayHashMap = std.StringHashMap(aoc.Day);
|
||||||
|
|
||||||
@ -24,21 +25,14 @@ const Days = [_]aoc.Day{
|
|||||||
create_day(@import("day2.zig")),
|
create_day(@import("day2.zig")),
|
||||||
create_day(@import("day3.zig")),
|
create_day(@import("day3.zig")),
|
||||||
create_day(@import("day4.zig")),
|
create_day(@import("day4.zig")),
|
||||||
|
create_day(@import("day5.zig")),
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main() !u8 {
|
fn kilobytes(count: u32) u32 {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
return count * 1024;
|
||||||
const allocator = gpa.allocator();
|
|
||||||
defer {
|
|
||||||
if (gpa.deinit() == .leak) @panic("Leaked memory");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var args = cli.init(allocator) catch |err| switch (err) {
|
fn run(allocator: Allocator, args: *cli) !u8 {
|
||||||
error.cli => return 255,
|
|
||||||
else => return err
|
|
||||||
} ;
|
|
||||||
defer args.deinit();
|
|
||||||
|
|
||||||
if (args.day > Days.len) {
|
if (args.day > Days.len) {
|
||||||
std.debug.print("ERROR: Day {} does not exist\n", .{args.day});
|
std.debug.print("ERROR: Day {} does not exist\n", .{args.day});
|
||||||
return 255;
|
return 255;
|
||||||
@ -97,9 +91,85 @@ pub fn main() !u8 {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_input(allocator: Allocator, args: *cli) !u8 {
|
||||||
|
var client = std.http.Client{ .allocator = allocator };
|
||||||
|
defer client.deinit();
|
||||||
|
|
||||||
|
var headers = std.http.Headers{ .allocator = allocator };
|
||||||
|
defer headers.deinit();
|
||||||
|
|
||||||
|
if (!try std.process.hasEnvVar(allocator, "AOC_SESSION")) {
|
||||||
|
std.debug.print("ERROR: Missing environment variable AOC_SESSION\n", .{});
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aoc_session = try std.process.getEnvVarOwned(allocator, "AOC_SESSION");
|
||||||
|
defer allocator.free(aoc_session);
|
||||||
|
|
||||||
|
var cookie_header = try std.fmt.allocPrint(allocator, "session={s}", .{aoc_session});
|
||||||
|
defer allocator.free(cookie_header);
|
||||||
|
|
||||||
|
try headers.append("cookie", cookie_header);
|
||||||
|
try headers.append("accept", "*/*");
|
||||||
|
|
||||||
|
var url_str = try std.fmt.allocPrint(allocator, "https://adventofcode.com/2023/day/{}/input", .{args.day});
|
||||||
|
defer allocator.free(url_str);
|
||||||
|
|
||||||
|
const uri = std.Uri.parse(url_str) catch unreachable;
|
||||||
|
|
||||||
|
var request = try client.request(.GET, uri, headers, .{});
|
||||||
|
defer request.deinit();
|
||||||
|
|
||||||
|
try request.start();
|
||||||
|
try request.wait();
|
||||||
|
|
||||||
|
if (request.response.status != .ok) {
|
||||||
|
if (request.response.status == .not_found) {
|
||||||
|
std.debug.print("ERROR: Day not found\n", .{});
|
||||||
|
} else if (request.response.status == .bad_request) {
|
||||||
|
std.debug.print("ERROR: Invalid session token\n", .{});
|
||||||
|
} else {
|
||||||
|
std.debug.print("ERROR: unknown error, status code {}\n", .{request.response.status});
|
||||||
|
}
|
||||||
|
|
||||||
|
return 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = try request.reader().readAllAlloc(allocator, kilobytes(8));
|
||||||
|
defer allocator.free(body);
|
||||||
|
|
||||||
|
var file = try std.fs.cwd().createFile(args.input_file, .{ });
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
try file.writeAll(body);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !u8 {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
const allocator = gpa.allocator();
|
||||||
|
defer {
|
||||||
|
if (gpa.deinit() == .leak) @panic("Leaked memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = cli.init(allocator) catch |err| switch (err) {
|
||||||
|
error.cli => return 255,
|
||||||
|
else => return err
|
||||||
|
} ;
|
||||||
|
defer args.deinit();
|
||||||
|
|
||||||
|
if (args.action == .Run) {
|
||||||
|
return run(allocator, &args);
|
||||||
|
} else {
|
||||||
|
return get_input(allocator, &args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
_ = @import("day1.zig");
|
_ = @import("day1.zig");
|
||||||
_ = @import("day2.zig");
|
_ = @import("day2.zig");
|
||||||
_ = @import("day3.zig");
|
_ = @import("day3.zig");
|
||||||
_ = @import("day4.zig");
|
_ = @import("day4.zig");
|
||||||
|
_ = @import("day5.zig");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user