-
-
Save purplemonkeymad/ebcc6f043261c58ec90e884a2507f8a5 to your computer and use it in GitHub Desktop.
day5 part2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import argparse | |
import sys | |
def print_error(*args, **kwargs): | |
print(*args, file=sys.stderr, **kwargs) | |
def parse_map(section:str): | |
header,*lines = section.splitlines() | |
source,destination = (header.split(' ')[0]).split('-to-') | |
object = Map() | |
object.source_name = source | |
object.destination_name = destination | |
for range in lines: | |
dest,sour,size = range.strip().split(' ') | |
range_object = MapRange(int(sour),int(dest),int(size)) | |
object.mapping_list.append(range_object) | |
return object | |
class Map: | |
def __init__(self) -> None: | |
self.source_name = "" | |
self.destination_name = "" | |
self.mapping_list = list() | |
def __str__(self): | |
return f"Map: Source:{self.source_name}, Dest:{self.destination_name}, MappingCount:{len(self.mapping_list)}" | |
def convert_forward(self,number): | |
for range in self.mapping_list: | |
if range.in_range(number): | |
num2 = number - range.source_start + range.destination_start | |
return num2 | |
return number | |
def convert_backward(self,number): | |
for range in self.mapping_list: | |
if range.in_range(number): | |
num2 = number + range.source_start - range.destination_start | |
return num2 | |
return number | |
class MapRange: | |
source_start = -1 | |
destination_start = -1 | |
range_size = 0 | |
def __init__(self,source:int ,destination:int ,size:int ): | |
self.source_start = source | |
self.destination_start = destination | |
self.range_size = size | |
def __repr__(self): | |
return f"<MapRange source_start:{self.source_start}, destination_start:{self.destination_start}, range:{self.range_size}>" | |
def __str__(self): | |
return f"Range: {self.source_start}:{self.source_start+self.range_size} -> {self.destination_start}:{self.destination_start+self.range_size}" | |
def in_range(self,number:int): | |
source_end = self.source_start + self.range_size | |
return (number >= self.source_start and number < source_end) | |
def combine_map(first:Map, second:Map): | |
if first.destination_name != second.source_name: | |
raise f"Cannot combine maps: {first} & {second}" | |
# we should sort start ranges to make it easy | |
first_map = list(first.mapping_list) | |
second_map = list(second.mapping_list) | |
first_map.sort(key=lambda x:x.destination_start) | |
second_map.sort(key=lambda x:x.source_start) | |
new_map = Map() | |
new_map.source_name = first.source_name | |
new_map.destination_name = second.destination_name | |
new_list = list() | |
while len(first_map) > 0 or len(second_map) > 0: | |
#end cases | |
if len(first_map) == 0: | |
for r in second_map: | |
new_list.append(r) | |
second_map.remove(r) | |
continue | |
if len(second_map) == 0: | |
for r in first_map: | |
new_list.append(r) | |
first_map.remove(r) | |
continue | |
# both are non empty | |
first_top:MapRange = first_map[0] | |
second_top:MapRange = second_map[0] | |
# check for ranges | |
if (first_top.destination_start < second_top.source_start): | |
# first lower | |
if (second_top.source_start > (first_top.destination_start + first_top.range_size ) ): | |
# out of range, just copy first top | |
new_list.append(first_top) | |
first_map.remove(first_top) | |
continue | |
# check for overlap | |
if (second_top.source_start <= (first_top.destination_start + first_top.range_size ) ): | |
# overlap with first lower | |
lower_range = second_top.source_start - first_top.destination_start | |
lower_over = MapRange(first_top.source_start,first_top.destination_start,lower_range) | |
new_list.append(lower_over) | |
# update first with new info | |
first_top.source_start = first_top.source_start + lower_range | |
first_top.destination_start = first_top.destination_start + lower_range | |
first_top.range_size = first_top.range_size - lower_range | |
continue | |
raise Exception("second neither larger or smaller that size :S, {},{}".format(first_top,second_top)) | |
if (first_top.destination_start == second_top.source_start): | |
# we only need to check the ranges | |
if (first_top.range_size == second_top.range_size): | |
# same, easy copy | |
new_range = MapRange(first_top.source_start,second_top.destination_start,first_top.range_size) | |
new_list.append(new_range) | |
first_map.remove(first_top) | |
second_map.remove(second_top) | |
continue | |
if (first_top.range_size > second_top.range_size): | |
# second wholy in first | |
new_range = MapRange(first_top.source_start, second_top.destination_start,second_top.range_size) | |
new_list.append(new_range) | |
second_map.remove(second_top) | |
shift_size = second_top.range_size | |
first_top.range_size -= shift_size | |
first_top.destination_start += shift_size | |
first_top.source_start += shift_size | |
continue | |
if (first_top.range_size < second_top.range_size): | |
# first wholy in second | |
new_range = MapRange(first_top.source_start, second_top.destination_start,first_top.range_size) | |
new_list.append(new_range) | |
first_map.remove(first_top) | |
shift_size = first_top.range_size | |
second_top.range_size -= shift_size | |
second_top.destination_start += shift_size | |
second_top.source_start += shift_size | |
continue | |
raise Exception("unable to determine range sizes: {},{}".format(first_top,second_top)) | |
if (first_top.destination_start > second_top.source_start): | |
# second lower | |
if (first_top.destination_start > (second_top.source_start + second_top.range_size ) ): | |
# out of range, just copy second top | |
new_list.append(second_top) | |
second_map.remove(second_top) | |
continue | |
# check for overlap | |
if (first_top.destination_start <= (second_top.source_start + second_top.range_size ) ): | |
# overlap with second lower | |
lower_range = first_top.destination_start - second_top.source_start | |
lower_over = MapRange(second_top.source_start,second_top.destination_start,lower_range) | |
new_list.append(lower_over) | |
# update second with new info | |
second_top.source_start += lower_range | |
second_top.destination_start += lower_range | |
second_top.range_size -= lower_range | |
continue | |
raise Exception("second neither larger or smaller that size :S, {},{}".format(first_top,second_top)) | |
raise Exception("unable to determine lower positions: {},{}".format(first_top,second_top)) | |
# end while | |
new_map.mapping_list = new_list | |
return new_map | |
def parse_seed(section:str): | |
header,seed_section = section.split(':') | |
if (header != 'seeds'): | |
print_error("seeds header missing, results might be wonky") | |
# this is now in pairs | |
seed_id_list = [int(x) for x in re.split(' +',seed_section.strip())] | |
seed_ranges = zip(seed_id_list[::2],seed_id_list[1::2]) | |
seed_list = list() | |
for s,e in seed_ranges: | |
seed_list.append( range(s,s+e) ) | |
return seed_list | |
class Seed: | |
seed = -1 | |
soil = -1 | |
fertilizer = -1 | |
water = -1 | |
light = -1 | |
temperature = -1 | |
humidity = -1 | |
location = -1 | |
def calculate_properties(self,maps:dict): | |
current_type = "seed" | |
while current_type in maps.keys() and hasattr(self,current_type): | |
new_n = maps[current_type].convert_forward(getattr(self,current_type)) | |
current_type = maps[current_type].destination_name | |
setattr(self,current_type,new_n) | |
def __repr__(self): | |
return f"<Seed: {self.seed}, soil={self.soil}, fert={self.fertilizer}, water={self.water}, light={self.water}, temp={self.temperature}, hum={self.humidity}, loc={self.location}>" | |
def __str__(self): | |
return f"Seed: {self.seed}, soil={self.soil}, fert={self.fertilizer}, water={self.water}, light={self.water}, temp={self.temperature}, hum={self.humidity}, loc={self.location}" | |
def calculate_properties(maps:dict,number:int): | |
current_type = "seed" | |
current_number = number | |
while current_type in maps.keys(): | |
new_n = maps[current_type].convert_forward(current_number) | |
current_type = maps[current_type].destination_name | |
current_number = new_n | |
return current_number | |
def main(file_string:str): | |
seed_part,*map_parts = file_string.split('\n\n') | |
if (not re.match('seeds:',seed_part)): | |
raise Exception("File Must start with seeds record.") | |
seeds = parse_seed(seed_part) | |
#print(seeds) | |
maps = [parse_map(x) for x in map_parts] | |
#[print(str(x)) for x in maps] | |
flat_map = maps[0] | |
for i in range(1,len(maps)): | |
flat_map = combine_map(flat_map,maps[i]) | |
#for j in seeds: | |
# print("a:{},b:{}".format(j.start,calculate_properties( {"seed": flat_map } ,j.start))) | |
# print("a:{},b:{}".format(len(j),calculate_properties( {"seed": flat_map } ,len(j)))) | |
flat_map.mapping_list.sort(key=lambda x:x.destination_start) | |
print(flat_map) | |
[print(x) for x in flat_map.mapping_list] | |
#maps_index = { x.source_name:x for x in maps} | |
#print (maps_index) | |
# sort output for lowest and work back | |
for seedstart in flat_map.mapping_list: | |
local_min = None | |
seed_map_item:MapRange = seedstart | |
for seedrange in seeds: | |
if (seedrange.start >= seed_map_item.source_start and | |
seedrange.start < (seed_map_item.source_start + seed_map_item.range_size)): | |
# check values | |
to_check = seedrange.start | |
test = calculate_properties({'seed':flat_map},to_check) | |
if local_min == None or test < local_min: | |
local_min = test | |
if (seedrange.stop >= seed_map_item.source_start and | |
seedrange.stop < (seed_map_item.source_start + seed_map_item.range_size)): | |
to_check = seed_map_item.source_start | |
test = calculate_properties({'seed':flat_map},to_check) | |
if local_min == None or test < local_min: | |
local_min = test | |
if local_min != None: | |
print(local_min) | |
exit(0) | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description="day 5 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) | |
file = open(filename,'r') | |
main(file.read()) | |
file.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment