Coverage for Day7 / part2.py: 77%

41 statements  

« 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 -*- 

3 

4""" 

5Advent Of Code 2025 

6=================== 

7Day : 7 

8Part : 2 

9 

10Ce script simule la propagation de multiples faisceaux lumineux depuis un 

11point d’entrée 'S'. Chaque faisceau descend ligne par ligne. 

12 

13Lorsqu’un faisceau rencontre un splitter '^', il : 

14 - se divise en deux faisceaux, 

15 - le nombre de faisceaux se multiplie donc. 

16 

17L’objectif est de calculer le **nombre total de faisceaux arrivant en bas 

18de la carte**, après toutes les divisions. 

19 

20La simulation conserve pour chaque colonne le nombre de faisceaux actifs, 

21et met à jour ces comptages à chaque ligne. 

22 

23Le résultat final correspond à la somme des faisceaux restants. 

24 

25.. codeauthor:: Alexandre Condette <alexandre.condette@wanadoo.fr> 

26""" 

27 

28# %% ======================================================================== 

29# Import  

30from collections import defaultdict 

31 

32# =========================================================================== 

33 

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é. 

39 

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] 

47 

48# =========================================================================== 

49 

50# %% ======================================================================== 

51# Résolution 

52def solve(data: list) -> int: 

53 """ 

54 Simule la descente des faisceaux depuis 'S' et compte toutes les branches. 

55 

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}. 

59 

60 :param data: lignes décrivant la carte 

61 :return: nombre total de faisceaux en bas de la carte 

62 """ 

63 

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 

67 

68 height = len(rows) 

69 width = max(len(r) for r in rows) 

70 grid = [r.ljust(width, " ") for r in rows] 

71 

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 

79 

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") 

82 

83 # current[col] = nombre de faisceaux actifs dans la colonne 

84 current = defaultdict(int) 

85 current[start_col] = 1 

86 

87 # Propagation ligne par ligne 

88 for r in range(start_row + 1, height): 

89 next_state = defaultdict(int) 

90 

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 

96 

97 ch = grid[r][col] 

98 

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 

105 

106 else: 

107 # Le faisceau continue droit 

108 next_state[col] += count 

109 

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 

114 

115 current = next_state 

116 

117 # Total des faisceaux restants 

118 return sum(current.values()) 

119 

120# =========================================================================== 

121 

122# %% 

123if __name__ == "__main__": 

124 RESULT = solve(get_input(7, False)) 

125 

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")