148 lines
4.2 KiB
Python
Executable File
148 lines
4.2 KiB
Python
Executable File
#!/usr/bin/python3
|
|
"""
|
|
Jour 12 du défi Advent Of Code pour l'année 2023
|
|
"""
|
|
import os
|
|
from functools import cache, wraps
|
|
from time import time
|
|
|
|
def read_sample():
|
|
"""récupère les entrées depuis le fichier texte correspondant"""
|
|
filename = os.path.join(os.path.dirname(__file__ ), "inputs", "day12.txt")
|
|
with open(filename, 'r') as f:
|
|
sample = f.read().split('\n')
|
|
sample = [ i for i in sample if i != '' ]
|
|
return sample
|
|
|
|
|
|
def parse_sample(sample):
|
|
springs = []
|
|
counts = []
|
|
for i in range(len(sample)):
|
|
springs.append(sample[i].split(" ")[0])
|
|
counts.append([int(i) for i in sample[i].split(" ")[1].split(",")])
|
|
return springs, counts
|
|
|
|
|
|
def timing(f):
|
|
@wraps(f)
|
|
def wrap(*args, **kw):
|
|
ts = time()
|
|
result = f(*args, **kw)
|
|
te = time()
|
|
print('> %s: %2.4f sec' % \
|
|
(f.__name__, te-ts))
|
|
return result
|
|
return wrap
|
|
|
|
|
|
def nb_poss(spring, count):
|
|
def check_new_elem(elem, size, count):
|
|
if size > len(count):
|
|
return False
|
|
if elem != count[size-1]:
|
|
return False
|
|
return True
|
|
|
|
@cache
|
|
def nb_poss_rec(actual_count_size, cur, pos, first):
|
|
"""
|
|
actual_count_size: already added elements
|
|
cur: current number of # in the spring pos
|
|
pos: current position in the spring
|
|
first: first elem of the upcoming spring
|
|
"""
|
|
# While loop for first elem
|
|
if pos < len(spring) and first != '?':
|
|
if first == '.':
|
|
if cur > 0:
|
|
actual_count_size += 1
|
|
if not check_new_elem(cur, actual_count_size, count):
|
|
return 0
|
|
cur = 0
|
|
elif first == '#':
|
|
cur += 1
|
|
pos += 1
|
|
|
|
# Exactly the same loop for the other elems
|
|
while pos < len(spring) and spring[pos] != '?':
|
|
if spring[pos] == '.':
|
|
if cur > 0:
|
|
actual_count_size += 1
|
|
if not check_new_elem(cur, actual_count_size, count):
|
|
return 0
|
|
cur = 0
|
|
elif spring[pos] == '#':
|
|
cur += 1
|
|
pos += 1
|
|
|
|
# spring ends with # ?
|
|
if pos == len(spring) and cur > 0:
|
|
actual_count_size += 1
|
|
if not check_new_elem(cur, actual_count_size, count):
|
|
return 0
|
|
|
|
# End of spring ?
|
|
if pos >= len(spring):
|
|
if len(count) == actual_count_size:
|
|
return 1
|
|
return 0
|
|
|
|
# Debugging
|
|
if spring[pos] != '?':
|
|
print("weird", spring, count, actual_count_size, pos, spring[pos], len(spring))
|
|
return
|
|
|
|
# Recursive calls
|
|
if spring[pos] == '?':
|
|
if cur > 0:
|
|
if len(count) > actual_count_size:
|
|
if cur == count[actual_count_size]:
|
|
return nb_poss_rec(actual_count_size, cur, pos, '.') # break the #### with a .
|
|
elif cur < count[actual_count_size]:
|
|
return nb_poss_rec(actual_count_size, cur, pos, '#') # continue with #
|
|
else:
|
|
return 0 # not possible
|
|
return nb_poss_rec(actual_count_size, cur, pos, '.') + nb_poss_rec(actual_count_size, cur, pos, '#')
|
|
|
|
return nb_poss_rec(0, 0, 0, spring[0])
|
|
|
|
|
|
def unfold(springs, counts):
|
|
for i in range(len(springs)):
|
|
springs[i] = '?'.join([springs[i]]*5)
|
|
counts[i] = counts[i]*5
|
|
|
|
|
|
@timing
|
|
def part1(sample):
|
|
"""Partie 1 du défi"""
|
|
springs, counts = parse_sample(sample)
|
|
possibilities = []
|
|
for i in range(len(springs)):
|
|
possibilities.append(nb_poss(springs[i], counts[i]))
|
|
|
|
return sum(possibilities)
|
|
|
|
|
|
@timing
|
|
def part2(sample):
|
|
"""Partie 2 du défi"""
|
|
springs, counts = parse_sample(sample)
|
|
unfold(springs, counts)
|
|
|
|
possibilities = []
|
|
for i in range(len(springs)):
|
|
possibilities.append(nb_poss(springs[i], counts[i]))
|
|
|
|
return sum(possibilities)
|
|
|
|
|
|
def main():
|
|
"""Fonction principale"""
|
|
sample = read_sample()
|
|
print(f"part1: {part1(sample)}")
|
|
print(f"part2: {part2(sample)}")
|
|
|
|
if __name__ == "__main__":
|
|
main() |