Guest User

AOC2020Day16inPython

a guest
Dec 16th, 2020
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.84 KB | None | 0 0
  1. import re
  2. import math
  3.  
  4. # Part 0
  5. # ------
  6. # Parsing the data
  7.  
  8. # "131,103,...,197" -> (131,103,...,197)
  9. def ticket_tuple(t): return tuple(map(int, t.split(",")))
  10.  
  11. # "price: 33-635 or 700-740" -> "price"
  12. def field_name(f): return f.split(':')[0]
  13. # "price: 33-635 or 700-740" -> set of valid values
  14. def field_range(f):
  15.     valid = set()
  16.     for a,b in re.findall("(\d+)-(\d+)",f):
  17.         valid |= set(range(int(a),int(b)+1))
  18.     return valid
  19.  
  20. # Read in the data and parse it
  21. # data[0] contains the fields
  22. # data[1] contains my ticket (with a header row)
  23. # data[2] contains all the other tickets (with a header row)
  24.  
  25. data =     [category.splitlines() for category in open("data16.txt").read().split("\n\n")]
  26. myticket = ticket_tuple(data[1][1])
  27. tickets  = [ticket_tuple(t) for t in data[2][1:]]
  28. valid    = {field_name(f): field_range(f) for f in data[0]}
  29.  
  30. # Part 1
  31. # ------
  32. # Find the sum of all the invalid values
  33. # Also filter the tickets, creating a valid_tickets set
  34.  
  35. invalid_sum, valid_tickets = 0, set()
  36. valid_tickets.add(myticket)
  37.  
  38. for ticket in tickets:
  39.     okay = True
  40.     for val in ticket:
  41.         if all(val not in valid[name] for name in valid):
  42.             invalid_sum += val
  43.             okay = False
  44.     if okay: valid_tickets.add(ticket)
  45.  
  46. print("Part 1:", invalid_sum)
  47.  
  48. # Part 2
  49. # ------
  50. # Determine which category maps to which index in the tickets
  51.  
  52. # First we generate 'to_allocate', a dictionary of the fields we need to allocate.
  53. # Each field is associated with a set of possible valid ticket indices
  54.  
  55. to_allocate = {f: set() for f in valid.keys()}
  56. for i in range(len(myticket)):
  57.     possibles = set(valid.keys())
  58.     for t in valid_tickets:
  59.         possibles = set(p for p in possibles if t[i] in valid[p])
  60.     for p in possibles:
  61.         to_allocate[p].add(i)
  62.  
  63. # We iterate through the list of unallocated fields, looking for ones which only
  64. # have one possible index. Once we've found one, we allocate it, and remove it from
  65. # the to_allocate dictionary, along with the index we've allocated.
  66. # This continues until we've paired together all the fields and indices.
  67.  
  68. # This is 'greedy matching' and will only work if backtracking isn't required,
  69. # in which case we'd need a more horrible algorithm - but luckily everything works!
  70.  
  71. allocated = {}
  72. while to_allocate:
  73.     for field in to_allocate:
  74.         if len(to_allocate[field]) == 1:
  75.             allocated[field] = to_allocate[field].pop()
  76.             to_allocate.pop(field)
  77.             for f in to_allocate: to_allocate[f].remove(allocated[field])
  78.             break
  79.  
  80. # 'allocated' is now our mapping from field name to ticket index
  81. # The actual calculation we need to do is to multiply together all the values
  82. # in fields which have 'departure' in the field name.
  83.  
  84. print("Part 2:",math.prod(myticket[i] for (f,i) in allocated.items() if "departure" in f))
Advertisement
Add Comment
Please, Sign In to add comment