From 838d06100b22de8e74bdb2a8d3e8d25d9f6c1559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Engl=C3=B6f=20Ytterstr=C3=B6m?= Date: Wed, 25 Oct 2023 23:27:51 +0200 Subject: [PATCH] Solve 2015:11 "Corporate Policy" I brainfarted and had a hard time trying to understand the instructions. > Incrementing is just like counting with numbers: xx, xy, xz, ya, yb, and so on. Increase the rightmost letter one step; if it was z, it wraps around to a, and repeat with the next letter to the left until one doesn't wrap around. I only managed to understand it by looking at solutions on the subreddit, figuring out the correct behavior: az -> ba, azzz -> baaa, azzzzz -> baaaaa etc. I also sped up the test case containing `ghi` as initial password, by looking for the leftmost invalid I, L or O and increase it, replacing all following chars with `a`. ghijklmn -> ghjaaaaa. --- 2015-python/solutions/day_11.py | 68 ++++++++++++++++++++++++++++++++ 2015-python/tests/test_day_11.py | 35 ++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 2015-python/solutions/day_11.py create mode 100644 2015-python/tests/test_day_11.py diff --git a/2015-python/solutions/day_11.py b/2015-python/solutions/day_11.py new file mode 100644 index 0000000..8bb0d8f --- /dev/null +++ b/2015-python/solutions/day_11.py @@ -0,0 +1,68 @@ +import re +from solutions import BaseSolution + + +class Solution(BaseSolution): + input_file = "11.txt" + + def __str__(self): + return "Day 11: Corporate Policy" + + def is_valid(self, suggestion): + if "i" in suggestion or "l" in suggestion or "o" in suggestion: + return False + if ( + len( + set( + map( + lambda x: x[0], + filter(lambda x: x[0] == x[1], zip(suggestion, suggestion[1:])), + ) + ) + ) + < 2 + ): + return False + + def seq(abc): + a, b, c = abc + return ord(b) - ord(a) == 1 and ord(c) - ord(b) == 1 + + return any(map(seq, zip(suggestion, suggestion[1:], suggestion[2:]))) + + def parse_input(self, data): + return data + + def solve(self, password): + def tick(p): + chars = list(p)[::-1] + i = 0 + for c in chars: + if c == "z": + chars[i] = "a" + else: + chars[i] = chr(ord(chars[i]) + 1) + break + i += 1 + return "".join(chars[::-1]) + + unallowed = re.compile(r"i|l|o") + fastforward = re.search(unallowed, password) + if fastforward: + password = ( + password[: fastforward.start()] + + chr(ord(password[fastforward.start()]) + 1) + ).ljust(len(password), "a") + + while True: + password = tick(password) + if self.is_valid(password): + return password + + def solve_again(self, puzzle_input): + return self.solve(self.solve(puzzle_input)) + + +if __name__ == "__main__": + solution = Solution() + solution.show_results() diff --git a/2015-python/tests/test_day_11.py b/2015-python/tests/test_day_11.py new file mode 100644 index 0000000..b1d148b --- /dev/null +++ b/2015-python/tests/test_day_11.py @@ -0,0 +1,35 @@ +import unittest + +from solutions.day_11 import Solution + + +class Day11TestCase(unittest.TestCase): + def setUp(self): + self.solution = Solution() + self.puzzle_input = self.solution.parse_input( + """ + + """ + ) + + def test_parse_puzzle_input(self): + data = """ + + """.strip() + assert self.solution.parse_input(data) == "" + + def test_validate_passwords(self): + assert not self.solution.is_valid("hijklmmn") + assert not self.solution.is_valid("abbceffg") + assert not self.solution.is_valid("abbcegjk") + + def test_solve_first_part(self): + assert self.solution.solve("abcdefgh") == "abcdffaa" + assert self.solution.solve("ghijklmn") == "ghjaabcc" + + # def test_solve_second_part(self): + # assert self.solution.solve_again(self.puzzle_input) == True + + +if __name__ == "__main__": + unittest.main()