216 lines
8.8 KiB
Zig
216 lines
8.8 KiB
Zig
const std = @import("std");
|
|
const print = std.debug.print;
|
|
const util = @import("util.zig");
|
|
const expect = std.testing.expect;
|
|
|
|
pub fn main() !void {
|
|
// const response = try part_one(false);
|
|
// print("{}\n", .{response});
|
|
try part_two();
|
|
}
|
|
|
|
const Point = struct { x: u32, y: u32 };
|
|
|
|
const Velocity = struct { x: i32, y: i32 };
|
|
|
|
const Robot = struct {
|
|
position: Point,
|
|
velocity: Velocity,
|
|
|
|
pub fn move(self: *Robot, width: usize, height: usize) void {
|
|
// I wish it were possible to put this on multiple lines, but it appears to be a syntax error
|
|
const new_x: u32 = @intCast(if (self.velocity.x >= 0) (self.position.x + util.magnitude(self.velocity.x)) % width else (self.position.x + (width - util.magnitude(self.velocity.x))) % width);
|
|
|
|
const new_y: u32 = @intCast(if (self.velocity.y >= 0) (self.position.y + util.magnitude(self.velocity.y)) % height else (self.position.y + (height - util.magnitude(self.velocity.y))) % height);
|
|
|
|
self.position = Point{ .x = new_x, .y = new_y };
|
|
}
|
|
|
|
pub fn move_iterated(self: *Robot, width: usize, height: usize, move_count: u32) void {
|
|
for (0..move_count) |_| {
|
|
// Technically inefficient in that it does the modulo operation on every movement, but the alternative would
|
|
// be having to deal with Zig's bullshit integer limits, so... :shrug:
|
|
self.move(width, height);
|
|
}
|
|
}
|
|
|
|
pub fn from_line(line: []const u8) !Robot {
|
|
// print("DEBUG - parsing line {s}\n", .{line});
|
|
const start_pos_x: usize = 2;
|
|
const end_pos_x: usize = std.mem.indexOf(u8, line, ",").?;
|
|
// print("DEBUG - end_pos_x is {}\n", .{end_pos_x});
|
|
const start_pos_y: usize = end_pos_x + 1;
|
|
const end_pos_y: usize = std.mem.indexOf(u8, line, " ").?;
|
|
// print("DEBUG - end_pos_y is {}\n", .{end_pos_y});
|
|
|
|
const start_vel_x: usize = std.mem.indexOf(u8, line, "v").? + 2;
|
|
// print("DEBUG - start_vel_x is {}\n", .{start_vel_x});
|
|
const end_vel_x: usize = std.mem.indexOf(u8, line[end_pos_y..], ",").? + end_pos_y;
|
|
// print("DEBUG - end_vel_x is {}\n", .{end_vel_x});
|
|
const start_vel_y: usize = end_vel_x + 1;
|
|
const end_vel_y: usize = line.len;
|
|
|
|
const x = try std.fmt.parseInt(u32, line[start_pos_x..end_pos_x], 10);
|
|
const y = try std.fmt.parseInt(u32, line[start_pos_y..end_pos_y], 10);
|
|
const vel_x = try std.fmt.parseInt(i32, line[start_vel_x..end_vel_x], 10);
|
|
const vel_y = try std.fmt.parseInt(i32, line[start_vel_y..end_vel_y], 10);
|
|
|
|
print("DEBUG - x, y, vel_x, vel_y are {}, {}, {}, {}\n", .{ x, y, vel_x, vel_y });
|
|
|
|
return Robot{ .position = Point{ .x = x, .y = y }, .velocity = Velocity{ .x = vel_x, .y = vel_y } };
|
|
}
|
|
};
|
|
|
|
fn part_one(is_test_case: bool) anyerror!u32 {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const input_file = try util.getInputFile("14", is_test_case);
|
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
|
defer allocator.free(data);
|
|
|
|
var it = std.mem.splitScalar(u8, data, '\n');
|
|
var robots = std.ArrayList(Robot).init(allocator);
|
|
defer robots.deinit();
|
|
|
|
const height: u32 = if (is_test_case) 7 else 103;
|
|
const width: u32 = if (is_test_case) 11 else 101;
|
|
|
|
while (it.next()) |line| {
|
|
try robots.append(try Robot.from_line(line));
|
|
}
|
|
|
|
var first_quadrant_count: u32 = 0;
|
|
var second_quadrant_count: u32 = 0;
|
|
var third_quadrant_count: u32 = 0;
|
|
var fourth_quadrant_count: u32 = 0;
|
|
// I don't know why this has to be `*robot` - see question [here](https://ziggit.dev/t/how-to-get-a-non-const-pointer-to-a-structs-field-from-within-a-function-of-the-struct/7639/13?u=scubbo)
|
|
for (robots.items) |*robot| {
|
|
robot.move_iterated(width, height, 100);
|
|
print("DEBUG - robot in position {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
// Can't use a `switch`, here, as `width/2` and `height/2` are not comptime expressions
|
|
if (robot.position.x < width / 2 and robot.position.y < height / 2) {
|
|
first_quadrant_count += 1;
|
|
}
|
|
if (robot.position.x > width / 2 and robot.position.y < height / 2) {
|
|
second_quadrant_count += 1;
|
|
}
|
|
if (robot.position.x < width / 2 and robot.position.y > height / 2) {
|
|
third_quadrant_count += 1;
|
|
}
|
|
if (robot.position.x > width / 2 and robot.position.y > height / 2) {
|
|
fourth_quadrant_count += 1;
|
|
}
|
|
}
|
|
print("DEBUG - quadrant counts are {}, {}, {}, {}\n", .{ first_quadrant_count, second_quadrant_count, third_quadrant_count, fourth_quadrant_count });
|
|
return first_quadrant_count * second_quadrant_count * third_quadrant_count * fourth_quadrant_count;
|
|
}
|
|
|
|
// ...wtf!? How on earth are you meant to do this without visual inspection?
|
|
// ...ok, giving up on this. I stepped through the output (run `zig run solutions 14.zig > /tmp/output 2>&1`, then open in vim and do:
|
|
// `qt/Iteration<enter>ztqq` (to record a macro on key `t` which will bring the next iteration to the top of the screen), then repeatedly do
|
|
// `@t`) but can't see anything resembling a christmas tree.
|
|
// Iterations 12 and 35 both show some structure, but nothing like a tree (and those are both rejected as answers)
|
|
fn part_two() anyerror!void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
const input_file = try util.getInputFile("14", false);
|
|
const data = try util.readAllInputWithAllocator(input_file, allocator);
|
|
defer allocator.free(data);
|
|
|
|
var it = std.mem.splitScalar(u8, data, '\n');
|
|
var robots = std.ArrayList(Robot).init(allocator);
|
|
defer robots.deinit();
|
|
|
|
const height: u32 = if (false) 7 else 103;
|
|
const width: u32 = if (false) 11 else 101;
|
|
|
|
while (it.next()) |line| {
|
|
try robots.append(try Robot.from_line(line));
|
|
}
|
|
for (0..100) |iteration| {
|
|
for (robots.items) |*robot| {
|
|
robot.move(width, height);
|
|
}
|
|
|
|
var lines = std.ArrayList([]u32).init(allocator);
|
|
for (0..height) |_| {
|
|
var line = std.ArrayList(u32).init(allocator);
|
|
for (0..width) |_| {
|
|
try line.append(0);
|
|
}
|
|
try lines.append(try line.toOwnedSlice());
|
|
}
|
|
const lines_owned = try lines.toOwnedSlice();
|
|
|
|
for (robots.items) |*robot| {
|
|
lines_owned[robot.position.y][robot.position.x] += 1;
|
|
}
|
|
|
|
// Print out display
|
|
print("Iteration count: {}\n", .{iteration + 1});
|
|
for (lines_owned) |line| {
|
|
for (line) |val| {
|
|
if (val > 0) {
|
|
print("X", .{});
|
|
} else {
|
|
print(" ", .{});
|
|
}
|
|
}
|
|
print("|\n", .{}); // I'm cheating by putting this trailing pipe to stop Vim from giving ugly red highlights on trailing whitespace
|
|
}
|
|
print("\n\n", .{});
|
|
|
|
for (lines_owned) |line| {
|
|
allocator.free(line);
|
|
}
|
|
allocator.free(lines_owned);
|
|
std.time.sleep(1000000);
|
|
}
|
|
}
|
|
|
|
test "robot movement" {
|
|
var robot = Robot{ .position = Point{ .x = 2, .y = 4 }, .velocity = Velocity{ .x = 2, .y = -3 } };
|
|
robot.move(11, 7);
|
|
print("DEBUG - robot position after one iteration is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 4);
|
|
try expect(robot.position.y == 1);
|
|
|
|
robot.move(11, 7);
|
|
print("DEBUG - robot position after two iterations is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 6);
|
|
try expect(robot.position.y == 5);
|
|
|
|
robot.move(11, 7);
|
|
print("DEBUG - robot position after three iterations is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 8);
|
|
try expect(robot.position.y == 2);
|
|
|
|
robot.move(11, 7);
|
|
print("DEBUG - robot position after four iterations is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 10);
|
|
try expect(robot.position.y == 6);
|
|
|
|
robot.move(11, 7);
|
|
print("DEBUG - robot position after five iterations is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 1);
|
|
try expect(robot.position.y == 3);
|
|
}
|
|
|
|
test "iterated movement" {
|
|
var robot = Robot{ .position = Point{ .x = 2, .y = 4 }, .velocity = Velocity{ .x = 2, .y = -3 } };
|
|
robot.move_iterated(11, 7, 3);
|
|
print("DEBUG - robot position after three iterations (in one go) is {}/{}\n", .{ robot.position.x, robot.position.y });
|
|
try expect(robot.position.x == 8);
|
|
try expect(robot.position.y == 2);
|
|
}
|
|
|
|
test "part_one" {
|
|
const part_one_response = try part_one(true);
|
|
print("DEBUG - part_one_response is {}\n", .{part_one_response});
|
|
try expect(part_one_response == 12);
|
|
}
|