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 (5867B)


      1const std = @import("std");
      2const aoc = @import("aoc");
      3
      4const Player = enum { P1, P2 };
      5const GameResult = struct { score: u64, winner: Player };
      6
      7fn parseInput(input: []u8, decks: [2]*std.ArrayList(u32)) !void {
      8    var partit = std.mem.split(u8, input, "\n\n");
      9    var decknum: u32 = 0;
     10    while (partit.next()) |part| : (decknum += 1) {
     11        if (decknum == 2) break;
     12        var lineit = std.mem.tokenize(u8, part, "\n");
     13        _ = lineit.next();
     14        while (lineit.next()) |line| {
     15            try decks[decknum].append(try std.fmt.parseInt(u32, line, 10));
     16        }
     17    }
     18    if (decknum < 2) return aoc.Error.InvalidInput;
     19}
     20
     21fn printDeck(deck: *std.ArrayList(u32)) void {
     22    for (deck.items) |v, i| {
     23        if (i > 0) aoc.debugfmt(", ", .{});
     24        aoc.debugfmt("{}", .{v});
     25    }
     26}
     27
     28fn printRoundInfo(roundnum: u32, decks: [2]*std.ArrayList(u32)) void {
     29    aoc.debugfmt("\n-- ROUND {} --\n", .{roundnum});
     30    aoc.debugfmt("Player 1's deck: ", .{});
     31    printDeck(decks[0]);
     32    aoc.debugfmt("\nPlayer 2's deck: ", .{});
     33    printDeck(decks[1]);
     34    aoc.debugfmt("\n", .{});
     35}
     36
     37fn printPickedCards(card1: u32, card2: u32) void {
     38    aoc.debugfmt("Player 1 plays: {}\n", .{card1});
     39    aoc.debugfmt("Player 2 plays: {}\n", .{card2});
     40}
     41
     42fn calcScore(deck: *std.ArrayList(u32)) u64 {
     43    var score: u64 = 0;
     44    for (deck.items) |v, i| {
     45        score += v * (deck.items.len - i);
     46    }
     47    return score;
     48}
     49
     50fn copyList(allocator: std.mem.Allocator, list: []u32) !std.ArrayList(u32) {
     51    var newlist = std.ArrayList(u32).init(allocator);
     52    errdefer newlist.deinit();
     53
     54    for (list) |v| {
     55        try newlist.append(v);
     56    }
     57
     58    return newlist;
     59}
     60
     61fn part1Round(allocator: std.mem.Allocator, roundnum: u32, decks: [2]*std.ArrayList(u32)) anyerror!void {
     62    _ = allocator;
     63    if (aoc.debug) printRoundInfo(roundnum, decks);
     64    const card1: u32 = decks[0].orderedRemove(0);
     65    const card2: u32 = decks[1].orderedRemove(0);
     66    if (aoc.debug) printPickedCards(card1, card2);
     67    if (card1 > card2) {
     68        if (aoc.debug) aoc.debugfmt("Player1 wins\n", .{});
     69        try decks[0].append(card1);
     70        try decks[0].append(card2);
     71    } else if (card2 > card1) {
     72        if (aoc.debug) aoc.debugfmt("Player2 wins\n", .{});
     73        try decks[1].append(card2);
     74        try decks[1].append(card1);
     75    } else return aoc.Error.InvalidInput;
     76}
     77
     78fn part2Round(
     79    allocator: std.mem.Allocator,
     80    roundnum: u32,
     81    decks: [2]*std.ArrayList(u32),
     82) anyerror!void {
     83    if (aoc.debug) printRoundInfo(roundnum, decks);
     84    const card1: u32 = decks[0].orderedRemove(0);
     85    const card2: u32 = decks[1].orderedRemove(0);
     86    if (aoc.debug) printPickedCards(card1, card2);
     87
     88    var winner = @intToEnum(Player, @boolToInt(card2 > card1));
     89    if (card1 <= decks[0].items.len and card2 <= decks[1].items.len) {
     90        if (aoc.debug) aoc.debugfmt("\nStarting subgame..\n", .{});
     91        var tmp_deck1 = try copyList(allocator, decks[0].items[0..card1]);
     92        defer tmp_deck1.deinit();
     93
     94        var tmp_deck2 = try copyList(allocator, decks[1].items[0..card2]);
     95        defer tmp_deck2.deinit();
     96
     97        const result = try playGame(allocator, [2]*std.ArrayList(u32){ &tmp_deck1, &tmp_deck2 }, part2Round);
     98        winner = result.winner;
     99        if (aoc.debug) {
    100            const wp: u32 = if (winner == Player.P1) 1 else 2;
    101            aoc.debugfmt("Player{} wins the subgame\n...anyway, back to previous game\n\n", .{wp});
    102        }
    103    }
    104
    105    if (winner == Player.P1) {
    106        if (aoc.debug) aoc.debugfmt("Player1 wins the round\n", .{});
    107        try decks[0].append(card1);
    108        try decks[0].append(card2);
    109    } else if (winner == Player.P2) {
    110        if (aoc.debug) aoc.debugfmt("Player2 wins the round\n", .{});
    111        try decks[1].append(card2);
    112        try decks[1].append(card1);
    113    } else return aoc.Error.InvalidInput;
    114}
    115
    116fn playGame(
    117    allocator: std.mem.Allocator,
    118    decks: [2]*std.ArrayList(u32),
    119    roundFunc: *const fn (
    120        alloc: std.mem.Allocator,
    121        num: u32,
    122        decks: [2]*std.ArrayList(u32),
    123    ) anyerror!void,
    124) !GameResult {
    125    var ctxstack = std.ArrayList(u64).init(allocator);
    126    defer ctxstack.deinit();
    127
    128    var roundnum: u32 = 1;
    129    while (decks[0].items.len > 0 and decks[1].items.len > 0) : (roundnum += 1) {
    130        try roundFunc(allocator, roundnum, decks);
    131
    132        var hash: u64 = calcScore(decks[0]) + 100000 * calcScore(decks[1]);
    133        if (std.mem.indexOfScalar(u64, ctxstack.items, hash) != null) {
    134            return GameResult{ .score = calcScore(decks[0]), .winner = Player.P1 };
    135        }
    136
    137        try ctxstack.append(hash);
    138    }
    139
    140    var nonempty = if (decks[0].items.len != 0) decks[0] else decks[1];
    141    return GameResult{
    142        .score = calcScore(nonempty),
    143        .winner = if (nonempty == decks[0]) Player.P1 else Player.P2,
    144    };
    145}
    146
    147fn part1(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
    148    _ = args;
    149
    150    var deck1 = std.ArrayList(u32).init(allocator);
    151    defer deck1.deinit();
    152
    153    var deck2 = std.ArrayList(u32).init(allocator);
    154    defer deck2.deinit();
    155
    156    const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 };
    157    try parseInput(input, decks);
    158
    159    const result = try playGame(allocator, decks, part1Round);
    160
    161    return try std.fmt.allocPrint(allocator, "{}", .{result.score});
    162}
    163
    164fn part2(allocator: std.mem.Allocator, input: []u8, args: [][]u8) !?[]u8 {
    165    _ = args;
    166
    167    var deck1 = std.ArrayList(u32).init(allocator);
    168    defer deck1.deinit();
    169
    170    var deck2 = std.ArrayList(u32).init(allocator);
    171    defer deck2.deinit();
    172
    173    const decks = [2]*std.ArrayList(u32){ &deck1, &deck2 };
    174    try parseInput(input, decks);
    175
    176    const result = try playGame(allocator, decks, part2Round);
    177
    178    return try std.fmt.allocPrint(allocator, "{}", .{result.score});
    179}
    180
    181pub const main = aoc.main(part1, part2, .{ "32272", "33206" });