diff options
| author | Orfeas <38209077+0xfea5@users.noreply.github.com> | 2023-12-11 22:16:01 +0200 |
|---|---|---|
| committer | Orfeas <38209077+0xfea5@users.noreply.github.com> | 2025-10-28 23:20:45 +0200 |
| commit | cd5600dfec9d59a8054cab53ff31c25be789ce9c (patch) | |
| tree | 4d03e479792023e67a06cc6a7580e15a2eb744b2 /day10/solution.zig | |
| parent | day9 (diff) | |
| download | aoc23-cd5600dfec9d59a8054cab53ff31c25be789ce9c.tar.gz aoc23-cd5600dfec9d59a8054cab53ff31c25be789ce9c.zip | |
day10
Diffstat (limited to 'day10/solution.zig')
| -rw-r--r-- | day10/solution.zig | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/day10/solution.zig b/day10/solution.zig new file mode 100644 index 0000000..8028c94 --- /dev/null +++ b/day10/solution.zig | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | const std = @import("std"); | ||
| 2 | const print = std.debug.print; | ||
| 3 | const assert = std.debug.assert; | ||
| 4 | const ArrayList = std.ArrayList; | ||
| 5 | const HashMap = std.HashMap; | ||
| 6 | const mem = std.mem; | ||
| 7 | |||
| 8 | const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace); | ||
| 9 | var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| 10 | const allocator = gpa.allocator(); | ||
| 11 | |||
| 12 | const Pair = struct { | ||
| 13 | row: isize, | ||
| 14 | col: isize, | ||
| 15 | |||
| 16 | pub fn eql(lhs: @This(), rhs: @This()) bool { | ||
| 17 | return lhs.col == rhs.col and lhs.row == rhs.row; | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | const Direction = enum(u4) { | ||
| 22 | N = 0, | ||
| 23 | U = 1 << 0, | ||
| 24 | R = 1 << 1, | ||
| 25 | D = 1 << 2, | ||
| 26 | L = 1 << 3, | ||
| 27 | }; | ||
| 28 | |||
| 29 | const c2dir = blk: { | ||
| 30 | var res: [256]u4 = undefined; | ||
| 31 | res['S'] = | ||
| 32 | @intFromEnum(Direction.U) | @intFromEnum(Direction.R) | | ||
| 33 | @intFromEnum(Direction.D) | @intFromEnum(Direction.L); | ||
| 34 | res['|'] = @intFromEnum(Direction.U) | @intFromEnum(Direction.D); | ||
| 35 | res['J'] = @intFromEnum(Direction.U) | @intFromEnum(Direction.L); | ||
| 36 | res['L'] = @intFromEnum(Direction.U) | @intFromEnum(Direction.R); | ||
| 37 | res['7'] = @intFromEnum(Direction.D) | @intFromEnum(Direction.L); | ||
| 38 | res['F'] = @intFromEnum(Direction.D) | @intFromEnum(Direction.R); | ||
| 39 | res['-'] = @intFromEnum(Direction.L) | @intFromEnum(Direction.R); | ||
| 40 | res['.'] = @intFromEnum(Direction.N); | ||
| 41 | break :blk res; | ||
| 42 | }; | ||
| 43 | |||
| 44 | const dir2off = blk: { | ||
| 45 | var res: [256][2]i64 = undefined; | ||
| 46 | res[@intFromEnum(Direction.U)] = .{ -1, 0 }; | ||
| 47 | res[@intFromEnum(Direction.R)] = .{ 0, 1 }; | ||
| 48 | res[@intFromEnum(Direction.D)] = .{ 1, 0 }; | ||
| 49 | res[@intFromEnum(Direction.L)] = .{ 0, -1 }; | ||
| 50 | break :blk res; | ||
| 51 | }; | ||
| 52 | |||
| 53 | pub fn solve(grid: []const []const u8) !void { | ||
| 54 | const height = grid.len; | ||
| 55 | const width = grid[0].len; | ||
| 56 | const start = find_start: { | ||
| 57 | for (grid, 0..) |row, i| { | ||
| 58 | if (mem.indexOfScalar(u8, row, 'S')) |j| { | ||
| 59 | break :find_start Pair{ | ||
| 60 | .row = @bitCast(i), | ||
| 61 | .col = @bitCast(j), | ||
| 62 | }; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | unreachable; | ||
| 66 | }; | ||
| 67 | |||
| 68 | var pathNodes = ArrayList(Pair).init(allocator); | ||
| 69 | defer pathNodes.deinit(); | ||
| 70 | try pathNodes.append(start); | ||
| 71 | var blocked: u4 = 0b0000; | ||
| 72 | |||
| 73 | outer: while (true) { | ||
| 74 | const curr = pathNodes.getLast(); | ||
| 75 | const cdirs = c2dir[grid[@bitCast(curr.row)][@bitCast(curr.col)]]; | ||
| 76 | |||
| 77 | for (0..4) |i| { | ||
| 78 | const d: u4 = @shlExact(@as(u4, 1), @intCast(i)); | ||
| 79 | const nrow: i64 = curr.row + dir2off[d][0]; | ||
| 80 | const ncol: i64 = curr.col + dir2off[d][1]; | ||
| 81 | |||
| 82 | if (nrow < 0 or nrow >= height or ncol < 0 or ncol >= width) { | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | |||
| 86 | const next = Pair{ | ||
| 87 | .row = nrow, | ||
| 88 | .col = ncol, | ||
| 89 | }; | ||
| 90 | const ndirs = std.math.rotl(u4, c2dir[grid[@bitCast(next.row)][@bitCast(next.col)]], @as(u2, 2)); | ||
| 91 | |||
| 92 | if (d & cdirs & ndirs & ~blocked != 0) { | ||
| 93 | // Wrapped back to the beginning | ||
| 94 | if (next.eql(start)) { | ||
| 95 | break :outer; | ||
| 96 | } | ||
| 97 | blocked = std.math.rotl(u4, d, @as(u2, 2)); | ||
| 98 | try pathNodes.append(next); | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | } else { | ||
| 102 | unreachable; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | const pathLen = pathNodes.items.len; | ||
| 107 | const part1 = pathLen / 2; | ||
| 108 | print("{d}\n", .{part1}); | ||
| 109 | |||
| 110 | // Use shoelace formula to calculate the area of the polygon | ||
| 111 | // https://en.wikipedia.org/wiki/Shoelace_formula#Shoelace_formula | ||
| 112 | var a2: isize = 0; | ||
| 113 | for (pathNodes.items, 1..) |f, j| { | ||
| 114 | const s = pathNodes.items[j % pathLen]; | ||
| 115 | a2 += f.row * s.col - f.col * s.row; | ||
| 116 | } | ||
| 117 | const a: isize = @divTrunc(if (a2 > 0) a2 else -a2, 2); | ||
| 118 | // Use Pick's formula to find the number of internal points | ||
| 119 | // https://en.wikipedia.org/wiki/Pick's_theorem#Formula | ||
| 120 | const part2 = a - @as(isize, @bitCast(part1)) + 1; | ||
| 121 | |||
| 122 | print("{d}\n", .{part2}); | ||
| 123 | } | ||
| 124 | |||
| 125 | pub fn main() !void { | ||
| 126 | var splitLines = mem.splitScalar(u8, fin, '\n'); | ||
| 127 | var grid = ArrayList([]const u8).init(allocator); | ||
| 128 | |||
| 129 | while (splitLines.next()) |line| { | ||
| 130 | try grid.append(line); | ||
| 131 | } | ||
| 132 | try solve(grid.items); | ||
| 133 | } | ||
