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