Solution to problem 7 in Python

This commit is contained in:
David Doblas Jiménez 2023-12-07 11:43:32 +01:00
parent adb94b2c63
commit 9d42f71b9c

281
src/Year_2023/Day07.py Normal file
View File

@ -0,0 +1,281 @@
# --- Day 7: Camel Cards ---
# Your all-expenses-paid trip turns out to be a one-way, five-minute ride in an
# airship. (At least it's a cool airship!) It drops you off at the edge of a
# vast desert and descends back to Island Island.
# "Did you bring the parts?"
# You turn around to see an Elf completely covered in white clothing, wearing
# goggles, and riding a large camel.
# "Did you bring the parts?" she asks again, louder this time. You aren't sure
# what parts she's looking for; you're here to figure out why the sand stopped.
# "The parts! For the sand, yes! Come with me; I will show you." She beckons you
# onto the camel.
# After riding a bit across the sands of Desert Island, you can see what look
# like very large rocks covering half of the horizon. The Elf explains that the
# rocks are all along the part of Desert Island that is directly above Island
# Island, making it hard to even get there. Normally, they use big machines to
# move the rocks and filter the sand, but the machines have broken down because
# Desert Island recently stopped receiving the parts they need to fix the
# machines.
# You've already assumed it'll be your job to figure out why the parts stopped
# when she asks if you can help. You agree automatically.
# Because the journey will take a few days, she offers to teach you the game of
# Camel Cards. Camel Cards is sort of similar to poker except it's designed to
# be easier to play while riding a camel.
# In Camel Cards, you get a list of hands, and your goal is to order them based
# on the strength of each hand. A hand consists of five cards labeled one of A,
# K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2. The relative strength of each card
# follows this order, where A is the highest and 2 is the lowest.
# Every hand is exactly one type. From strongest to weakest, they are:
# Five of a kind, where all five cards have the same label: AAAAA
# Four of a kind, where four cards have the same label and one card has a
# different label: AA8AA
# Full house, where three cards have the same label, and the remaining two
# cards share a different label: 23332
# Three of a kind, where three cards have the same label, and the remaining
# two cards are each different from any other card in the hand: TTT98
# Two pair, where two cards share one label, two other cards share a second
# label, and the remaining card has a third label: 23432
# One pair, where two cards share one label, and the other three cards have
# a different label from the pair and each other: A23A4
# High card, where all cards' labels are distinct: 23456
# Hands are primarily ordered based on type; for example, every full house is
# stronger than any three of a kind.
# If two hands have the same type, a second ordering rule takes effect. Start by
# comparing the first card in each hand. If these cards are different, the hand
# with the stronger first card is considered stronger. If the first card in each
# hand have the same label, however, then move on to considering the second card
# in each hand. If they differ, the hand with the higher second card wins;
# otherwise, continue with the third card in each hand, then the fourth, then
# the fifth.
# So, 33332 and 2AAAA are both four of a kind hands, but 33332 is stronger
# because its first card is stronger. Similarly, 77888 and 77788 are both a full
# house, but 77888 is stronger because its third card is stronger (and both
# hands have the same first and second card).
# To play Camel Cards, you are given a list of hands and their corresponding bid
# (your puzzle input). For example:
# 32T3K 765
# T55J5 684
# KK677 28
# KTJJT 220
# QQQJA 483
# This example shows five hands; each hand is followed by its bid amount. Each
# hand wins an amount equal to its bid multiplied by its rank, where the weakest
# hand gets rank 1, the second-weakest hand gets rank 2, and so on up to the
# strongest hand. Because there are five hands in this example, the strongest
# hand will have rank 5 and its bid will be multiplied by 5.
# So, the first step is to put the hands in order of strength:
# 32T3K is the only one pair and the other hands are all a stronger type, so
# it gets rank 1.
# KK677 and KTJJT are both two pair. Their first cards both have the same
# label, but the second card of KK677 is stronger (K vs T), so KTJJT gets rank 2
# and KK677 gets rank 3.
# T55J5 and QQQJA are both three of a kind. QQQJA has a stronger first card,
# so it gets rank 5 and T55J5 gets rank 4.
# Now, you can determine the total winnings of this set of hands by adding up
# the result of multiplying each hand's bid with its rank (765 * 1 + 220 * 2 +
# 28 * 3 + 684 * 4 + 483 * 5). So the total winnings in this example are 6440.
# Find the rank of every hand in your set. What are the total winnings?
from collections import Counter
with open("files/P7.txt") as f:
hands = [line for line in f.read().strip().split("\n")]
sorting = ["2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A"]
types = [
"five_of_kind",
"four_of_kind",
"full_house",
"three_of_kind",
"two_pair",
"one_pair",
"high_card",
]
def part1():
cards = {}
# from weakest to strongest
for type in reversed(types):
cards[type] = {}
for h in hands:
hand, bid = h.split()
scored_hand = Counter(hand)
# high card
if len(scored_hand) == 5:
cards["high_card"][hand] = bid
# one pair
elif len(scored_hand) == 4:
cards["one_pair"][hand] = bid
# two pair, three of a kind
elif len(scored_hand) == 3:
if 3 not in scored_hand.values():
cards["two_pair"][hand] = bid
else:
cards["three_of_kind"][hand] = bid
# full house, four of a kind
elif len(scored_hand) == 2:
if 4 not in scored_hand.values():
cards["full_house"][hand] = bid
else:
cards["four_of_kind"][hand] = bid
# five of a kind
else:
cards["five_of_kind"][hand] = bid
strength = {}
for type in cards:
strength[type] = sorted(
cards[type].items(), key=lambda x: [sorting.index(c) for c in x[0]]
)
total = 0
rank = 1
for val in strength.values():
for hand in val:
total += int(hand[1]) * rank
rank += 1
print(f"The total winnings are {total}")
# --- Part Two ---
# To make things a little more interesting, the Elf introduces one additional
# rule. Now, J cards are jokers - wildcards that can act like whatever card
# would make the hand the strongest type possible.
# To balance this, J cards are now the weakest individual cards, weaker even
# than 2. The other cards stay in the same order: A, K, Q, T, 9, 8, 7, 6, 5, 4,
# 3, 2, J.
# J cards can pretend to be whatever card is best for the purpose of determining
# hand type; for example, QJJQ2 is now considered four of a kind. However, for
# the purpose of breaking ties between two hands of the same type, J is always
# treated as J, not the card it's pretending to be: JKKK2 is weaker than QQQQ2
# because J is weaker than Q.
# Now, the above example goes very differently:
# 32T3K 765
# T55J5 684
# KK677 28
# KTJJT 220
# QQQJA 483
# 32T3K is still the only one pair; it doesn't contain any jokers, so its
# strength doesn't increase.
# KK677 is now the only two pair, making it the second-weakest hand.
# T55J5, KTJJT, and QQQJA are now all four of a kind! T55J5 gets rank 3,
# QQQJA gets rank 4, and KTJJT gets rank 5.
# With the new joker rule, the total winnings in this example are 5905.
# Using the new joker rule, find the rank of every hand in your set. What are
# the new total winnings?
def part2():
# introduce Jokers as weakest card
sorting.insert(0, "J")
cards = {}
# from weakest to strongest
for type in reversed(types):
cards[type] = {}
for h in hands:
hand, bid = h.split()
scored_hand = Counter(hand)
# five of a kind hand
if len(scored_hand) == 1:
cards["five_of_kind"][hand] = bid
else:
# balance the card
if "J" in scored_hand.keys():
jokers = scored_hand["J"]
del scored_hand["J"]
scored_hand = dict(
sorted(
scored_hand.items(),
key=lambda x: (x[1], [sorting.index(c) for c in x[0]]),
)
)
last_key, last_val = scored_hand.popitem()
scored_hand[last_key] = last_val + jokers
# high card
if len(scored_hand) == 5:
cards["high_card"][hand] = bid
# one pair
elif len(scored_hand) == 4:
cards["one_pair"][hand] = bid
# two pair, three of a kind
elif len(scored_hand) == 3:
if 3 not in scored_hand.values():
cards["two_pair"][hand] = bid
else:
cards["three_of_kind"][hand] = bid
# full house, four of a kind
elif len(scored_hand) == 2:
if 4 not in scored_hand.values():
cards["full_house"][hand] = bid
else:
cards["four_of_kind"][hand] = bid
# five of a kind
else:
cards["five_of_kind"][hand] = bid
strength = {}
for type in cards:
strength[type] = sorted(
cards[type].items(), key=lambda x: [sorting.index(c) for c in x[0]]
)
total = 0
rank = 1
for val in strength.values():
for hand in val:
total += int(hand[1]) * rank
rank += 1
print(f"The total winnings are {total}")
if __name__ == "__main__":
part1()
part2()