Coverage for Day7 / part2.py: 77%
41 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-12 09:47 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-12 09:47 +0000
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-
4"""
5Advent Of Code 2025
6===================
7Day : 7
8Part : 2
10Ce script simule la propagation de multiples faisceaux lumineux depuis un
11point d’entrée 'S'. Chaque faisceau descend ligne par ligne.
13Lorsqu’un faisceau rencontre un splitter '^', il :
14 - se divise en deux faisceaux,
15 - le nombre de faisceaux se multiplie donc.
17L’objectif est de calculer le **nombre total de faisceaux arrivant en bas
18de la carte**, après toutes les divisions.
20La simulation conserve pour chaque colonne le nombre de faisceaux actifs,
21et met à jour ces comptages à chaque ligne.
23Le résultat final correspond à la somme des faisceaux restants.
25.. codeauthor:: Alexandre Condette <alexandre.condette@wanadoo.fr>
26"""
28# %% ========================================================================
29# Import
30from collections import defaultdict
32# ===========================================================================
34# %% ========================================================================
35# Input data
36def get_input(day: int = 1, example: bool = False) -> list:
37 """
38 Lit le fichier d'entrée pour le jour donné.
40 :param day: numéro du jour AoC
41 :param example: True pour example.txt, False pour input.txt
42 :return: liste des lignes lues
43 """
44 filename = 'example.txt' if example else 'input.txt'
45 with open(f"./Day{day}/{filename}", 'r', encoding='utf-8') as f:
46 return [line.rstrip('\n') for line in f]
48# ===========================================================================
50# %% ========================================================================
51# Résolution
52def solve(data: list) -> int:
53 """
54 Simule la descente des faisceaux depuis 'S' et compte toutes les branches.
56 Chaque faisceau suit la colonne jusqu'à rencontrer '^'.
57 Un splitter génère deux nouvelles branches.
58 Les branches sont comptabilisées via un dictionnaire {colonne: nombre}.
60 :param data: lignes décrivant la carte
61 :return: nombre total de faisceaux en bas de la carte
62 """
64 rows = [r.rstrip("\n") for r in data]
65 if not rows: 65 ↛ 66line 65 didn't jump to line 66 because the condition on line 65 was never true
66 return 0
68 height = len(rows)
69 width = max(len(r) for r in rows)
70 grid = [r.ljust(width, " ") for r in rows]
72 # Recherche du point de départ 'S'
73 start_row = start_col = None
74 for i, row in enumerate(grid): 74 ↛ 80line 74 didn't jump to line 80 because the loop on line 74 didn't complete
75 if "S" in row: 75 ↛ 74line 75 didn't jump to line 74 because the condition on line 75 was always true
76 start_row = i
77 start_col = row.index("S")
78 break
80 if start_row is None: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true
81 raise ValueError("Point d'entrée 'S' introuvable dans l'input")
83 # current[col] = nombre de faisceaux actifs dans la colonne
84 current = defaultdict(int)
85 current[start_col] = 1
87 # Propagation ligne par ligne
88 for r in range(start_row + 1, height):
89 next_state = defaultdict(int)
91 for col, count in current.items():
92 if count == 0: 92 ↛ 93line 92 didn't jump to line 93 because the condition on line 92 was never true
93 continue
94 if col < 0 or col >= width: 94 ↛ 95line 94 didn't jump to line 95 because the condition on line 94 was never true
95 continue
97 ch = grid[r][col]
99 # Splitter : deux branches
100 if ch == "^":
101 if col - 1 >= 0: 101 ↛ 103line 101 didn't jump to line 103 because the condition on line 101 was always true
102 next_state[col - 1] += count
103 if col + 1 < width: 103 ↛ 91line 103 didn't jump to line 91 because the condition on line 103 was always true
104 next_state[col + 1] += count
106 else:
107 # Le faisceau continue droit
108 next_state[col] += count
110 # Si plus aucun faisceau actif → fin anticipée
111 if not next_state: 111 ↛ 112line 111 didn't jump to line 112 because the condition on line 111 was never true
112 current = next_state
113 break
115 current = next_state
117 # Total des faisceaux restants
118 return sum(current.values())
120# ===========================================================================
122# %%
123if __name__ == "__main__":
124 RESULT = solve(get_input(7, False))
126 print("\n" + "═" * 60)
127 print(" 🔐 Advent of Code 2025 — Day 7 | Part 2".center(60))
128 print("═" * 60)
129 print(f"Résultat : \033[96m{RESULT}\033[0m")
130 print("═" * 60 + "\n")