Compare commits

..

2 Commits

Author SHA1 Message Date
413e13b601 solve day 17 2024-04-22 00:45:39 +03:00
5c079539df update to zig 0.12.0 2024-04-21 21:39:24 +03:00
17 changed files with 294 additions and 46 deletions

View File

@ -1,3 +1,5 @@
# Advent of Code
https://adventofcode.com/2023
Uses [zig 0.12.0](https://ziglang.org/download/#release-0.12.0)

View File

@ -31,7 +31,7 @@ pub const Day = struct {
fn expectAnswer(comptime T: type, solver: Solver, expected: T, lines: []const []const u8) !void {
var input = Input{ .lines = lines, .allocator = std.testing.allocator };
var result: Result = try solver(&input);
var actual = result.value(T) orelse return error.ResultTypeMismatch;
const actual = result.value(T) orelse return error.ResultTypeMismatch;
try std.testing.expectEqual(expected, actual);
}

View File

@ -23,7 +23,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
}
}
var number = 10*first_digit + last_digit;
const number = 10*first_digit + last_digit;
sum += number;
}
@ -49,9 +49,9 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
}
for (0.., digits) |i, digit| {
var occurence_opt = std.mem.indexOf(u8, line, digit);
const occurence_opt = std.mem.indexOf(u8, line, digit);
if (occurence_opt == null) continue;
var occurence = occurence_opt.?;
const occurence = occurence_opt.?;
if (found_first and occurence > first_idx) continue;
@ -74,9 +74,9 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
}
for (0.., digits) |i, digit| {
var occurence_opt = std.mem.lastIndexOf(u8, line, digit);
const occurence_opt = std.mem.lastIndexOf(u8, line, digit);
if (occurence_opt == null) continue;
var occurence = occurence_opt.?;
const occurence = occurence_opt.?;
if (found_last and occurence < last_idx) continue;
@ -86,7 +86,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
}
}
var number = 10*first_digit + last_digit;
const number = 10*first_digit + last_digit;
sum += number;
}

View File

@ -147,15 +147,15 @@ fn parse_tile(char: u8) !Tile {
}
fn parse_input(allocator: Allocator, lines: []const []const u8) !Grid {
var height: u32 = @intCast(lines.len);
var width: u32 = @intCast(lines[0].len);
const height: u32 = @intCast(lines.len);
const width: u32 = @intCast(lines[0].len);
var tiles = try allocator.alloc(Tile, width * height);
errdefer allocator.free(tiles);
for (0.., lines) |y, line| {
for (0.., line) |x, char| {
var idx = y * width + x;
const idx = y * width + x;
tiles[idx] = try parse_tile(char);
}
}
@ -193,8 +193,8 @@ fn get_neighbours(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Point, 4)
const ox = offset[0];
const oy = offset[1];
var nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
var ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
if (grid.in_bounds(nx, ny)) {
neighbours.append(Point{ .x = @intCast(nx), .y = @intCast(ny) }) catch unreachable;
}
@ -253,8 +253,8 @@ fn get_connections(grid: *const Grid, x: u32, y: u32) std.BoundedArray(Direction
const ox = direction_tuple[1];
const oy = direction_tuple[2];
var nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
var ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
const nx: i32 = @intCast(@as(i32, @intCast(x)) + ox );
const ny: i32 = @intCast(@as(i32, @intCast(y)) + oy );
if (grid.in_bounds(nx, ny)) {
const tile = grid.get(@intCast(nx), @intCast(ny));
if (@as(TileType, tile) == TileType.Pipe and does_pipe_have_direction(tile.Pipe, opposite_direction)) {
@ -350,7 +350,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
twice_area += (x_0 * y_1) - (y_0 * x_1);
}
var result = std.math.absCast(twice_area) / 2 - (loop_path.items.len / 2 - 1);
const result = @abs(twice_area) / 2 - (loop_path.items.len / 2 - 1);
return .{ .uint = result };
}

View File

@ -46,8 +46,8 @@ const GalaxyMap = struct {
};
fn parse_input(allocator: Allocator, lines: []const []const u8) !GalaxyMap {
var height: u32 = @intCast(lines.len);
var width: u32 = @intCast(lines[0].len);
const height: u32 = @intCast(lines.len);
const width: u32 = @intCast(lines[0].len);
var tile_map = try allocator.alloc(Tile, width * height);
errdefer allocator.free(tile_map);
@ -163,7 +163,7 @@ fn sum_min_distances(points: []Point) u64 {
const p2 = points[j];
const dx = @as(i64, @intCast(p1.x)) - @as(i64, @intCast(p2.x));
const dy = @as(i64, @intCast(p1.y)) - @as(i64, @intCast(p2.y));
result += std.math.absCast(dx) + std.math.absCast(dy);
result += @abs(dx) + @abs(dy);
}
}
return result;

View File

@ -297,7 +297,7 @@ fn count_valid_arrangements_dp(cache: *Cache, states: []const SpringState, group
}
}
var cache_key = CacheKey{ .states = states, .groups = groups };
const cache_key = CacheKey{ .states = states, .groups = groups };
if (cache.get(cache_key)) |cached_result| {
return cached_result;
}

View File

@ -51,8 +51,8 @@ const Input = struct {
};
fn parse_map(allocator: Allocator, lines: []const []const u8) !Map {
var width: u32 = @intCast(lines[0].len);
var height: u32 = @intCast(lines.len);
const width: u32 = @intCast(lines[0].len);
const height: u32 = @intCast(lines.len);
var tiles = try std.ArrayList(bool).initCapacity(allocator, width*height);
errdefer tiles.deinit();

View File

@ -238,7 +238,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
const map = try parse_input(input.allocator, input.lines);
defer map.deinit();
var energy_map = try simulate_beam(allocator, map, PointU32.zero(), RIGHT);
const energy_map = try simulate_beam(allocator, map, PointU32.zero(), RIGHT);
defer allocator.free(energy_map);
// print_energy_map(map, energy_map);

226
src/day17.zig Normal file
View File

@ -0,0 +1,226 @@
const std = @import("std");
const aoc = @import("./aoc.zig");
const assert = std.debug.assert;
const Allocator = std.mem.Allocator;
const PointU8 = @import("./point.zig").Point(u8);
const PointI8 = @import("./point.zig").Point(i8);
const Direction = enum {
Up,
Down,
Left,
Right,
pub fn to_offset(self: Direction) PointI8 {
return switch (self) {
.Up => PointI8{ .x = 0, .y = -1 },
.Down => PointI8{ .x = 0, .y = 1 },
.Left => PointI8{ .x = -1, .y = 0 },
.Right => PointI8{ .x = 1, .y = 0 },
};
}
fn inv(self: Direction) Direction {
return switch (self) {
.Up => .Down,
.Down => .Up,
.Left => .Right,
.Right => .Left,
};
}
};
const directions: []const Direction = &[_]Direction{ .Up, .Down, .Left, .Right };
const HeatMap = struct {
tiles: std.ArrayList(u8),
width: u32,
height: u32,
pub fn init(allocator: Allocator, width: u32, height: u32) !HeatMap {
var tiles = try std.ArrayList(u8).initCapacity(allocator, width * height);
errdefer tiles.deinit();
try tiles.appendNTimes(0, width * height);
return HeatMap{
.tiles = tiles,
.width = width,
.height = height,
};
}
pub fn deinit(self: HeatMap) void {
self.tiles.deinit();
}
pub fn get_idx(self: HeatMap, x: u32, y: u32) usize {
return self.width * y + x;
}
pub fn in_bounds(self: HeatMap, x: i32, y: i32) bool {
return (0 <= x and x < self.width) and (0 <= y and y < self.height);
}
pub fn set(self: HeatMap, x: u32, y: u32, value: u8) void {
self.tiles.items[self.get_idx(x, y)] = value;
}
pub fn get(self: HeatMap, x: u32, y: u32) u8 {
return self.tiles.items[self.get_idx(x, y)];
}
pub fn print(self: HeatMap) void {
for (0..self.height) |y| {
for (0..self.width) |x| {
const tile_idx = self.get_idx(@intCast(x), @intCast(y));
const symbol = self.tiles.items[tile_idx] + '0';
std.debug.print("{c}", .{symbol});
}
std.debug.print("\n", .{});
}
}
};
fn parse_input(allocator: Allocator, lines: []const []const u8) !HeatMap {
const height = lines.len;
const width = lines[0].len;
var heat_map = try HeatMap.init(allocator, @intCast(width), @intCast(height));
for (0.., lines) |y, line| {
for (0.., line) |x, symbol| {
assert('0' <= symbol and symbol <= '9');
heat_map.set(@intCast(x), @intCast(y), symbol - '0');
}
}
return heat_map;
}
const QueueItem = struct {
pos: PointU8,
dir: Direction,
consecutive_count: u8,
heat_loss: u32,
};
const HeatPriorityQueue = std.PriorityQueue(QueueItem, HeatMap, compareQueueItem);
fn compareQueueItem(heat_map: HeatMap, a: QueueItem, b: QueueItem) std.math.Order {
const score_a = a.heat_loss + @abs(heat_map.width - a.pos.x) + @abs(heat_map.height - a.pos.y);
const score_b = b.heat_loss + @abs(heat_map.width - b.pos.x) + @abs(heat_map.height - b.pos.y);
return std.math.order(score_a, score_b);
}
inline fn push_to_queue(queue: *HeatPriorityQueue, heat_map: HeatMap, state: QueueItem, dir: Direction) !void {
const dir_offset = dir.to_offset();
const neighbour_x = @as(i32, @intCast(state.pos.x)) + dir_offset.x;
const neighbour_y = @as(i32, @intCast(state.pos.y)) + dir_offset.y;
if (!heat_map.in_bounds(neighbour_x, neighbour_y)) return; // Skip out of bounds
var consecutive_count = state.consecutive_count;
if (state.dir == dir) {
consecutive_count += 1;
} else {
consecutive_count = 1;
}
const tile_heat_loss = heat_map.get(@intCast(neighbour_x), @intCast(neighbour_y));
const next_state = QueueItem{
.consecutive_count = consecutive_count,
.heat_loss = state.heat_loss + tile_heat_loss,
.dir = dir,
.pos = PointU8{ .x = @intCast(neighbour_x), .y = @intCast(neighbour_y) }
};
try queue.add(next_state);
}
fn solve_min_heat_loss(allocator: Allocator, heat_map: HeatMap, min_steps: u32, max_steps: u32) !?u32 {
var queue = HeatPriorityQueue.init(allocator, heat_map);
defer queue.deinit();
var seen = std.AutoHashMap(QueueItem, void).init(allocator);
defer seen.deinit();
try queue.add(.{
.pos = .{ .x = 0, .y = 0 },
.dir = .Right,
.heat_loss = 0,
.consecutive_count = 1
});
try queue.add(.{
.pos = .{ .x = 0, .y = 0 },
.dir = .Down,
.heat_loss = 0,
.consecutive_count = 1
});
while (queue.removeOrNull()) |item| {
const state: QueueItem = item;
if (seen.contains(state)) continue;
try seen.put(state, {});
if (state.pos.x == heat_map.width-1 and state.pos.y == heat_map.height-1 and state.consecutive_count >= min_steps) {
return state.heat_loss;
}
if (state.consecutive_count < min_steps) {
try push_to_queue(&queue, heat_map, state, state.dir);
} else {
for (directions) |dir| {
if (state.dir == dir and state.consecutive_count == max_steps) continue;
// Skip direction, where it would need to turn around
if (state.dir == dir.inv()) continue;
try push_to_queue(&queue, heat_map, state, dir);
}
}
}
return null;
}
pub fn part1(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
const heat_map = try parse_input(allocator, input.lines);
defer heat_map.deinit();
const answer = try solve_min_heat_loss(allocator, heat_map, 0, 3);
return .{ .uint = answer.? };
}
pub fn part2(input: *aoc.Input) !aoc.Result {
const allocator = input.allocator;
const heat_map = try parse_input(allocator, input.lines);
defer heat_map.deinit();
const answer = try solve_min_heat_loss(allocator, heat_map, 4, 10);
return .{ .uint = answer.? };
}
const example_input = [_][]const u8{
"2413432311323",
"3215453535623",
"3255245654254",
"3446585845452",
"4546657867536",
"1438598798454",
"4457876987766",
"3637877979653",
"4654967986887",
"4564679986453",
"1224686865563",
"2546548887735",
"4322674655533",
};
test "part 1 example" {
try aoc.expectAnswerUInt(part1, 102, &example_input);
}
test "part 2 example" {
try aoc.expectAnswerUInt(part2, 94, &example_input);
}

View File

@ -33,10 +33,10 @@ fn parse_game(game: *Game, line: []const u8) void {
var cubes = std.mem.splitSequence(u8, game_set_str, ", ");
while (cubes.next()) |cube_count| {
var space = std.mem.indexOf(u8, cube_count, " ") orelse @panic("Invalid format, expected space");
const space = std.mem.indexOf(u8, cube_count, " ") orelse @panic("Invalid format, expected space");
var cube = parse_cube(cube_count[(space+1)..]) orelse @panic("Invalid cube name");
var count = std.fmt.parseInt(u32, cube_count[0..space], 10) catch @panic("Invalid format");
const cube = parse_cube(cube_count[(space+1)..]) orelse @panic("Invalid cube name");
const count = std.fmt.parseInt(u32, cube_count[0..space], 10) catch @panic("Invalid format");
cube_set.set(cube, count);
}
}
@ -54,7 +54,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
const lines = input.lines;
const allocator = input.allocator;
var games = try parse_games(allocator, lines);
const games = try parse_games(allocator, lines);
defer allocator.free(games);
const max_red = 12;
@ -81,7 +81,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
pub fn part2(input: *aoc.Input) !aoc.Result {
const lines = input.lines;
const allocator = input.allocator;
var games = try parse_games(allocator, lines);
const games = try parse_games(allocator, lines);
defer allocator.free(games);
var sum: u32 = 0;

View File

@ -115,7 +115,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
var lowest_location: ?u64 = null;
for (parsed.seeds.slice()) |seed| {
var value = apply_seed_to_location(seed, &parsed);
const value = apply_seed_to_location(seed, &parsed);
if (lowest_location) |location| {
lowest_location = @min(location, value);

View File

@ -91,7 +91,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
}
}
var answer = max_time - min_time + 1;
const answer = max_time - min_time + 1;
return .{ .uint = @intCast(answer) };
}

View File

@ -100,7 +100,7 @@ fn count_cards(hand: Hand) CardCounts {
}
fn determine_card_strength(hand: Hand) HandStrength {
var counts = count_cards(hand);
const counts = count_cards(hand);
var ones_count: u4 = 0;
var two_count: u4 = 0;

View File

@ -248,7 +248,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
for (parsed.nodes.items) |node_junction| {
if (node_junction.from[2] == comptime parse_char_to_node_part('A')) {
var steps = find_steps_needed(&node_map, parsed.instructions.items, node_junction.from);
const steps = find_steps_needed(&node_map, parsed.instructions.items, node_junction.from);
try all_steps.append(steps);
}
}

View File

@ -90,7 +90,7 @@ pub fn part1(input: *aoc.Input) !aoc.Result {
for (parsed.histories.items) |history| {
const numbers = history.items;
var changes = try all_changes_of_history(allocator, numbers);
const changes = try all_changes_of_history(allocator, numbers);
defer free_history_changes(changes);
var prediction: i32 = numbers[numbers.len-1];
@ -113,7 +113,7 @@ pub fn part2(input: *aoc.Input) !aoc.Result {
for (parsed.histories.items) |history| {
const numbers = history.items;
var changes = try all_changes_of_history(allocator, numbers);
const changes = try all_changes_of_history(allocator, numbers);
defer free_history_changes(changes);
var prediction: i32 = 0;

View File

@ -37,6 +37,7 @@ const Days = [_]aoc.Day{
create_day(@import("./day14.zig")),
create_day(@import("./day15.zig")),
create_day(@import("./day16.zig")),
create_day(@import("./day17.zig")),
};
fn kilobytes(count: u32) u32 {
@ -49,7 +50,7 @@ fn run(allocator: Allocator, args: *cli) !u8 {
return 255;
}
var day = Days[args.day-1];
const day = Days[args.day-1];
if ((args.part == 1 and day.part1 == null) or (args.part == 2 and day.part2 == null)) {
std.debug.print("ERROR: Part {} for day {} does not exist\n", .{args.part, args.day});
return 255;
@ -81,7 +82,7 @@ fn run(allocator: Allocator, args: *cli) !u8 {
var input = aoc.Input{ .allocator = allocator, .lines = lines.items };
var start_time = std.time.microTimestamp();
const start_time = std.time.microTimestamp();
var result: aoc.Result = undefined;
if (args.part == 1) {
result = try day.part1.?(&input);
@ -107,32 +108,33 @@ 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");
const 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});
const 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});
const 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, .{});
var server_header_buffer: [1024]u8 = undefined;
var request = try client.open(.GET, uri, .{
.server_header_buffer = &server_header_buffer,
.extra_headers = &.{
.{ .name = "cookie", .value = cookie_header },
.{ .name = "accept", .value = "*/*" },
}
});
defer request.deinit();
try request.start();
try request.send();
try request.wait();
if (request.response.status != .ok) {
@ -195,4 +197,5 @@ test {
_ = @import("./day14.zig");
_ = @import("./day15.zig");
_ = @import("./day16.zig");
_ = @import("./day17.zig");
}

View File

@ -9,6 +9,13 @@ pub fn Point(comptime T: type) type {
return self.x == other.x and self.y == other.y;
}
pub fn neg(self: Self) Self {
return Self{
.x = -self.x,
.y = -self.y,
};
}
pub fn zero() Self {
return Self{ .x = 0, .y = 0 };
}
@ -20,6 +27,13 @@ pub fn Point(comptime T: type) type {
};
}
pub fn sub(self: Self, other: Self) Self {
return Self{
.x = self.x - other.x,
.y = self.y - other.y,
};
}
pub fn to_i32(self: Self) Point(i32) {
return Point(i32){
.x = @intCast(self.x),
@ -35,3 +49,6 @@ pub fn Point(comptime T: type) type {
}
};
}
pub const PointI32 = Point(i32);
pub const PointU32 = Point(u32);