main.zig (5639B)
1const std = @import("std"); 2const aoc = @import("aoc"); 3 4// Links in this list are arranges in a circle 5const RingLink = struct { 6 data: u32, 7 next: *RingLink, 8 const Self = @This(); 9 10 pub fn insertNext(self: *Self, link: *RingLink) void { 11 link.next = self.next; 12 self.next = link; 13 } 14 15 pub fn popNext(self: *Self) !*Self { 16 const nlink = self.next; 17 if (nlink == self) return error.EmptyRingList; 18 self.next = nlink.next; 19 return nlink; 20 } 21 22 pub fn length(self: *Self) usize { 23 var count: usize = 1; 24 var link = self.next; 25 while (link != self) : (link = link.next) { 26 count += 1; 27 } 28 return count; 29 } 30 31 pub fn advance(self: *Self, count: usize) *RingLink { 32 var link = self; 33 var counter: usize = 0; 34 while (counter < count) : (link = link.next) { 35 counter += 1; 36 } 37 return link; 38 } 39}; 40 41fn printLinks(start: *RingLink, end: *RingLink, skipfirst: bool) void { 42 var link = start; 43 var b = skipfirst; 44 while (b or link != end) : (link = link.next) { 45 b = false; 46 aoc.debugfmt("{} ", .{link.data}); 47 } 48} 49 50fn parseInput(input: []const u8, lookup: []RingLink, list: *?*RingLink) !u32 { 51 var max: u32 = 0; 52 var link: *RingLink = undefined; 53 for (input) |c, i| { 54 if (c == '\n') break; 55 const v = try std.fmt.parseInt(u32, input[i .. i + 1], 10); 56 if (v > max) max = v; 57 if (list.* == null) { 58 list.* = &lookup[v]; 59 link = list.*.?; 60 link.next = link; 61 } else { 62 link.insertNext(&lookup[v]); 63 link = link.next; 64 } 65 } 66 return max; 67} 68 69fn doRound(list: *RingLink, len: u32, lookup: []RingLink, current: **RingLink, max: u32, round: usize) !void { 70 _ = list; 71 var target = (current.*.data + max - 2) % max + 1; 72 var k: usize = 0; 73 var check = current.*.next; 74 while (k < 9) : (k += 1) { 75 if (check.data == target) 76 target = (target + max - 2) % max + 1; 77 78 check = if (k % 3 == 0) current.*.next else check.next; 79 } 80 var closest = &lookup[target]; 81 82 if (aoc.debug) { 83 aoc.debugfmt("\n-- move {} --\ncups:", .{round}); 84 85 if (aoc.debuglvl == 1) { 86 var start = current.*.advance(len - ((round - 1) % len)); 87 var link = start; 88 while (true) { 89 if (link == current.*) { 90 aoc.debugfmt(" ({})", .{link.data}); 91 } else { 92 aoc.debugfmt(" {} ", .{link.data}); 93 } 94 link = link.next; 95 if (link == start) break; 96 } 97 aoc.debugfmt("\n", .{}); 98 } else { 99 aoc.debugfmt("\n{} {} {}\n", .{ current.*.data, target, closest.data }); 100 aoc.debugfmt(".. ", .{}); 101 printLinks(current.*, current.*.next.next.next.next, false); 102 aoc.debugfmt("..\n.. ", .{}); 103 printLinks(closest, closest.next.next.next.next, false); 104 aoc.debugfmt("..\n", .{}); 105 } 106 } 107 108 var i: usize = 0; 109 while (i < 3) : (i += 1) { 110 const poplink = try current.*.popNext(); 111 closest.insertNext(poplink); 112 closest = poplink; 113 } 114 115 current.* = current.*.next; 116} 117 118fn createLookup(allocator: std.mem.Allocator, len: usize) ![]RingLink { 119 var lookup = try allocator.alloc(RingLink, len + 1); 120 errdefer allocator.free(lookup); 121 122 var i: usize = 1; 123 while (i <= len) : (i += 1) { 124 lookup[i] = RingLink{ .data = @intCast(u32, i), .next = undefined }; 125 } 126 127 return lookup; 128} 129 130fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { 131 _ = args; 132 133 var lookup = try createLookup(allocator, 9); 134 defer allocator.free(lookup); 135 136 var list: ?*RingLink = null; 137 const max = try parseInput(input, lookup, &list); 138 if (list == null) return aoc.Error.InvalidInput; 139 const real_len = @intCast(u32, list.?.length()); 140 141 var round: usize = 0; 142 var current = list.?; 143 while (round < 100) : (round += 1) { 144 try doRound(list.?, real_len, lookup, ¤t, max, round + 1); 145 } 146 147 var start = &lookup[1]; 148 var link = start.next; 149 150 var parts = std.ArrayList(u8).init(allocator); 151 defer parts.deinit(); 152 while (link != start) : (link = link.next) { 153 try parts.append('0' + @intCast(u8, link.data)); 154 } 155 156 return try std.fmt.allocPrint(allocator, "{s}", .{parts.items}); 157} 158 159fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 { 160 _ = args; 161 162 var lookup = try createLookup(allocator, 1000000); 163 defer allocator.free(lookup); 164 165 var list: ?*RingLink = null; 166 _ = try parseInput(input, lookup, &list); 167 if (list == null) return aoc.Error.InvalidInput; 168 const real_len = list.?.length(); 169 170 var end = list.?.advance(real_len - 1); 171 var i = real_len + 1; 172 while (i <= 1000000) : (i += 1) { 173 end.insertNext(&lookup[i]); 174 end = end.next; 175 } 176 177 var round: usize = 0; 178 var current = list.?; 179 while (round < 10000000) : (round += 1) { 180 if (round % 1000000 == 0) aoc.debugfmt(".", .{}); 181 try doRound(list.?, 1000000, lookup, ¤t, 1000000, round + 1); 182 } 183 aoc.debugfmt("\n", .{}); 184 185 var link = (&lookup[1]).next; 186 187 const a = @intCast(u64, link.data); 188 const b = @intCast(u64, link.next.data); 189 aoc.debugfmt("{} * {} = {}\n", .{ a, b, a * b }); 190 191 return try std.fmt.allocPrint(allocator, "{}", .{a * b}); 192} 193 194pub const main = aoc.main(part1, part2, .{ "97245386", "156180332979" });