def main(): cube = Cube(read_full_text(2020, 17), 4) for _ in range(6): cube.simulate_cycle() print(cube.count_active())
def main(): text = read_full_text(2020, 19) [rules, messages] = text.split("\n\n") rules = rules.splitlines() # Need to slightly change formatting so precedence rules work. # Space cannot be both an operator and space - it just doesn't make sense, # so remove spaces around |, which are just for display. rules = { int(index): rule.replace(" | ", "|").strip() for [index, rule] in [rule.split(":") for rule in rules] } messages = messages.splitlines() lexer = RuleLexer() parser = RuleParser() for rule in rules: rules[rule] = parser.parse(lexer.tokenize(rules[rule])) regex = re.compile("^" + generate_regex(rules, 0) + "$") count = 0 for message in messages: if regex.match(message): count += 1 print(count)
def main(): text = read_full_text(2020, 16) fields = get_fields(text) nearby_tickets = get_tickets("nearby", text) your_ticket = get_tickets("your", text)[0] invalid_values = [] all_fields = set(chain.from_iterable(fields.values())) valid_tickets = [] for ticket in nearby_tickets: if all([value in all_fields for value in ticket]): valid_tickets.append(ticket) # List of each column of the CSV. column_values = list(zip(*valid_tickets)) # Dictionary of field name to potential indices. field_indices = { field: set(range(len(valid_tickets[0]))) for field in fields.keys() } # Remove column numbers for each field that does not match the ranges. for i in range(len(column_values)): for field, range_ in fields.items(): values_in_range = [ value for value in column_values[i] if value in range_ ] if len(values_in_range) != len(column_values[i]): field_indices[field].remove(i) # Whiddle down indices by setting an int for any field that has only one possible index, # then remove that index from all other fields, since it can only be used once. # # Repeat until all fields have been resolved. while True: for field, values in field_indices.items(): if isinstance(values, set) and len(values) == 1: field_indices[field] = list(values)[0] for values_ in field_indices.values(): if isinstance(values_, set) and field_indices[field] in values_: values_.remove(field_indices[field]) if all(isinstance(_values, int) for _values in field_indices.values()): break print( reduce( mul, [ your_ticket[index] for field, index in field_indices.items() if field.startswith("departure") ], ))
def main(): text = read_full_text(2020, 19) [rules, messages] = text.split("\n\n") rules = rules.splitlines() # Need to slightly change formatting so precedence rules work. # Space cannot be both an operator and space - it just doesn't make sense, # so remove spaces around |, which are just for display. rules = { int(index): rule.replace(" | ", "|").strip() for [index, rule] in [rule.split(":") for rule in rules] } messages = messages.splitlines() lexer = RuleLexer() parser = RuleParser() for rule in rules: rules[rule] = parser.parse(lexer.tokenize(rules[rule])) rule_res = {} for rule in sorted(rules.keys()): if rule == 0 or rule == 8 or rule == 11: continue rule_res[rule] = generate_regex(rules, rule) # Pattern 8 is just pattern 42 one or more times rule_res[8] = f"(?:{rule_res[42]}+)" # Pattern 11 needs to specifically use a recursive regex rule_res[ 11] = f"(?:({rule_res[42]}{rule_res[31]}|{rule_res[42]}(?-1){rule_res[31]}))" # Now that we have patterns 8 and 11 we can build pattern 0 rule_res[0] = f"^(?:{rule_res[8]}{rule_res[11]})$" count = 0 for message in messages: # Need to use perl because Python does not have recursive regex # (could use the regex module but don't feel like it) proc = subprocess.run( f"perl -e 'print \"{message}\" =~ /{rule_res[0]}/ ? 1 : 0'", capture_output=True, shell=True, ) if int(proc.stdout) == 1: count += 1 print(count)
def main(): text = read_full_text(2020, 16) fields = get_fields(text) nearby_tickets = get_tickets("nearby", text) your_tickets = get_tickets("your", text) invalid_values = [] all_values = list(chain.from_iterable(nearby_tickets)) all_fields = set(chain.from_iterable(chain.from_iterable(fields.values()))) for value in all_values: if value not in all_fields: invalid_values.append(value) print(sum(invalid_values))
def main(): text = read_full_text(2020, 20) tiles = { int(id_): Tile(tile) for id_, tile in re.findall(r"Tile (\d+):\n([\.#\n]+)", text) } matches = defaultdict(lambda: 0) for tile_a, tile_b in product(tiles, tiles): if tile_a == tile_b: continue matches[tile_a] += len(tiles[tile_a].match_tile(tiles[tile_b])) print(reduce(mul, (id_ for id_, matches in matches.items() if matches == 2)))
def main(): text = read_full_text(2020, 20) tiles = { int(id_): Tile(tile) for id_, tile in re.findall(r"Tile (\d+):\n([\.#\n]+)", text) } matches = defaultdict(lambda: 0) for tile_a, tile_b in product(tiles, tiles): if tile_a == tile_b: continue matches[tile_a] += len(tiles[tile_a].match_tile(tiles[tile_b])) corners = [tiles[id_] for id_, matches in matches.items() if matches == 2] corners[0].extend_corner(tiles.values())
def get_passenger_groups(): return (group.strip().split("\n") for group in read_full_text(2020, 6).split("\n\n"))
def get_passport_chunks() -> Iterator[str]: """Read the input text into individual passport chunks.""" text = read_full_text(2020, 4) return (re.sub(r"\s+", " ", chunk) for chunk in text.split("\n\n"))