const std = @import("std"); pub fn main() anyerror!void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); var gpa = &arena.allocator; var f = try std.fs.cwd().openFile("input", .{}); var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u32)); defer gpa.free(contents); var ship = ShipPosition{}; var it = std.mem.tokenize(contents, "\n"); while (it.next()) |line| { ship.update(line[0], try std.fmt.parseInt(i64, line[1..], 10)); std.log.debug("After '{}', ship at ({}, {}) facing {}", .{line, ship.position[0], ship.position[1], ship.facing}); } std.log.info("Ship is at ({},{}) facing {}. It has travalled {} manhattan distance from start", .{ship.position[0], ship.position[1], ship.facing, ship.manhattan_distance_from([_]i64{0, 0})}); // Part 2 ship = ShipPosition{}; it = std.mem.tokenize(contents, "\n"); while (it.next()) |line| { ship.update_part2(line[0], try std.fmt.parseInt(i64, line[1..], 10)); std.log.debug("After '{}', ship at ({}, {}), with waypoint ({}, {})", .{line, ship.position[0], ship.position[1], ship.waypoint[0], ship.waypoint[1]}); } std.log.info("Ship is at ({},{}) facing {} and waypoint ({}, {}). It has travalled {} manhattan distance from start", .{ship.position[0], ship.position[1], ship.facing, ship.waypoint[0], ship.waypoint[1], ship.manhattan_distance_from([_]i64{0, 0})}); } const ShipPosition = struct { facing : i64 = 90, position : [2]i64 = [_]i64{0, 0}, waypoint: [2]i64 = [_]i64{10, 1}, pub fn update(self: *ShipPosition, command: u8, value: i64) void { switch(command) { 'N' => { self.position[1] += value; }, 'S' => { self.position[1] -= value; }, 'E' => { self.position[0] += value; }, 'W' => { self.position[0] -= value; }, 'R' => { self.facing += value; }, 'L' => { self.facing -= value; }, 'F' => { std.log.debug("Facing before moving forward: {}", .{self.facing}); while (self.facing < 0) { self.facing += 360; } if (self.facing >= 360) { self.facing = @intCast(i64, @mod(self.facing, 360)); } std.log.debug("Facing after normalizing, before moving forward: {}", .{self.facing}); var c : u8 = switch(self.facing) { 0 => 'N', 90 => 'E', 180 => 'S', 270 => 'W', else => { std.log.warn("Unexpected facing {}", .{self.facing}); unreachable; }, }; self.update(c, value); }, else => { std.log.warn("Unknown command: '{}'", .{command}); }, } } pub fn manhattan_distance_from(self: *ShipPosition, from: [2]i64) u64 { return (std.math.absCast(self.position[0] - from[0])) + (std.math.absCast(self.position[1] - from [1])); } pub fn update_part2(self: *ShipPosition, command: u8, value: i64) void { switch(command) { 'N' => { self.waypoint[1] += value; }, 'S' => { self.waypoint[1] -= value; }, 'E' => { self.waypoint[0] += value; }, 'W' => { self.waypoint[0] -= value; }, 'R' => { // Rotate around the ship self.waypoint = rotate_point(self.waypoint, -value); }, 'L' => { // Rotate around the ship self.waypoint = rotate_point(self.waypoint, value); }, 'F' => { // For forward value times towards the waypoint. self.position[0] += self.waypoint[0] * value; self.position[1] += self.waypoint[1] * value; // Waypoint stays relative to ship //self.waypoint[0] = self.position[0] + self.waypoint[0]; //self.waypoint[1] = self.position[1] + self.waypoint[1]; }, else => { std.log.warn("Unknown command: '{}'", .{command}); }, } } }; pub fn rotate_point(point: [2]i64, degrees: i64) [2]i64 { var radians : f64 = (@intToFloat(f64, degrees) * std.math.pi) / 180.0; var old_x = @intToFloat(f64, point[0]); var old_y = @intToFloat(f64, point[1]); var x : f64 = old_x * std.math.cos(radians) - old_y * std.math.sin(radians); var y : f64 = old_y * std.math.cos(radians) + old_x * std.math.sin(radians); return [2]i64 { @floatToInt(i64, std.math.round(x)), @floatToInt(i64, std.math.round(y)), }; } test "part_one" { var data = [_][]const u8 { "F10", "N3", "F7", "R90", "F11", }; var ship = ShipPosition{}; for (data) |d| { ship.update(d[0], try std.fmt.parseInt(i64, d[1..], 10)); } std.testing.expectEqual(ship.position[0], 17); std.testing.expectEqual(ship.position[1], -8); std.testing.expectEqual(ship.manhattan_distance_from([_]i64{0, 0}), 25); } test "part_two" { var data = [_][]const u8 { "F10", "N3", "F7", "R90", "F11", }; var ship = ShipPosition{}; for (data) |d| { ship.update_part2(d[0], try std.fmt.parseInt(i64, d[1..], 10)); std.log.warn("After '{}', ship at ({}, {}), with waypoint ({}, {})", .{d, ship.position[0], ship.position[1], ship.waypoint[0], ship.waypoint[1]}); } std.testing.expectEqual(ship.position[0], 214); std.testing.expectEqual(ship.position[1], -72); std.testing.expectEqual(ship.manhattan_distance_from([_]i64{0, 0}), 286); } // test "rotate" { // var point = [_]i64 {4,-3}; // var p = rotate_point(point, 90); // std.testing.expectEqual(p[0], 3); // std.testing.expectEqual(p[1], 4); // p = rotate_point(point, 180); // std.testing.expectEqual(p[0], -4); // std.testing.expectEqual(p[1], 3); // p = rotate_point(point, 270); // std.testing.expectEqual(p[0], -4); // std.testing.expectEqual(p[1], 3); // }