Skip to content

Instantly share code, notes, and snippets.

@purplemonkeymad
Created December 12, 2023 14:17
Show Gist options
  • Save purplemonkeymad/31c73b90797c97eccdb10411ea26c620 to your computer and use it in GitHub Desktop.
Save purplemonkeymad/31c73b90797c97eccdb10411ea26c620 to your computer and use it in GitHub Desktop.
2023 day 12 takes a long time.
import re
import math
import argparse
import itertools
import enum
class TestState(enum.Enum):
Start = 0
Working = 1
Damaged = 2
class Record:
def __init__(self,data:str,check_digits:list[int]) -> None:
self.data = data
self.check_digits = check_digits
def __repr__(self) -> str:
return f"Record({self.data}:{self.check_digits})"
# need a tester for a valid data line:
# this only works for fully filled items
# meaning there would be too many branches
def is_valid_line(self) -> bool:
# we turn check digits to a regex:
regex_mid = '\.+?'.join(["#{"+str(x)+'}' for x in self.check_digits])
regex = '^\.+?'+regex_mid+'\.+?$'
string = '.'+self.data+'.' # fix problems with at end of string items
return re.search(regex,string) != None
def is_missing_data(self) -> bool:
return '?' in self.data
# use a finite machines so we can
# ditch a branch & children sooner.
def can_be_possible(self) -> bool:
state = TestState.Start
check_queue = list(self.check_digits)
block_size = 0
drop_on_damage = False
for c in [*self.data, '.']:
# reached known tests have to say yes
if c == '?':
return True
if not c in '.?#':
raise Exception(f"Unknown char: {c} in {self}")
## too many blocks will cause a failure
if len(check_queue) == 0:
drop_on_damage = True
match state:
case TestState.Start:
if c == '.':
state = TestState.Working
if c == '#':
state = TestState.Damaged
block_size += 1
case TestState.Working:
if c == '#':
if drop_on_damage:
return False
block_size += 1
state = TestState.Damaged
case TestState.Damaged:
if c == '#':
block_size += 1
if c =='.':
check = check_queue.pop(0)
if block_size != check:
return False
block_size = 0
state = TestState.Working
#left overs == bad
if len(check_queue) > 0:
return False
return True
def possible_records(self):
if not self.can_be_possible():
return []
if not self.is_missing_data():
return [self]
sub_records = list()
# we only replace on ? at a time
index = self.data.index(r'?')
temp_data = [*self.data]
for new_char in ['.','#']:
temp_data[index] = new_char
sub_records.extend(Record("".join(temp_data),self.check_digits).possible_records())
return sub_records
def parse_record(line:str) -> tuple:
springs,checks = line.split(' ')
check_digits = [ int(x) for x in checks.split(',')]
return (springs,check_digits)
def main(line_list:list,part:int):
records = [Record(*parse_record(_)) for _ in line_list]
# part1
sum = 0
for r in records:
print(f"{r} --------- :")
pos = r.possible_records()
#for x in pos:
# print(x)
sum += len(pos)
print(f"{len(pos)}")
print(f"Part1: {sum}")
if part != 2:
exit(0)
# part 2
sum2 = 0
for r in records:
r.data = '?'.join([r.data] *5)
r.check_digits = r.check_digits * 5
print(f"{r} --------- :")
pos = r.possible_records()
#for x in pos:
# print(x)
sum2 += len(pos)
print(f"{len(pos)}")
print(f"Part2: {sum2}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="template for aoc solver")
parser.add_argument("-input",type=str)
parser.add_argument("-part",type=int)
args = parser.parse_args()
filename = args.input
if filename == None:
parser.print_help()
exit(1)
part = args.part
file = open(filename,'r')
main([line.rstrip('\n') for line in file.readlines()],part)
file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment