|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- from collections import Counter
-
- from helpers import Helper
-
- helper = Helper(debug=True)
- debug = helper.debug
- load_input = helper.load_input
-
- CARD_VALUES = ["J"]
- for i in range(2,10):
- CARD_VALUES.append(str(i))
- CARD_VALUES.extend(["T", "Q", "K", "A"])
-
- HAND_VALUES = [
- "High card",
- "One pair",
- "Two pair",
- "Three of a kind",
- "Full House",
- "Four of a kind",
- "Five of a kind"
- ]
-
- class Hand:
- def __init__(self, cards, bid):
- self.cards = cards
- self.bid = bid
-
- @property
- def value(self):
- card_counter = Counter(self.cards)
- card_counts = card_counter.most_common()
- # We have to separate J being the most common card out,
- # or else it might get counted twice.
- if card_counts[0][0] == "J":
- # Five jacks is five of a kind.
- # Four jacks is five of a kind too, because the other card counts.
- # Three jacks plus two of another card is five of a kind.
- if (card_counts[0][1] == 5
- or card_counts[0][1] == 4
- or (card_counts[0][1] == 3 and card_counts[1][1] == 2)
- ):
- return "Five of a kind"
- # Three jacks is four of a kind, because the next most common card counts.
- # Two jacks plus two of another card is four of a kind.
- if (card_counts[0][1] == 3
- or (card_counts[0][1] == 2 and card_counts[1][1] == 2)
- ):
- return "Four of a kind"
- # Weirdly, you can only get a full house if there's only one jack...
- # and if that's the case, it won't be at the front of most_common().
- if card_counts[0][1] == 2:
- return "Three of a kind"
- # If J is at the front of most_common(), and there's only one of it,
- # then there's no more than 1 of any other card. So we have a pair.
- return "One pair"
-
- # Okay, done with J being at the front.
- # There are five cards of a kind, or (three/four) cards of a kind and
- # (one/two) J.
- # If there are only two of a kind at the front, then there aren't more
- # than two of any other kind, so we can't get to 5.
- if card_counts[0][1] == 5 or card_counts[0][1] + card_counter["J"] == 5:
- return "Five of a kind"
- # There are four cards of a kind without a J, or (two/three) cards of
- # a kind and (one/two) J.
- if card_counts[0][1] == 4 or card_counts[0][1] + card_counter["J"] == 4:
- return "Four of a kind"
- # There are three cards of a kind without a J, or two cards of a kind and
- # exactly one J.
- if card_counts[0][1] == 3 or card_counts[0][1] + card_counter["J"] == 3:
- # We know the most common card isn't a J; we already covered that
- # in a separate branch.
- # If the most common count is 3 and there's a J, we already covered
- # that too, with four and five of a kind.
- # If the most common count is 2 and there are 2 Js, we already covered
- # that with four of a kind.
- # So if the most common count is 2 and there's a J, it can't be in second
- # position, and if the most common count is 3 then there can't be a J.
- # So we can discard the possibility that J is in second position.
- if card_counts[1][1] == 2:
- return "Full House"
- return "Three of a kind"
- # There are two of the most common card without any Js, or one and a single J.
- if card_counts[0][1] == 2 or card_counts[0][1] + card_counter["J"] == 2:
- # Same logic as above. If J were the second-most-common card we'd have
- # already encountered it.
- if card_counts[1][1] == 2:
- return "Two pair"
- return "One pair"
- return "High card"
-
- def __lt__(self, other):
- # They have different hand values
- if self.value != other.value:
- return HAND_VALUES.index(self.value) < HAND_VALUES.index(other.value)
- # They have the same hand value
- # So check each card in sequence
- for i in range(len(self.cards)):
- if self.cards[i] != other.cards[i]:
- # They have different nth cards
- return CARD_VALUES.index(self.cards[i]) < CARD_VALUES.index(other.cards[i])
- # They're the same
- return False
-
- def __str__(self):
- return f"Camel Cards hand: {self.cards} (value {self.value}, bid {self.bid})"
-
- def __repr__(self):
- return self.__str__()
-
-
- def main():
- lines = load_input(7)
- hands = []
- for line in lines:
- cards, bid = line.split()
- hands.append(Hand(
- cards = cards,
- bid = bid
- ))
- hands.sort()
- print(hands[:10])
-
- total_winnings = 0
- for i, hand in enumerate(hands):
- total_winnings += ((i+1) * int(hand.bid))
-
- print(f"Total winnings: {total_winnings}")
-
- if __name__ == "__main__":
- main()
|