156 lines
3.5 KiB
Python
156 lines
3.5 KiB
Python
from collections import defaultdict, Counter
|
|
|
|
with open("input.txt") as f:
|
|
hands = f.read().split("\n")
|
|
hands = [(hand.split()[0], int(hand.split()[1])) for hand in hands]
|
|
|
|
|
|
def rank(hands):
|
|
ranked = defaultdict(list)
|
|
for hand in hands:
|
|
r = get_rank(hand)
|
|
ranked[r].append(hand)
|
|
return ranked
|
|
|
|
def get_rank(hand):
|
|
c = Counter(hand[0])
|
|
counts = c.most_common(2)
|
|
# extract counts
|
|
first = counts[0][1]
|
|
second = counts[1][1] if len(counts) == 2 else 0
|
|
# discriminate based on counts
|
|
d = (first, second)
|
|
match d:
|
|
case (1,1):
|
|
return "HC" # high card
|
|
case (2,1):
|
|
return "1P" # one pair
|
|
case (2,2):
|
|
return "2P" # two pair
|
|
case (3,1):
|
|
return "3K" # three of a kind
|
|
case (3,2):
|
|
return "FH" # full house
|
|
case (4,1):
|
|
return "4K" # four of a kind
|
|
case (5,0):
|
|
return "5K" # five of a kind
|
|
|
|
def order(hand: str):
|
|
"""
|
|
Convert to base-15 for proper sorting
|
|
"""
|
|
# A = ten
|
|
# B = jack
|
|
# C = queen
|
|
# D = king
|
|
# E = ace
|
|
return hand.replace(
|
|
"A", "E"
|
|
).replace(
|
|
"K", "D"
|
|
).replace(
|
|
"Q", "C"
|
|
).replace(
|
|
"J", "B"
|
|
).replace(
|
|
"T", "A"
|
|
)
|
|
|
|
def part1(hands):
|
|
ranked = rank(hands)
|
|
ranked_hands = []
|
|
ranked_hands += sorted(ranked["HC"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["1P"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["2P"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["3K"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["FH"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["4K"], key=lambda hand: order(hand[0]))
|
|
ranked_hands += sorted(ranked["5K"], key=lambda hand: order(hand[0]))
|
|
answer = sum(
|
|
[
|
|
hand[1] * (rank + 1)
|
|
for rank, hand in enumerate(ranked_hands)
|
|
]
|
|
)
|
|
print(answer)
|
|
|
|
part1(hands)
|
|
|
|
### Part 2
|
|
|
|
def order2(hand: str):
|
|
"""
|
|
Convert to base-15 for proper sorting
|
|
"""
|
|
# A = ten
|
|
# B = jack
|
|
# C = queen
|
|
# D = king
|
|
# E = ace
|
|
return hand.replace(
|
|
"A", "E"
|
|
).replace(
|
|
"K", "D"
|
|
).replace(
|
|
"Q", "C"
|
|
).replace(
|
|
"J", "1" # jokers are worth less than 2
|
|
).replace(
|
|
"T", "A"
|
|
)
|
|
|
|
def get_rank2(hand):
|
|
c1 = Counter(hand[0])
|
|
c2 = Counter(hand[0].replace("J", ""))
|
|
jokers_count = c1.get("J") or 0
|
|
counts = c2.most_common(2)
|
|
if not counts:
|
|
counts = [('', 0),('', 0)] # prevent index error when 5 jokers
|
|
# extract counts
|
|
first = counts[0][1] + jokers_count # convert jokers to highest-scoring card
|
|
second = counts[1][1] if len(counts) == 2 else 0
|
|
# discriminate based on counts
|
|
d = (first, second)
|
|
match d:
|
|
case (1,1):
|
|
return "HC" # high card
|
|
case (2,1):
|
|
return "1P" # one pair
|
|
case (2,2):
|
|
return "2P" # two pair
|
|
case (3,1):
|
|
return "3K" # three of a kind
|
|
case (3,2):
|
|
return "FH" # full house
|
|
case (4,1):
|
|
return "4K" # four of a kind
|
|
case (5,0):
|
|
return "5K" # five of a kind
|
|
|
|
def rank2(hands):
|
|
ranked = defaultdict(list)
|
|
for hand in hands:
|
|
r = get_rank2(hand)
|
|
ranked[r].append(hand)
|
|
return ranked
|
|
|
|
def part2(hands):
|
|
ranked = rank2(hands)
|
|
ranked_hands = []
|
|
ranked_hands += sorted(ranked["HC"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["1P"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["2P"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["3K"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["FH"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["4K"], key=lambda hand: order2(hand[0]))
|
|
ranked_hands += sorted(ranked["5K"], key=lambda hand: order2(hand[0]))
|
|
answer = sum(
|
|
[
|
|
hand[1] * (rank + 1)
|
|
for rank, hand in enumerate(ranked_hands)
|
|
]
|
|
)
|
|
print(answer)
|
|
|
|
part2(hands) |