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(self: *@This(), withJoker: bool) void { var cardF = [_]u32{0} ** 256; var cmax: usize = 0; for (self.hand) |c| { cardF[c] += 1; if (withJoker and c != 'J' and cardF[c] > cardF[cmax]) { cmax = c; } } if (withJoker) { cardF[cmax] += cardF['J']; cardF['J'] = 0; } var combo = Combo.high; for (cardF) |f| { combo = switch (f) { 0, 1 => continue, 2 => switch (combo) { .pair1 => .pair2, .triple => .full, else => .pair1, }, 3 => if (combo == Combo.pair1) .full else .triple, 4 => .quad, 5 => .quint, else => unreachable, }; } self.combo = combo; } pub fn lessThan(valueFunc: *const fn (u8) u64, 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(valueFunc, l, r); } } return false; } pub fn init(hand: []const u8, bid: u64) Player { var res: Player = undefined; res.bid = bid; mem.copy(u8, &res.hand, hand); return res; } pub fn deinit(self: @This()) void { allocator.free(self.hand); } }; fn cardValue(c: u8) u64 { return switch (c) { '2'...'9' => c - '0', 'T' => 10, 'J' => 11, 'Q' => 12, 'K' => 13, 'A' => 14, else => unreachable, }; } fn cardValueWithJoker(c: u8) u64 { return switch (c) { '2'...'9' => c - '0', 'T' => 10, 'J' => 0, 'Q' => 12, 'K' => 13, 'A' => 14, else => unreachable, }; } fn cmpCard(valueFunc: *const fn (u8) u64, lhs: u8, rhs: u8) bool { return valueFunc(lhs) < valueFunc(rhs); } pub fn part1(players: []Player) void { var ans: u64 = 0; for (players) |*p| { p.evalHand(false); } mem.sort(Player, players, &cardValue, Player.lessThan); for (players, 1..) |p, rank| { ans += p.bid * rank; } print("{d}\n", .{ans}); } pub fn part2(players: []Player) void { var ans: u64 = 0; for (players) |*p| { p.evalHand(true); } mem.sort(Player, players, &cardValueWithJoker, Player.lessThan); 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 = ArrayList(Player).init(allocator); defer { players.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.append(Player.init(hand, bid)); } part1(players.items); part2(players.items); }