aoc/2023/07/07.py
2024-03-05 03:32:18 -06:00

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)