Advertisement
Guest User

Advent of Code 2023, Day 7 Python Attempt

a guest
Dec 11th, 2023
180
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.47 KB | Source Code | 0 0
  1. '''
  2. Advent of Code 2023, Day 7
  3. input: file
  4. output: int
  5.  
  6. input: hands of "Camel Cards" and their bets
  7.  
  8. Part1: Order hands in descending order of best hand to worst and multiply the bid by the hand placement. Sum all the winnings
  9. Part2: J's are now wild and weakest card. J's give the strongest hand possible
  10.  
  11. '''
  12. import re,math
  13.  
  14. class hand_class():
  15.  
  16.     card_values = {"2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "T":10, "J":11, "Q":12, "K":13, "A":14}
  17.     wild_cards = {"J":2, "2":3, "3":4, "4":5, "5":6, "6":7, "7":8, "8":9, "9":10, "T":11, "Q":12, "K":13, "A":14}
  18.  
  19.     def __init__(self,cards,bid):
  20.         self.cards = cards
  21.         self.bid = bid
  22.         self.parttwo = False
  23.         self.og_cards = cards
  24.         self.get_cardvalues()
  25.  
  26.     def set_parttwo(self):
  27.         self.parttwo = True
  28.         self.get_cardvalues()
  29.  
  30.     def get_cardvalues(self):
  31.         self.card_values = []
  32.  
  33.         #if parttwo is true, then "J" are the weakest value and use the hand_class.wild_cards values
  34.         if self.parttwo:
  35.             card_values = hand_class.wild_cards
  36.         else:
  37.             card_values = hand_class.card_values
  38.         for card in self.cards:
  39.             self.card_values.append(card_values[card])
  40.    
  41.     def __lt__(self,item):
  42.         #card_values is a list of ints representing the cards
  43.         #enables the use of the built-in sort function
  44.         return self.card_values < item.card_values
  45.  
  46.     def __str__(self):
  47.         #output giving data on the hand for easy comparison
  48.         str_values = str(self.card_values).replace("[","").replace("]","").replace(" ","")
  49.         return f"{str_values} :: {self.og_cards} :: {self.cards} : {self.bid}"
  50.    
  51. def main():
  52.     path = r"d:\scripts\projects\adventofcode\2023\day7"
  53.     file = r"\input.txt"
  54.     filepath = path + file
  55.  
  56.     with open(filepath,"r") as fh:
  57.         lines = fh.readlines()
  58.  
  59.     hands = []
  60.  
  61.     #generate a list of hand_class objects
  62.     for line in lines:
  63.         items = line.split()
  64.         hands.append(hand_class(items[0].strip(),int(items[-1].strip())))
  65.    
  66.     partone(hands)
  67.     parttwo(hands)
  68.  
  69. def partone(hands):
  70.     #dict of hand types. used numbers as the keys, as dicts are not stored in order, like lists
  71.     #reckon possible to do a list of lists instead ...
  72.     #1 -> 5 of a kind
  73.     #2 -> 4 of a kind
  74.     #3 -> full house
  75.     #4 -> 3 of a kind
  76.     #5 -> two pair
  77.     #6 -> one pair
  78.     #7 -> high card
  79.     winning_hands = {1:[],2:[],3:[],4:[],5:[],6:[],7:[]}
  80.     totalhands = len(hands)
  81.  
  82.     #sort hands using built-in function against card_values (list of ints of card_values of the hand)
  83.     #hands are sorted in ascending order and placed in the winning_hands dict of lists in the same order
  84.     hands.sort()
  85.  
  86.     sum = 0
  87.  
  88.     for hand in hands:
  89.         #find unique cards in the hand, along with number of unique cards
  90.         unique_cards = set(hand.cards)
  91.         unique_len = len(unique_cards)
  92.  
  93.         if unique_len == 1:
  94.             #five of a kind
  95.             winning_hands[1].append(hand)
  96.         elif unique_len == 2:
  97.             #full house or four of a kind
  98.             fullhouse = True
  99.             for card in unique_cards:
  100.                 #re.findall creates a list of the matching entries
  101.                 #if length is 4, then hand must be a four of a kind
  102.                 if len(re.findall(card,str(hand.cards))) == 4:
  103.                     fullhouse = False
  104.             if fullhouse:
  105.                 winning_hands[3].append(hand)
  106.             else:
  107.                 winning_hands[2].append(hand)
  108.         elif unique_len == 3:
  109.             #two pair or three of a kind
  110.             twopair = True
  111.             for card in unique_cards:
  112.                 #re.findall creates a list of matching strings
  113.                 #if length is 3, hand must a 3 of a kind
  114.                 if len(re.findall(card,str(hand.cards))) == 3:
  115.                     twopair = False
  116.             if twopair:
  117.                 winning_hands[5].append(hand)
  118.             else:
  119.                 winning_hands[4].append(hand)
  120.         elif unique_len == 4:
  121.             #one pair
  122.             winning_hands[6].append(hand)
  123.         elif unique_len == 5:
  124.             #high card
  125.             winning_hands[7].append(hand)
  126.            
  127.     for j in range(1,len(winning_hands)+1):
  128.         #since the sorting put the hands in ascending order
  129.         #need to look at the list is reverse order to get descending order
  130.         for hand in winning_hands[j][::-1]:
  131.             sum += totalhands * hand.bid
  132.             print(j,hand,"->",totalhands,totalhands * hand.bid)
  133.             totalhands -= 1
  134.  
  135.     print(sum)
  136.  
  137. def parttwo(hands):
  138.     '''
  139.    J are now wilds, so give the best hand possible, and worth 1 point
  140.    '''
  141.  
  142.     for hand in hands:
  143.         hand.set_parttwo()
  144.  
  145.     for hand in hands:
  146.         #obtain count of wild cards, "J" and unique cards in the hand
  147.         wilds = hand.cards.count("J")
  148.         uniq_cards = set(hand.cards)
  149.  
  150.  
  151.         #If hand is full of wilds, then it's just a 5 of a kind, and doesn't need treated differently
  152.         #idea is to find the wilds and change to the most occuring other card
  153.         #then pass to partone function to sort into the hand types and sum the values
  154.         if wilds:
  155.             if len(uniq_cards) == 1:
  156.                 continue
  157.  
  158.             #add the first card in the unique set to a variable then look for any other
  159.             #unique cards in the set
  160.             #card_list is a list of list of [count,uniq_card]
  161.             #allows for easy sorting to find the uniq_card with the highest count
  162.             card_list = [[1,hand.cards[0]]]
  163.             for card in hand.cards[1:]:
  164.                 for i in card_list:
  165.                     found = False
  166.                     if card in i:
  167.                         i[0] += 1
  168.                         found = True
  169.                 if not found:
  170.                     card_list.append([1,card])
  171.             #card_list is reverse order to have the highest uniq_card count first
  172.             #if highest card is "J", assign next highest to copy_card
  173.             card_list.sort(reverse=True)
  174.             if card_list[0][1] == "J":
  175.                 copy_card = card_list[1][1]
  176.             else:
  177.                 copy_card = card_list[0][1]
  178.            
  179.             #replace all wilds with highest non-"J" uniq_card
  180.             hand.cards = hand.cards.replace("J",copy_card)
  181.    
  182.     #call partone to determine hand type
  183.     partone(hands)
  184.  
  185. if __name__ == "__main__":
  186.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement