const std = @import("std"); const INPUT = "394618527"; const Str = []const u8; pub fn first(allocator: ?std.mem.Allocator) anyerror!usize { var cc = try CrabCups(u4, 9).init(allocator.?); defer { cc.cups.deinit(); } try cc.parse(INPUT); // std.debug.print("{any}\n", .{cc.cups.items}); var round: usize = 0; while (round < 100) : (round += 1) { // std.debug.print("{d} {any}\n", .{ cc.current, cc.cups.items }); try cc.move(); } var ret: usize = 0; var idx: usize = 0; var next: usize = 0; while (idx < 8) : (idx += 1) { ret += (cc.cups.items[next] + 1) * try std.math.powi(usize, 10, cc.cups.items.len - idx - 2); next = cc.cups.items[next]; } return ret; } pub fn second(allocator: ?std.mem.Allocator) anyerror!usize { const ARRAY_SIZE = 1_000_000; const input = INPUT; var cc = try CrabCups(u20, ARRAY_SIZE).init(allocator.?); defer { cc.cups.deinit(); } try cc.parse(input); // fill up the array var item: u20 = input.len; while (item < ARRAY_SIZE) : (item += 1) { cc.cups.items[item] = item + 1; } // fill up fixes... const tmp = (try std.fmt.parseUnsigned(usize, input[input.len - 1 ..], 10)) - 1; // last item should point to first cc.cups.items[cc.cups.items.len - 1] = cc.current; // parsed last item should point to first item in fill up cc.cups.items[tmp] = input.len; var round: usize = 0; while (round < 10_000_000) : (round += 1) { try cc.move(); } var ret: usize = 0; const cup1 = cc.cups.items[0]; const cup2 = cc.cups.items[cup1]; ret = cup1 + 1; ret *= cup2 + 1; return ret; } fn CrabCups(comptime CupType: anytype, comptime SIZE: usize) type { const Cups = std.ArrayList(CupType); return struct { cups: Cups = undefined, current: CupType = undefined, allocator: std.mem.Allocator, fn init(allocator: std.mem.Allocator) !@This() { return @This(){ .cups = try std.ArrayList(CupType).initCapacity(allocator, SIZE), .allocator = allocator, }; } // cup_label points to next cup fn parse(self: *@This(), input: Str) !void { var array = try self.cups.addManyAsArray(SIZE); var prev: CupType = undefined; for (input) |_, idx| { const cup_label = (try std.fmt.parseUnsigned(CupType, input[idx .. idx + 1], 10)) - 1; if (idx == 0) self.current = cup_label; if (idx != 0) array[prev] = cup_label; if (idx == input.len - 1) array[cup_label] = self.current; prev = cup_label; } } fn move(self: *@This()) !void { // std.debug.print("current: {d}\n", .{self.current + 1}); const cup1 = self.cups.items[self.current]; const cup2 = self.cups.items[cup1]; const cup3 = self.cups.items[cup2]; // std.debug.print("pick up: {d} {d} {d}\n", .{ cup1 + 1, cup2 + 1, cup3 + 1 }); const destination = blk: { var dest: CupType = self.current; while (true) { if (dest != 0) dest -= 1 else dest = SIZE - 1; if (dest != self.current and dest != cup1 and dest != cup2 and dest != cup3) { break; } } break :blk dest; }; // std.debug.print("destination: {}\n", .{destination + 1}); // 1. point current to item after cup3 self.cups.items[self.current] = self.cups.items[cup3]; // 2. point cup3 to destination's next self.cups.items[cup3] = self.cups.items[destination]; // 3. point destination to cup1 self.cups.items[destination] = cup1; // update current self.current = self.cups.items[self.current]; } }; } test "day23a" { try std.testing.expectEqual(@as(usize, 78569234), try first(std.testing.allocator)); } test "day23b" { try std.testing.expectEqual(@as(usize, 565615814504), try second(std.testing.allocator)); } const test_input = "389125467";