solve_day5_part1<-function(input){## extract seeds ----seeds<-stringi::stri_extract_all(input[[1]], regex ="\\d+")seeds<-as.numeric(unlist(seeds))## extract maps ----map_input<-input[-1]# split input into sections based on blank linesection_ends<-which(stringi::stri_detect(map_input, regex ="^$"))section_ends<-c(section_ends, length(map_input))map_range<-vector("list", length(section_ends)-1)for(indexin1:(length(section_ends)-1)){map_range[index]<-list(c(section_ends[index], section_ends[index+1]))}# each map goes from a blank line + 2 (skip map title)# to the next blank line - 1maps<-lapply(map_range, \(range, m_i)m_i[(range[[1]]+2):(range[[2]]-1)],map_input)# pull out the numbers in each line, with lines nested in mapsmaps<-lapply(maps, \(map)stringi::stri_extract_all(map, regex ="\\d+"))# convert nested character numbers to numericmaps<-lapply(maps, \(map)lapply(map, as.numeric))## map seeds to location ----# starting with the seed numbers, sequentially pass the numbers through# each source-to-destination maplocation<-Reduce(source_to_dest, maps, init =seeds)# lowest location numbermin(location)}source_to_dest<-function(source_nums, map){vapply(source_nums, find_mapped_value, numeric(1), map)}find_mapped_value<-function(source_num, map){for(mappinginmap){dest_start<-mapping[[1]]source_start<-mapping[[2]]length<-mapping[[3]]source_end<-(source_start+length)-1# if the number is within the source rangeif(source_num>=source_start&source_num<=source_end){# return new destination numberreturn(dest_start+source_num-source_start)}}# keep the number the same if no mapping foundsource_num}
Run
aoc_source(day =5, part =1)input=aoc_read(day =5)aoc_run(solve_day5_part1(input))
Elapsed: 0.002 seconds
Memory: 731 KB
— R Day 5 Part 2 —
solve_day5_part2<-function(input){## extract seeds ----seed_ranges<-stringi::stri_extract_all(input[[1]], regex ="\\d+ \\d+")seed_ranges<-stringi::stri_split(unlist(seed_ranges), fixed =" ")seed_ranges<-lapply(seed_ranges, as.numeric)length_to_end<-function(range){range[[2]]<-range[[1]]+range[[2]]-1range}seed_ranges<-lapply(seed_ranges, length_to_end)## extract maps ----map_input<-input[-1]# split input into sections based on blank linesection_ends<-which(stringi::stri_detect(map_input, regex ="^$"))section_ends<-c(section_ends, length(map_input)+1)map_range<-vector("list", length(section_ends)-1)for(indexin1:(length(section_ends)-1)){map_range[index]<-list(c(section_ends[index], section_ends[index+1]))}# each map goes from a blank line + 2 (skip map title)# to the next blank line - 1maps<-lapply(map_range, \(range, m_i)m_i[(range[[1]]+2):(range[[2]]-1)],map_input)# pull out the numbers in each line, with lines nested in mapsmaps<-lapply(maps, \(map)stringi::stri_extract_all(map, regex ="\\d+"))# convert nested character numbers to numericmaps<-lapply(maps, \(map)lapply(map, as.numeric))## asdfasdf ----smallest_locations<-lapply(seed_ranges, find_smallest_in_range, maps)min(unlist(smallest_locations))}find_smallest_in_range<-function(input_range, maps, map_no=1){if(map_no>length(maps)){return(unlist(input_range[[1]]))}map<-maps[[map_no]]ranges_to_check<-list(input_range)output_ranges<-list()for(mappinginmap){if(length(ranges_to_check)<=0){break}# remaining to check against next mappingremaining_ranges<-list()# mapping rangedest_start<-mapping[[1]]mapping_start<-mapping[[2]]length<-mapping[[3]]mapping_end<-(mapping_start+length)-1for(range_to_checkinranges_to_check){# starting range-to-checkchecking_start<-range_to_check[[1]]checking_end<-range_to_check[[2]]# Check for intersection between input and mapping range# input start before, inside or after the mapping# each handled separately# Before ----# if any of the input is before the mapping range# store it as remaining to be checked again on the next mapping# Example:# input |-----|# mapping |-----|## remaining |--|if(checking_start<mapping_start){before_end<-min(checking_end, mapping_start-1)before_range<-c(checking_start, before_end)remaining_ranges<-c(remaining_ranges, list(before_range))}# After ----# if any of the input is after the mapping range# store it as remaining to be checked again on the next iteration# Example:# input |-------|# mapping |-----|## remaining |----|if(checking_end>mapping_end){after_start<-max(checking_start, mapping_end+1)after_range<-c(after_start, checking_end)remaining_ranges<-c(remaining_ranges, list(after_range))}# Intersection ----# where there is an intersection derive the intersection range# then map it to the destination range# append the new range to the output ranges# Example:# input |------|# mapping |-----|# map-dest |-----|## intersects |--|# output |--|intersect_start<-max(checking_start, mapping_start)intersect_end<-min(checking_end, mapping_end)intersect_length<-intersect_end-intersect_start+1if(intersect_length>0){mapped_start<-dest_start+(intersect_start-mapping_start)mapped_end<-mapped_start+intersect_length-1output_ranges<-c(output_ranges, list(c(mapped_start, mapped_end)))intersecting_in_remaining<-vapply(remaining_ranges, \(x)identical(x, range_to_check),logical(1))remaining_ranges<-remaining_ranges[!intersecting_in_remaining]}}ranges_to_check<-remaining_ranges}output_ranges<-c(output_ranges, ranges_to_check)map_no<-map_no+1# pass all output ranges back into this function as input# to be checked against the next group of mappingslapply(output_ranges, find_smallest_in_range, maps, map_no)}
Run
aoc_source(day =5, part =2)input=aoc_read(day =5)aoc_run(solve_day5_part2(input))