Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """
- Advent of Code 2020 - Day 19: Monster Messages
- https://adventofcode.com/2020/day/19
- :license: GPLv3 --- Copyright (C) 2020 Olivier Pirson
- :author: Olivier Pirson --- http://www.opimedia.be/
- :version: December 19, 2020
- """
- import sys
- from typing import List, Optional, Tuple, Union
- import regex as re # type: ignore
- RULES: List[Union['RuleChar', 'RuleEmpty', 'RuleSeq', 'RuleUnion']] = []
- class RuleChar:
- def __init__(self, rule_id: int, char: str) -> None:
- assert len(char) == 1
- self._char = char
- self._id = rule_id
- def __repr__(self) -> str:
- return 'RuleChar({})'.format(self._char)
- def __str__(self) -> str:
- return self._char
- def regex(self) -> str:
- return self._char
- def reset(self) -> None:
- pass
- class RuleEmpty:
- def __init__(self, rule_id: int) -> None:
- self._id = rule_id
- def __repr__(self) -> str:
- return 'RuleEmpty'
- def __str__(self) -> str:
- return 'RuleEmpty'
- def regex(self) -> str: # pylint: disable=no-self-use
- assert False
- def reset(self) -> None:
- pass
- class RuleSeq:
- def __init__(self, rule_id: int, indices_str: str) -> None:
- assert indices_str
- self._indices = tuple(map(int, indices_str.split()))
- self._id = rule_id
- self._regex: Optional[str] = None
- def __repr__(self) -> str:
- return 'RuleSeq({})'.format(' '.join(map(str, self._indices)))
- def __str__(self) -> str:
- return ' '.join(map(str, self._indices))
- def regex(self) -> str:
- if self._regex is None:
- self._regex = '(?:{})'.format(
- ''.join(map(lambda i: RULES[i].regex(), self._indices)))
- return self._regex
- def reset(self) -> None:
- self._regex = None
- class RuleUnion:
- def __init__(self, rule_id: int, indices_str: str) -> None:
- assert indices_str
- seq = []
- for sub in indices_str.split(' | '):
- seq.append(RuleSeq(rule_id, sub))
- self._subrules = tuple(seq)
- self._id = rule_id
- self._regex: Optional[str] = None
- def __repr__(self) -> str:
- return ' | '.join(map(repr, self._subrules))
- def __str__(self) -> str:
- return ' | '.join(map(str, self._subrules))
- def regex(self) -> str:
- if self._regex is None:
- if self._id == 8: # ad hoc for the problem
- self._regex = '(?:{})+'.format(RULES[42].regex())
- elif self._id == 11: # ad hoc for the problem
- self._regex = '(?P<r11>{}(?P&r11)?{})'.format(
- RULES[42].regex(), RULES[31].regex())
- else:
- self._regex = '(?:{})'.format(
- '|'.join(map(lambda subrule: subrule.regex(),
- self._subrules)))
- return self._regex
- def reset(self) -> None:
- self._regex = None
- class Rule:
- def __init__(self, line: str) -> None:
- assert line
- rule_id_str, rule_str = line.split(': ')
- rule_id = int(rule_id_str)
- self._rule: Union[RuleChar, RuleSeq, RuleUnion]
- if rule_str[0] == '"':
- self._rule = RuleChar(rule_id, rule_str[1:-1])
- elif '|' not in rule_str:
- self._rule = RuleSeq(rule_id, rule_str)
- else:
- self._rule = RuleUnion(rule_id, rule_str)
- self._id = rule_id
- if rule_id >= len(RULES):
- RULES.extend([RuleEmpty(rule_id)] * (rule_id + 1 - len(RULES)))
- RULES[rule_id] = self # type: ignore
- def __repr__(self) -> str:
- return 'Rule {}: {}'.format(self._id, repr(self._rule))
- def __str__(self) -> str:
- return 'Rule {}: {}'.format(self._id, str(self._rule))
- def regex(self) -> str:
- return self._rule.regex()
- def reset(self) -> None:
- self._rule.reset()
- def check(messages: Tuple[str, ...]) -> int:
- regex_str = RULES[0].regex()
- regex = re.compile(regex_str)
- nb = 0
- for message in messages:
- if regex.fullmatch(message):
- nb += 1
- return nb
- def log(*params) -> None:
- sys.stdout.flush()
- print(*params, file=sys.stderr)
- sys.stderr.flush()
- def main() -> None:
- for line in sys.stdin:
- line = line.rstrip()
- if not line:
- break
- Rule(line)
- messages = tuple(map(str.rstrip, sys.stdin.readlines()))
- # Part 1
- nb = check(messages)
- print(nb)
- sys.stdout.flush()
- # Part 2
- if len(RULES) >= 11:
- log('Old rule', RULES[8])
- log('Old rule', RULES[11])
- RULES[8] = Rule('8: 42 | 42 8') # type: ignore
- RULES[11] = Rule('11: 42 31 | 42 11 31') # type: ignore
- log('New rule', RULES[8])
- log('New rule', RULES[11])
- for rule in RULES:
- rule.reset()
- nb = check(messages)
- print(nb)
- if __name__ == '__main__':
- main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement