Let's see how far I get this year.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

day07-2.py 5.0KB

11 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. from collections import Counter
  2. from helpers import Helper
  3. helper = Helper(debug=True)
  4. debug = helper.debug
  5. load_input = helper.load_input
  6. CARD_VALUES = ["J"]
  7. for i in range(2,10):
  8. CARD_VALUES.append(str(i))
  9. CARD_VALUES.extend(["T", "Q", "K", "A"])
  10. HAND_VALUES = [
  11. "High card",
  12. "One pair",
  13. "Two pair",
  14. "Three of a kind",
  15. "Full House",
  16. "Four of a kind",
  17. "Five of a kind"
  18. ]
  19. class Hand:
  20. def __init__(self, cards, bid):
  21. self.cards = cards
  22. self.bid = bid
  23. @property
  24. def value(self):
  25. card_counter = Counter(self.cards)
  26. card_counts = card_counter.most_common()
  27. # We have to separate J being the most common card out,
  28. # or else it might get counted twice.
  29. if card_counts[0][0] == "J":
  30. # Five jacks is five of a kind.
  31. # Four jacks is five of a kind too, because the other card counts.
  32. # Three jacks plus two of another card is five of a kind.
  33. if (card_counts[0][1] == 5
  34. or card_counts[0][1] == 4
  35. or (card_counts[0][1] == 3 and card_counts[1][1] == 2)
  36. ):
  37. return "Five of a kind"
  38. # Three jacks is four of a kind, because the next most common card counts.
  39. # Two jacks plus two of another card is four of a kind.
  40. if (card_counts[0][1] == 3
  41. or (card_counts[0][1] == 2 and card_counts[1][1] == 2)
  42. ):
  43. return "Four of a kind"
  44. # Weirdly, you can only get a full house if there's only one jack...
  45. # and if that's the case, it won't be at the front of most_common().
  46. if card_counts[0][1] == 2:
  47. return "Three of a kind"
  48. # If J is at the front of most_common(), and there's only one of it,
  49. # then there's no more than 1 of any other card. So we have a pair.
  50. return "One pair"
  51. # Okay, done with J being at the front.
  52. # There are five cards of a kind, or (three/four) cards of a kind and
  53. # (one/two) J.
  54. # If there are only two of a kind at the front, then there aren't more
  55. # than two of any other kind, so we can't get to 5.
  56. if card_counts[0][1] == 5 or card_counts[0][1] + card_counter["J"] == 5:
  57. return "Five of a kind"
  58. # There are four cards of a kind without a J, or (two/three) cards of
  59. # a kind and (one/two) J.
  60. if card_counts[0][1] == 4 or card_counts[0][1] + card_counter["J"] == 4:
  61. return "Four of a kind"
  62. # There are three cards of a kind without a J, or two cards of a kind and
  63. # exactly one J.
  64. if card_counts[0][1] == 3 or card_counts[0][1] + card_counter["J"] == 3:
  65. # We know the most common card isn't a J; we already covered that
  66. # in a separate branch.
  67. # If the most common count is 3 and there's a J, we already covered
  68. # that too, with four and five of a kind.
  69. # If the most common count is 2 and there are 2 Js, we already covered
  70. # that with four of a kind.
  71. # So if the most common count is 2 and there's a J, it can't be in second
  72. # position, and if the most common count is 3 then there can't be a J.
  73. # So we can discard the possibility that J is in second position.
  74. if card_counts[1][1] == 2:
  75. return "Full House"
  76. return "Three of a kind"
  77. # There are two of the most common card without any Js, or one and a single J.
  78. if card_counts[0][1] == 2 or card_counts[0][1] + card_counter["J"] == 2:
  79. # Same logic as above. If J were the second-most-common card we'd have
  80. # already encountered it.
  81. if card_counts[1][1] == 2:
  82. return "Two pair"
  83. return "One pair"
  84. return "High card"
  85. def __lt__(self, other):
  86. # They have different hand values
  87. if self.value != other.value:
  88. return HAND_VALUES.index(self.value) < HAND_VALUES.index(other.value)
  89. # They have the same hand value
  90. # So check each card in sequence
  91. for i in range(len(self.cards)):
  92. if self.cards[i] != other.cards[i]:
  93. # They have different nth cards
  94. return CARD_VALUES.index(self.cards[i]) < CARD_VALUES.index(other.cards[i])
  95. # They're the same
  96. return False
  97. def __str__(self):
  98. return f"Camel Cards hand: {self.cards} (value {self.value}, bid {self.bid})"
  99. def __repr__(self):
  100. return self.__str__()
  101. def main():
  102. lines = load_input(7)
  103. hands = []
  104. for line in lines:
  105. cards, bid = line.split()
  106. hands.append(Hand(
  107. cards = cards,
  108. bid = bid
  109. ))
  110. hands.sort()
  111. print(hands[:10])
  112. total_winnings = 0
  113. for i, hand in enumerate(hands):
  114. total_winnings += ((i+1) * int(hand.bid))
  115. print(f"Total winnings: {total_winnings}")
  116. if __name__ == "__main__":
  117. main()