const std = @import("std");
const PATH = "input/day12.txt";
const CardinalType = u2;
const Cardinal = enum(CardinalType) {
north,
east,
south,
west,
};
const Ship = struct {
dir: Cardinal = .east,
x: isize = 0,
y: isize = 0,
fn manhattan(self: @This()) usize {
const absx = std.math.absInt(self.x) catch unreachable;
const absy = std.math.absInt(self.y) catch unreachable;
return @intCast(usize, absx + absy);
}
};
pub fn first(allocator: ?std.mem.Allocator) anyerror!usize {
_ = allocator;
const file = @embedFile(PATH);
var lines = std.mem.tokenize(u8, file, "\n");
var ship = Ship{};
while (lines.next()) |line| {
var op = line[0];
if (op == 'F') {
switch (ship.dir) {
.north => op = 'N',
.east => op = 'E',
.west => op = 'W',
.south => op = 'S',
}
}
switch (op) {
'N' => ship.y -= try std.fmt.parseUnsigned(isize, line[1..], 0),
'S' => ship.y += try std.fmt.parseUnsigned(isize, line[1..], 0),
'W' => ship.x -= try std.fmt.parseUnsigned(isize, line[1..], 0),
'E' => ship.x += try std.fmt.parseUnsigned(isize, line[1..], 0),
'R' => {
const deg = try std.fmt.parseUnsigned(u9, line[1..], 0);
const pos: i4 = @enumToInt(ship.dir);
ship.dir = @intToEnum(Cardinal, @mod(pos + @intCast(i4, deg / 90), std.math.maxInt(CardinalType) + 1));
},
'L' => {
const deg = try std.fmt.parseUnsigned(u9, line[1..], 0);
const pos: i4 = @enumToInt(ship.dir);
ship.dir = @intToEnum(Cardinal, @mod(pos - @intCast(i4, deg / 90), std.math.maxInt(CardinalType) + 1));
},
else => unreachable,
}
}
return ship.manhattan();
}
const Waypoint = struct {
x: isize = 10,
y: isize = -1,
};
pub fn second(allocator: ?std.mem.Allocator) anyerror!usize {
_ = allocator;
var file = @embedFile(PATH);
var lines = std.mem.tokenize(u8, file, "\n");
var ship = Ship{};
var wp = Waypoint{};
while (lines.next()) |line| {
switch (line[0]) {
'N' => wp.y -= try std.fmt.parseUnsigned(isize, line[1..], 0),
'S' => wp.y += try std.fmt.parseUnsigned(isize, line[1..], 0),
'W' => wp.x -= try std.fmt.parseUnsigned(isize, line[1..], 0),
'E' => wp.x += try std.fmt.parseUnsigned(isize, line[1..], 0),
'R', 'L' => {
var deg = try std.fmt.parseUnsigned(u9, line[1..], 0);
// R90 == L270 and R270 == L90
if (deg == 90 and line[0] == 'L')
deg = 270
else if (deg == 270 and line[0] == 'L')
deg = 90;
// Rotate right with `deg` degrees
switch (deg) {
0 => {},
90 => {
const tmp = wp.x;
wp.x = -wp.y;
wp.y = tmp;
},
180 => {
wp.x *= -1;
wp.y *= -1;
},
270 => {
const tmp = wp.x;
wp.x = wp.y;
wp.y = -tmp;
},
else => unreachable,
}
},
'F' => {
const times = try std.fmt.parseUnsigned(isize, line[1..], 0);
ship.x += wp.x * times;
ship.y += wp.y * times;
},
else => unreachable,
}
}
return ship.manhattan();
}
test "day12a" {
try std.testing.expectEqual(@as(usize, 1589), try first(std.testing.allocator));
}
test "day12b" {
try std.testing.expectEqual(@as(usize, 23960), try second(std.testing.allocator));
}