diff --git a/aoc2023/src/day01/python/solution.py b/aoc2023/src/day01/python/solution.py index ca7c692..9f98cc6 100644 --- a/aoc2023/src/day01/python/solution.py +++ b/aoc2023/src/day01/python/solution.py @@ -11,24 +11,81 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -def _find_first_digit(string: str) -> int: +_SPELLED_OUT_NUMBERS = ( + 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine') +_NUMBERS = tuple(str(i) for i in range(0, 10)) + + +def _find_first_digit(string: str, read_spelled: bool) -> int: """Returns the left-most digit in a string.""" - return next(int(char) for char in string if char.isdigit()) + if not read_spelled: + return next(int(char) for char in string if char.isdigit()) + + min_index = len(string) + found_index = len(string) + num_at_min_index = None + for number in _NUMBERS: + try: + found_index = string.index(number) + except ValueError: + # Number not found, ignore. + pass + if found_index < min_index: + num_at_min_index = int(number) + min_index = found_index + for i in range(len(_SPELLED_OUT_NUMBERS)): + try: + found_index = string.index(_SPELLED_OUT_NUMBERS[i]) + except ValueError: + # Number not found, ignore. + pass + if found_index < min_index: + num_at_min_index = int(i) + min_index = found_index + return num_at_min_index -def _find_last_digit(string: str) -> int: +def _find_last_digit(string: str, read_spelled: bool) -> int: """Returns the right-most digit in a string.""" - return next( - int(string[i]) for i in range(len(string) - 1, -1, -1) - if string[i].isdigit()) + if not read_spelled: + return next( + int(string[i]) for i in range(len(string) - 1, -1, -1) + if string[i].isdigit()) + max_index = -1 + found_index = -1 + num_at_max_index = None + for number in _NUMBERS: + try: + found_index = string.rfind(number) + except ValueError: + # Number not found, ignore. + pass + if found_index > max_index: + num_at_max_index = int(number) + max_index = found_index + for i in range(len(_SPELLED_OUT_NUMBERS)): + try: + found_index = string.rfind(_SPELLED_OUT_NUMBERS[i]) + except ValueError: + # Number not found, ignore. + pass + if found_index > max_index: + num_at_max_index = int(i) + max_index = found_index + return num_at_max_index -def _get_calibration_value(string: str) -> int: + +def _get_calibration_value(string: str, + read_spelled: bool) -> int: """Calculates calibration value of a string.""" - return _find_first_digit(string) * 10 + _find_last_digit(string) + + return (_find_first_digit(string, read_spelled) * 10 + + _find_last_digit(string, read_spelled)) -def sum_calibration_values(calibration_document: tuple[str, ...]) -> int: +def sum_calibration_values(calibration_document: tuple[str, ...], + read_spelled: bool = False) -> int: """Calculates sum of the calibration values. The calibration value can be found by combining the first digit and the last @@ -36,7 +93,11 @@ def sum_calibration_values(calibration_document: tuple[str, ...]) -> int: Args calibration_document A calibration document. + read_spelled Whether to consider spelled out numbers in the calibration + document lines. False by default. Returns Sum of all calibration values in the document. """ - return sum((_get_calibration_value(line) for line in calibration_document)) + return sum( + (_get_calibration_value(line, read_spelled) + for line in calibration_document)) diff --git a/aoc2023/test/day01/python/example2.txt b/aoc2023/test/day01/python/example2.txt new file mode 100644 index 0000000..4316a6b --- /dev/null +++ b/aoc2023/test/day01/python/example2.txt @@ -0,0 +1,7 @@ +two1nine +eightwothree +abcone2threexyz +xtwone3four +4nineeightseven2 +zoneight234 +7pqrstsixteen \ No newline at end of file diff --git a/aoc2023/test/day01/python/test_solution.py b/aoc2023/test/day01/python/test_solution.py index 38b8439..b6afd43 100644 --- a/aoc2023/test/day01/python/test_solution.py +++ b/aoc2023/test/day01/python/test_solution.py @@ -27,6 +27,15 @@ def test_part1_withExampleInput_sumsCalibrationValues(self): def test_part1_withInput_sumsCalibrationValues(self): self.assertEqual(53334, sum_calibration_values(self.input)) + def test_part2_withExampleInput_sumsCalibrationValues(self): + self.assertEqual(281, + sum_calibration_values(self.examples[1], + read_spelled=True)) + + def test_part2_withInput_sumsCalibrationValues(self): + self.assertEqual(52834, + sum_calibration_values(self.input, read_spelled=True)) + if __name__ == '__main__': unittest.main()