From b5c262097ad6662c1a1975b0008677742c25b3ef Mon Sep 17 00:00:00 2001 From: augustin64 Date: Tue, 12 Dec 2023 09:49:16 +0100 Subject: [PATCH] Add 2023 day 12 ':] --- 2023/day12.py | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100755 2023/day12.py diff --git a/2023/day12.py b/2023/day12.py new file mode 100755 index 0000000..d8743cc --- /dev/null +++ b/2023/day12.py @@ -0,0 +1,148 @@ +#!/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() \ No newline at end of file