Fixup day18 and add second part
[advent-of-code-2020.git] / day19 / monstermessages2.py
1 #!/usr/bin/python3
2
3 import sys
4 import re
5
6 filename="p2_12.txt"
7 if len(sys.argv) > 1:
8     filename=sys.argv[1]
9
10 in_rules=True
11 rules={}
12 data=[]
13
14 for line in open(filename, "r"):
15     line = line.rstrip()
16     if line == "":
17         in_rules=False
18         continue
19
20     if in_rules:
21         (rule_number,rule) = line.split(":")
22         rule_number=int(rule_number)
23         rule=rule.strip()
24         rules[rule_number] = [s.strip() for s in rule.split("|")]
25     else:
26         data.append(line)
27
28 def get_rule_regex(rulenumber):
29     regex=""
30     first=False
31     for rule in rules[rulenumber]:
32         if rule[0] != '"':
33             # not a character, so we're going to go down the wishing well
34             for number in rule.split(" "):
35                 number=int(number)
36                 regex+=get_rule_regex(number)
37             regex+="|"
38         else:
39             regex+=rule[1:-1]
40     if regex[-1] == "|":
41         regex=regex[0:-1]
42     if len(rules[rulenumber]) > 1 or len(rules[rulenumber][0]) > 3:
43         regex="("+regex+")"
44
45     return regex
46
47 # because we're doing insane things, lets build the regex for rules[8] first, which is literally 42 multiple times
48 #rules[8]=["42", "42 8"]
49 #rules[11]=["42 31", "42 11 31"]
50
51 rules[8]=['"((' + get_rule_regex(42) + ')(?P<rule8>(' + get_rule_regex(42) + ')*))"']
52 rules[11]=['"((' + get_rule_regex(42) + '))(?P<rule11>.*)((' + get_rule_regex(31) + '))"']
53
54 # ok - so all the rules are in, lets build a huge regex that gets all the rules
55 pattern="^"+get_rule_regex(0)+"$"
56
57 match_count=0
58
59 for line in data:
60     matches = re.match(pattern, line)
61     if matches:
62         if matches.group('rule11'):
63             part=matches.group('rule8') + matches.group('rule11')
64             rule_31_matches=0
65             rule_42_matches=0
66             while len(part) > 0:
67                 # remove the end, and keep count, we need at least that many rule 42s to
68                 # be removed from the front
69                 remove_pattern=get_rule_regex(31)+"$"
70                 new_part=re.sub(remove_pattern, '', part)
71                 if len(new_part) == len(part):
72                     # try removing a rule 42 from the start
73                     remove_pattern="^" + get_rule_regex(42)
74                     new_part=re.sub(remove_pattern, '', part)
75                     if len(new_part) == len(part):
76                         # if nothing got removed, and we're not 0 length, we're not a match
77                         break
78                     else:
79                         part=new_part
80                         rule_42_matches+=1
81                 else:
82                     rule_31_matches+=1
83                     part=new_part
84             else:
85                 if rule_42_matches >= rule_31_matches:
86                     match_count+=1
87         else:
88             match_count+=1
89
90 print("There were {} matches in total".format(match_count))