Day 9: Mirage Maintenance

 29th December 2023 at 11:04am

Part 1

I put off solving the exercise right away, but this was actually one of the easiest exercises so far. I might have been lucky to get the approach right without any major issues. My approach to solving these exercises — being test-driven – leaves very little room for error, although I end up producing much more code than needed.

Halfway through solving I realised there was no need to completely follow up until the array of zeroes; getting to the constant array was enough.

Tests

TEST_DATA = """
0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45
"""


class Test20230901(unittest.TestCase):
    def test_can_get_data_from_input(self):
        self.assertEqual(
            get_data_from_input(TEST_DATA),
            [[0, 3, 6, 9, 12, 15],
             [1, 3, 6, 10, 15, 21],
             [10, 13, 16, 21, 30, 45]]
        )

    def test_can_get_sequence_of_differences(self):
        lines = get_data_from_input(TEST_DATA)
        self.assertEqual(
            get_sequence_of_differences(lines[0]),
            [3, 3, 3, 3, 3]
        )

    def test_can_get_successive_sequences_of_differences(self):
        lines = get_data_from_input(TEST_DATA)
        self.assertEqual(
            get_successive_sequences_of_differences(
                [lines[0]]),
            [
                lines[0],
                [3, 3, 3, 3, 3],
                [0, 0, 0, 0]
                ]
        )
        self.assertEqual(
            get_successive_sequences_of_differences(
                [lines[1]]),
            [
                lines[1],
                [2, 3, 4, 5, 6],
                [1, 1, 1, 1],
                [0, 0, 0]
                ]
        )

    def test_can_extrapolate_one_value(self):
        lines = get_data_from_input(TEST_DATA)
        all_sequences = get_successive_sequences_of_differences(
            [lines[0]]
            )
        self.assertEqual(
            extrapolate_value_on_sequence(
                all_sequences[-3],
                # gets constant numbers list
                all_sequences[-2]
                ),
            [0, 3, 6, 9, 12, 15, 18]
            )

    def test_can_extrapolate_all_values_of_one_sequence(self):
        lines = get_data_from_input(TEST_DATA)
        results = [
                [0, 3, 6, 9, 12, 15, 18],
                [1, 3, 6, 10, 15, 21, 28],
                [10, 13, 16, 21, 30, 45, 68]
                ]
        for i, line in enumerate(lines):
            all_sequences = get_successive_sequences_of_differences([line])
            self.assertEqual(
                get_new_value_on_main_sequence(
                    all_sequences[:-1],
                    all_sequences[-1]
                ),
                results[i]
            )

    def test_can_extrapolate_all_values_of_all_sequences(self):
        lines = get_data_from_input(TEST_DATA)
        self.assertEqual(
                get_new_values_on_all_main_sequences(lines, []),
                [
                    [0, 3, 6, 9, 12, 15, 18],
                    [1, 3, 6, 10, 15, 21, 28],
                    [10, 13, 16, 21, 30, 45, 68]
                    ]
                )

    def test_can_get_sum_of_new_values(self):
        lines = get_data_from_input(TEST_DATA)
        self.assertEqual(
            get_sum_of_new_values_on_all_main_sequences(lines),
            114
        )

Code

def get_data_from_input(data_input: str):
    return list(map(lambda x: list(map(int, x.split())),
                    data_input.strip().splitlines()))


def get_sequence_of_differences(numbers: list[int]):
    return list(map(lambda x: x[1] - x[0],
                    zip(numbers, numbers[1:])))


def get_successive_sequences_of_differences(
        past: list[list[int]],
        ) -> list[list[int]]:
    if set(past[-1]) == {0}:
        return past
    return get_successive_sequences_of_differences(
            past + [get_sequence_of_differences(past[-1])]
            )


def extrapolate_value_on_sequence(
        sequence: list[int],
        extrapolate_from: list[int],
        ) -> list[int]:
    return sequence + [sequence[-1] + extrapolate_from[-1]]


def get_new_value_on_main_sequence(
        old_sequences: list[list[int]],
        extrapolate_from: list[int],
        ) -> list[int]:
    if len(old_sequences) == 0:
        return extrapolate_from
    return get_new_value_on_main_sequence(
            old_sequences[:-1],
            extrapolate_value_on_sequence(
                old_sequences[-1],
                extrapolate_from
                )
            )


def get_new_values_on_all_main_sequences(
        sequences: list[list[int]],
        new_sequences: list[list[int]]
        ) -> list[list[int]]:
    if sequences == []:
        return new_sequences
    all_extrapolated_sequences = (
            get_successive_sequences_of_differences([sequences[0]]))
    return get_new_values_on_all_main_sequences(
            sequences[1:],
            new_sequences + [
                get_new_value_on_main_sequence(
                    all_extrapolated_sequences,
                    all_extrapolated_sequences[-1]
                )
            ]
    )


def get_sum_of_new_values_on_all_main_sequences(
        sequences: list[list[int]]
        ) -> int:
    return sum(map(lambda x: x[-1],
                   get_new_values_on_all_main_sequences(
                       sequences,
                       [])))


with open("input") as f:
    print(get_sum_of_new_values_on_all_main_sequences(
        get_data_from_input(f.read())))

Part 2

Long before I gave much thought to Part 2, I wondered whether it would be just a matter of inverting the lines. And since the result immediately matched the assignment example, I submitted my answer right away. And it worked. So I ended up doing just a small test for inverting the input lines, and nothing more, really. What a nice Part 2!

Code

def get_data_from_input(data_input: str):
    return list(map(lambda x: list(map(int, x.split()))[::-1],
        data_input.strip().splitlines()))
				
...