aoc-2020-zig

Advent of Code 2020 Solutions in Zig
git clone https://git.sinitax.com/sinitax/aoc-2020-zig
Log | Files | Refs | README | sfeed.txt

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, &current, 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, &current, 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" });