diff --git a/Day12/README.md b/Day12/README.md index d2d22ed..ad46842 100644 --- a/Day12/README.md +++ b/Day12/README.md @@ -72,3 +72,32 @@ In this example, the number of possible arrangements for each row is: Adding all of the possible arrangement counts together produces a total of 21 arrangements. For each row, count all of the different arrangements of operational and broken springs that meet the given criteria. What is the sum of those counts? + +## Part Two +As you look out at the field of springs, you feel like there are way more springs than the condition records list. When you examine the records, you discover that they were actually folded up this whole time! + +To unfold the records, on each row, replace the list of spring conditions with five copies of itself (separated by ?) and replace the list of contiguous groups of damaged springs with five copies of itself (separated by ,). + +So, this row: +``` +.# 1 +``` +Would become: +``` +.#?.#?.#?.#?.# 1,1,1,1,1 +``` +The first line of the above example would become: +``` +???.###????.###????.###????.###????.### 1,1,3,1,1,3,1,1,3,1,1,3,1,1,3 +``` +In the above example, after unfolding, the number of possible arrangements for some rows is now much larger: +- ???.### 1,1,3 - 1 arrangement +- .??..??...?##. 1,1,3 - 16384 arrangements +- ?#?#?#?#?#?#?#? 1,3,1,6 - 1 arrangement +- ????.#...#... 4,1,1 - 16 arrangements +- ????.######..#####. 1,6,5 - 2500 arrangements +- ?###???????? 3,2,1 - 506250 arrangements + +After unfolding, adding all of the possible arrangement counts together produces 525152. + +Unfold your condition records; what is the new sum of possible arrangement counts? \ No newline at end of file diff --git a/Day12/python/solution1.py b/Day12/python/solution1.py index 0b4e485..25349ed 100644 --- a/Day12/python/solution1.py +++ b/Day12/python/solution1.py @@ -5,12 +5,13 @@ def parse_line(line): try: parts = line.strip().split() states = parts[0] - group_sizes = [int(x) for x in parts[1].split(',')] + group_sizes = [int(x) for x in parts[1].split(",")] return states, group_sizes except Exception as e: print(f"Error parsing line '{line}': {e}") raise + def is_valid_configuration(states, group_sizes): """ Check if a given configuration of springs is valid based on group sizes. @@ -18,12 +19,12 @@ def is_valid_configuration(states, group_sizes): try: i, size_index = 0, 0 while i < len(states): - if states[i] == '#': + if states[i] == "#": if size_index >= len(group_sizes): return False # More broken springs than groups count = 0 - while i < len(states) and states[i] == '#': + while i < len(states) and states[i] == "#": count += 1 i += 1 @@ -38,6 +39,7 @@ def is_valid_configuration(states, group_sizes): print(f"Error in is_valid_configuration: {e}") raise + def count_configurations(states, group_sizes, index=0, memo=None): """ Count the number of valid configurations using backtracking and memoization. @@ -53,26 +55,27 @@ def count_configurations(states, group_sizes, index=0, memo=None): memo[key] = 1 if is_valid_configuration(states, group_sizes) else 0 return memo[key] - if states[index] != '?': + if states[index] != "?": memo[key] = count_configurations(states, group_sizes, index + 1, memo) return memo[key] count = 0 - for state in ['.', '#']: + for state in [".", "#"]: states[index] = state count += count_configurations(states, group_sizes, index + 1, memo) - states[index] = '?' + states[index] = "?" memo[key] = count return count + def solve_puzzle(file_path): """ Solve the puzzle for each line in the file and return the total count of configurations. """ try: total_count = 0 - with open(file_path, 'r') as file: + with open(file_path, "r") as file: for line in file: states, group_sizes = parse_line(line) count = count_configurations(list(states), group_sizes) @@ -83,6 +86,7 @@ def solve_puzzle(file_path): print(f"Error in solve_puzzle: {e}") raise + def test(): """ Test the solution with a test file. @@ -93,6 +97,7 @@ def test(): assert test_result == 21, f"Test failed, expected 21, got {test_result}" print("Test passed successfully.") + def main(): """ Main function to run the test and then solve the puzzle. @@ -105,5 +110,6 @@ def main(): print(f"Error in main: {e}") raise + if __name__ == "__main__": main() diff --git a/Day12/python/solution2.py b/Day12/python/solution2.py new file mode 100644 index 0000000..d434b88 --- /dev/null +++ b/Day12/python/solution2.py @@ -0,0 +1,85 @@ +import sys + + +def read_data(filepath): + """Reads the data from the given file.""" + try: + with open(filepath, "r") as file: + return file.read().strip().split("\n") + except IOError as e: + print(f"Error reading file '{filepath}': {e}") + sys.exit(1) + + +def unfold_record(record): + """Unfolds the record according to the puzzle rules.""" + dots, blocks = record.split() + dots = "?".join([dots] * 5) + blocks = ",".join([blocks] * 5) + return dots, [int(x) for x in blocks.split(",")] + + +def count_arrangements(dots, blocks, i=0, bi=0, current=0, memo=None): + """Counts valid arrangements using dynamic programming.""" + if memo is None: + memo = {} + + key = (i, bi, current) + if key in memo: + return memo[key] + + if i == len(dots): + if bi == len(blocks) and current == 0: + return 1 + elif bi == len(blocks) - 1 and blocks[bi] == current: + return 1 + else: + return 0 + + ans = 0 + for c in [".", "#"]: + if dots[i] == c or dots[i] == "?": + if c == ".": + if current == 0: + ans += count_arrangements(dots, blocks, i + 1, bi, 0, memo) + elif current > 0 and bi < len(blocks) and blocks[bi] == current: + ans += count_arrangements(dots, blocks, i + 1, bi + 1, 0, memo) + elif c == "#": + ans += count_arrangements(dots, blocks, i + 1, bi, current + 1, memo) + + memo[key] = ans + return ans + + +def solve_puzzle(lines): + """Solves the puzzle for the given input lines.""" + total = 0 + for line in lines: + print(f"Processing: {line}") + dots, blocks = unfold_record(line) + total += count_arrangements(dots, blocks) + return total + + +def test_puzzle(): + """Runs the puzzle solution on test data.""" + test_data = read_data("../test.txt") + print("Running tests...") + test_result = solve_puzzle(test_data) + print(f"Test result: {test_result}") + assert test_result == 525152, "Test failed!" + print("Test passed.") + + +def main(): + """Main function to run the puzzle solution.""" + test_puzzle() + + input_data = read_data("../input.txt") + print("Processing input data...") + result = solve_puzzle(input_data) + print(f"Final result: {result}") + + +if __name__ == "__main__": + main() diff --git a/README.md b/README.md index 40f3abe..a0d79d3 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@