const std = @import("std"); const print = std.debug.print; const assert = std.debug.assert; const ArrayList = std.ArrayList; const HashMap = std.HashMap; const mem = std.mem; const fin = mem.trim(u8, @embedFile("./input.txt"), &std.ascii.whitespace); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); const Combo = enum(u32) { high, pair1, pair2, triple, full, quad, quint, }; const Player = struct { bid: u64, hand: [5]u8, combo: Combo, fn evalHand(hand: []const u8) Combo { var cardF = [_]u32{0} ** 256; for (hand) |c| { cardF[c] += 1; } var combo = Combo.high; for (cardF) |f| { switch (f) { 0, 1 => {}, 2 => { combo = switch (combo) { .pair1 => .pair2, .triple => .full, else => .pair1, }; }, 3 => combo = if (combo == Combo.pair1) .full else .triple, 4 => combo = .quad, 5 => combo = .quint, else => unreachable, } } return combo; } fn evalHandWithJoker(hand: []const u8) Combo { var cardF = [_]u32{0} ** 256; var cmax: usize = 0; for (hand) |c| { cardF[c] += 1; if (c != 'J' and cardF[c] > cardF[cmax]) { cmax = c; } } cardF[cmax] += cardF['J']; var combo = Combo.high; for (cardF, 0..) |f, c| { if (c == 'J') { continue; } switch (f) { 0, 1 => {}, 2 => { combo = switch (combo) { .pair1 => .pair2, .triple => .full, else => .pair1, }; }, 3 => combo = if (combo == Combo.pair1) .full else .triple, 4 => combo = .quad, 5 => combo = .quint, else => unreachable, } } return combo; } pub fn init(hand: []const u8, bid: u64) Player { var res: Player = undefined; res.bid = bid; res.combo = Player.evalHand(hand); mem.copy(u8, &res.hand, hand); return res; } pub fn initWithJoker(hand: []const u8, bid: u64) Player { var res: Player = undefined; res.bid = bid; res.combo = Player.evalHandWithJoker(hand); mem.copy(u8, &res.hand, hand); return res; } }; fn cardValue(c: u8) u64 { switch (c) { '2'...'9' => return c - '0', 'T' => return 10, 'J' => return 11, 'Q' => return 12, 'K' => return 13, 'A' => return 14, else => unreachable, } } fn cmpCard(_: void, lhs: u8, rhs: u8) bool { return cardValue(lhs) < cardValue(rhs); } fn playerCmp(_: void, lhs: Player, rhs: Player) bool { if (lhs.combo != rhs.combo) { return @intFromEnum(lhs.combo) < @intFromEnum(rhs.combo); } for (lhs.hand, rhs.hand) |l, r| { if (l != r) { return cmpCard(undefined, l, r); } } return false; } fn cardValueWithJoker(c: u8) u64 { switch (c) { '2'...'9' => return c - '0', 'T' => return 10, 'J' => return 0, 'Q' => return 12, 'K' => return 13, 'A' => return 14, else => unreachable, } } fn cmpCardWithJoker(_: void, lhs: u8, rhs: u8) bool { return cardValueWithJoker(lhs) < cardValueWithJoker(rhs); } fn playerCmpWithJoker(_: void, lhs: Player, rhs: Player) bool { if (lhs.combo != rhs.combo) { return @intFromEnum(lhs.combo) < @intFromEnum(rhs.combo); } for (lhs.hand, rhs.hand) |l, r| { if (l != r) { return cmpCardWithJoker(undefined, l, r); } } return false; } pub fn part1(players: []Player) void { var ans: u64 = 0; mem.sort(Player, players, {}, playerCmp); for (players, 1..) |p, rank| { ans += p.bid * rank; } print("{d}\n", .{ans}); } pub fn part2(players: []Player) void { var ans: u64 = 0; mem.sort(Player, players, {}, playerCmpWithJoker); for (players, 1..) |p, rank| { ans += p.bid * rank; } print("{d}\n", .{ans}); } pub fn main() !void { var splitLines = mem.splitScalar(u8, fin, '\n'); var players_pt1 = ArrayList(Player).init(allocator); var players_pt2 = ArrayList(Player).init(allocator); defer { players_pt1.deinit(); players_pt2.deinit(); } while (splitLines.next()) |line| { var splitElem = mem.splitScalar(u8, line, ' '); const hand = splitElem.next().?; const bid = try std.fmt.parseInt(u64, splitElem.next().?, 10); try players_pt1.append(Player.init(hand, bid)); try players_pt2.append(Player.initWithJoker(hand, bid)); } part1(players_pt1.items); part2(players_pt2.items); }