Day 6: Wait For It

 22nd December 2023 at 11:28pm

Part 1

Cute problem. It seemed very simple from the very beginning, and the examples even suggested a sort of symmetry (0 to 6 to 10 to 12 and repeating on reverse order). It took only a brief pen sketching, and I was confident enough on the code.

Tests

TEST_DATA = """
Time:      7  15   30
Distance:  9  40  200
"""


class Test202305_01(unittest.TestCase):
    def test_transform_input_into_game_data(self):
        games = get_game_data(TEST_DATA)
        self.assertEqual(
                games,
                [(7, 9), (15, 40), (30, 200)]
                )

    def test_get_list_of_all_game_possibilities(self):
        games = get_game_data(TEST_DATA)
        possibilities = get_all_game_possibilities(games[0])
        self.assertEqual(
            possibilities,
            [6, 10, 12, 12, 10, 6]
        )

    def test_get_list_of_all_winning_times_for_one_game(self):
        games = get_game_data(TEST_DATA)
        possibilities = get_winning_times(games[0])
        self.assertEqual(
            possibilities,
            [10, 12, 12, 10]
        )

    def test_get_list_of_all_winning_times_for_all_games(self):
        games = get_game_data(TEST_DATA)
        possibilities = get_winning_times_for_all_games(games)
        self.assertEqual(
            possibilities,
            [4, 8, 9]
        )

    def test_solve_part_one(self):
        self.assertEqual(
            solve_part_one(TEST_DATA),
            288
        )

Code

On get_distance_upon_release, I noticed the hint of a quadratic formula (time_elapsed would be squared). Some people on the AOC's subreddit pursued this method, which is, of course, a little more interesting. I went by bruteforcing anyway, but I do realise this would probably be the best course of action.

def get_game_data(data: str):
    nbr_lists = map(lambda line: list(map(int, line)),
                    map(lambda line: line.split(':')[1].split(),
                    data.strip().splitlines()))
    return list(zip(*nbr_lists))


def get_distance_upon_release(
        total_time: int,
        time_elapsed: int
        ) -> int:
    return time_elapsed * (total_time - time_elapsed)


def get_all_game_possibilities(
        game: list[int, int]
        ):
    total_time, distance = game
    return list(map(lambda elapsed: get_distance_upon_release(total_time, elapsed),
                    range(1, total_time)))


def get_winning_times(
        game: list[int, int]
        ):
    total_time, distance = game
    return list(filter(lambda elapsed: elapsed > distance,
                       get_all_game_possibilities(game)))


def get_winning_times_for_all_games(
        games: list
        ):
    return list(map(len, map(get_winning_times, games)))


def solve_part_one(
        data: str
        ) -> int:
    games = get_game_data(data)
    return reduce(lambda x, y: x * y,
                  get_winning_times_for_all_games(games))


with open('input') as f:
    TEST_DATA = f.read()
    print(solve_part_one(TEST_DATA))

Part 2

For Part 2, it was basically the same exercise — and in fact one of the very few cases where Part 2 entails less code than the first. Bruteforcing using the same algorithm takes a while (approximately 5 to 7 seconds on lardafada); still, not enough to make me implement the more sensible, mathematical solution.

Tests

TEST_DATA = """
Time:      7  15   30
Distance:  9  40  200
"""


class Test202305_02(unittest.TestCase):
    def test_transform_input_into_game_data(self):
        game = get_game_data(TEST_DATA)
        self.assertEqual(
                game,
                [71530, 940200]
                )

    def test_get_list_of_all_winning_times_for_one_game(self):
        game = get_game_data(TEST_DATA)
        possibilities = get_winning_times(game)
        self.assertEqual(
            len(possibilities),
            71503
        )

Code

from functools import reduce


def get_game_data(data: str):
    nbr_lists = map(lambda line: int("".join(line)),
                    map(lambda line: line.split(':')[1].split(),
                    data.strip().splitlines()))
    return list(nbr_lists)


def get_distance_upon_release(
        total_time: int,
        time_elapsed: int
        ) -> int:
    return time_elapsed * (total_time - time_elapsed)


def get_all_game_possibilities(
        game: list[int, int]
        ):
    total_time, distance = game
    return list(map(lambda elapsed: get_distance_upon_release(total_time, elapsed),
                    range(1, total_time)))


def get_winning_times(
        game: list[int, int]
        ):
    total_time, distance = game
    return list(filter(lambda elapsed: elapsed > distance,
                       get_all_game_possibilities(game)))


def solve_part_two(
        data: str
        ) -> int:
    game = get_game_data(data)
    return len(get_winning_times(game))


with open('input') as f:
    DATA = f.read()
    print(solve_part_two(DATA))