Day 5 aoc, stuck on b, works on test input, enters infinite loop on one entry of the real input
This commit is contained in:
338
advent_of_code/2023/5/src/main.rs
Normal file
338
advent_of_code/2023/5/src/main.rs
Normal file
@@ -0,0 +1,338 @@
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{i64, multispace0, multispace1},
|
||||
multi::separated_list1,
|
||||
sequence::{delimited, tuple},
|
||||
IResult, Parser,
|
||||
};
|
||||
|
||||
/// This function assumes your input vector is sorted.
|
||||
/// It might break if this is not the case.
|
||||
/// We don't check if this is actually true, because that would take O(n) time,
|
||||
/// while we aim for this function to only take up O(log n) time.
|
||||
fn binary_search<'a, Item: Ord + Eq>(
|
||||
value_to_find: &Item,
|
||||
vector_to_find_it_in: &'a Vec<Item>,
|
||||
) -> Option<&'a Item> {
|
||||
if vector_to_find_it_in.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut index = vector_to_find_it_in.len() / 2;
|
||||
let mut lower = 0;
|
||||
let mut upper = vector_to_find_it_in.len() - 1;
|
||||
while upper != lower {
|
||||
if vector_to_find_it_in[index] <= *value_to_find {
|
||||
upper = index;
|
||||
} else {
|
||||
lower = index;
|
||||
}
|
||||
index = (upper - lower) / 2;
|
||||
}
|
||||
if vector_to_find_it_in[index] == *value_to_find {
|
||||
Some(&vector_to_find_it_in[index])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn give_map_parser<'a: 'b, 'b: 'a>(
|
||||
map_name: &'a str,
|
||||
) -> impl FnMut(&'a str) -> IResult<&'b str, Vec<(i64, i64, i64)>> {
|
||||
delimited(
|
||||
tag(map_name),
|
||||
separated_list1(
|
||||
multispace1,
|
||||
tuple((i64, multispace1, i64, multispace1, i64)).map(|(a, _, b, _, c)| (a, b, c)),
|
||||
),
|
||||
multispace0,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_input(
|
||||
input: &str,
|
||||
) -> IResult<
|
||||
&str,
|
||||
(
|
||||
Vec<i64>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
),
|
||||
> {
|
||||
let (input, result) = tuple((
|
||||
delimited(tag("seeds: "), separated_list1(tag(" "), i64), multispace1),
|
||||
give_map_parser("seed-to-soil map:\n"),
|
||||
give_map_parser("soil-to-fertilizer map:\n"),
|
||||
give_map_parser("fertilizer-to-water map:\n"),
|
||||
give_map_parser("water-to-light map:\n"),
|
||||
give_map_parser("light-to-temperature map:\n"),
|
||||
give_map_parser("temperature-to-humidity map:\n"),
|
||||
give_map_parser("humidity-to-location map:\n"),
|
||||
))(input)?;
|
||||
|
||||
Ok((input, result))
|
||||
}
|
||||
|
||||
fn destination_to_source_mapping(source: i64, mappings: &Vec<(i64, i64, i64)>) -> i64 {
|
||||
mappings
|
||||
.iter()
|
||||
.filter(|&(_destination_start, source_start, range)| {
|
||||
source >= *source_start && source < *source_start + *range
|
||||
})
|
||||
.next()
|
||||
.map(|(destination_start, source_start, _range)| {
|
||||
destination_start + (source - source_start)
|
||||
})
|
||||
.unwrap_or(source)
|
||||
}
|
||||
|
||||
fn solve_1(
|
||||
almanac: &(
|
||||
Vec<i64>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
),
|
||||
) -> i64 {
|
||||
almanac
|
||||
.0
|
||||
.iter()
|
||||
.map(|&seed| {
|
||||
let soil = destination_to_source_mapping(seed, &almanac.1);
|
||||
let fertilizer = destination_to_source_mapping(soil, &almanac.2);
|
||||
let water = destination_to_source_mapping(fertilizer, &almanac.3);
|
||||
let light = destination_to_source_mapping(water, &almanac.4);
|
||||
let temperature = destination_to_source_mapping(light, &almanac.5);
|
||||
let humidity = destination_to_source_mapping(temperature, &almanac.6);
|
||||
destination_to_source_mapping(humidity, &almanac.7)
|
||||
})
|
||||
.min()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn search_range(
|
||||
input_range: (i64, i64),
|
||||
mappings: &Vec<(i64, i64, i64)>,
|
||||
) -> Option<(i64, i64, i64)> {
|
||||
if mappings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut index = mappings.len() / 2;
|
||||
let mut lower = 0;
|
||||
let mut upper = mappings.len() - 1;
|
||||
|
||||
while upper != lower {
|
||||
if mappings[index].1 <= input_range.0
|
||||
&& mappings[index].1 + mappings[index].2 > input_range.0
|
||||
{
|
||||
return Some(mappings[index]);
|
||||
} else if mappings[index].1 < input_range.0 {
|
||||
lower = index;
|
||||
} else {
|
||||
upper = index - 1;
|
||||
}
|
||||
index = (upper + lower).div_ceil(2);
|
||||
}
|
||||
|
||||
if index >= mappings.len() - 1 && mappings[index].1 + mappings[index].2 < input_range.1 {
|
||||
None
|
||||
} else if mappings[index].1 == input_range.0 {
|
||||
mappings.get(index + 1).copied()
|
||||
} else {
|
||||
Some(mappings[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We will assume the mappings are sorted on the source, so we can more quickly, using binary search, find
|
||||
/// the suitable ranges. This is relevant because of the large numbers involved.
|
||||
fn get_suitable_range(input_range: (i64, i64), mappings: &Vec<(i64, i64, i64)>) -> Vec<(i64, i64)> {
|
||||
let mut found_until = input_range.0;
|
||||
let mut result = vec![];
|
||||
|
||||
while found_until < input_range.1 {
|
||||
println!(
|
||||
"{} {:?} {:?}",
|
||||
found_until,
|
||||
mappings,
|
||||
search_range((found_until, input_range.1), mappings)
|
||||
);
|
||||
if let Some((destination_start, source_start, range)) =
|
||||
search_range((found_until, input_range.1), mappings)
|
||||
{
|
||||
if source_start > found_until {
|
||||
println!("ding");
|
||||
result.push((found_until, std::cmp::min(source_start, input_range.1)));
|
||||
found_until = std::cmp::min(source_start, input_range.1);
|
||||
} else {
|
||||
println!("dong");
|
||||
let max_source = std::cmp::min(source_start + range, input_range.1);
|
||||
result.push((
|
||||
found_until + (destination_start - source_start),
|
||||
destination_start + (max_source - source_start),
|
||||
));
|
||||
found_until = max_source;
|
||||
}
|
||||
} else {
|
||||
println!("dang");
|
||||
result.push((found_until, input_range.1));
|
||||
found_until = input_range.1;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_suitable_ranges(
|
||||
input_ranges: &Vec<(i64, i64)>,
|
||||
mappings: &Vec<(i64, i64, i64)>,
|
||||
) -> Vec<(i64, i64)> {
|
||||
let mut output_ranges_uncompressed = input_ranges
|
||||
.iter()
|
||||
.map(|&(range_start, range_length)| {
|
||||
get_suitable_range((range_start, range_start + range_length), mappings)
|
||||
})
|
||||
.collect::<Vec<Vec<(i64, i64)>>>()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<(i64, i64)>>();
|
||||
|
||||
output_ranges_uncompressed.sort_unstable();
|
||||
let (mut lower, mut upper) = output_ranges_uncompressed[0];
|
||||
|
||||
let mut output_ranges = vec![];
|
||||
for (range_low, range_up) in output_ranges_uncompressed {
|
||||
if range_low <= upper {
|
||||
upper = range_up;
|
||||
} else {
|
||||
output_ranges.push((lower, upper));
|
||||
lower = range_low;
|
||||
upper = range_up;
|
||||
}
|
||||
}
|
||||
output_ranges.push((lower, upper));
|
||||
output_ranges = output_ranges
|
||||
.iter()
|
||||
.map(|&(range_start, range_end)| (range_start, range_end - range_start))
|
||||
.collect::<Vec<_>>();
|
||||
output_ranges
|
||||
}
|
||||
|
||||
fn solve_2(
|
||||
almanac: &(
|
||||
Vec<i64>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
Vec<(i64, i64, i64)>,
|
||||
),
|
||||
) -> i64 {
|
||||
let mut lowest_location_numbers = vec![];
|
||||
let mut seed_iter = almanac.0.iter();
|
||||
|
||||
let mut soil_almanac = almanac.1.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
soil_almanac.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut fertilizer_almanac = almanac.2.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
fertilizer_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut water_almanac = almanac.3.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
water_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut light_almanac = almanac.4.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
light_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut temperature_almanac = almanac.5.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
temperature_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut humidity_almanac = almanac.6.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
humidity_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
let mut location_almanac = almanac.7.iter().map(|&n| n).collect::<Vec<_>>();
|
||||
location_almanac
|
||||
.sort_unstable_by(|&(_d1, source1, _r1), (_d2, source2, _r2)| source1.cmp(source2));
|
||||
|
||||
while let (Some(&lower_seed), Some(&range_length)) = (seed_iter.next(), seed_iter.next()) {
|
||||
println!("ding");
|
||||
let seed_range = vec![(lower_seed, range_length)];
|
||||
let soil_ranges = get_suitable_ranges(&seed_range, &soil_almanac);
|
||||
println!("soil");
|
||||
|
||||
let fertilizer_ranges = get_suitable_ranges(&soil_ranges, &fertilizer_almanac);
|
||||
println!("fertilizer");
|
||||
|
||||
let water_ranges = get_suitable_ranges(&fertilizer_ranges, &water_almanac);
|
||||
println!("water");
|
||||
|
||||
let light_ranges = get_suitable_ranges(&water_ranges, &light_almanac);
|
||||
println!("light");
|
||||
|
||||
let temperature_ranges = get_suitable_ranges(&light_ranges, &temperature_almanac);
|
||||
println!("temperature");
|
||||
|
||||
let humidity_ranges = get_suitable_ranges(&temperature_ranges, &humidity_almanac);
|
||||
println!("humidity");
|
||||
|
||||
lowest_location_numbers.push(
|
||||
get_suitable_ranges(&humidity_ranges, &location_almanac)
|
||||
.iter()
|
||||
.map(|&(range_min, _range_max)| range_min)
|
||||
.min()
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
*lowest_location_numbers.iter().min().unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Hello, this is Patrick!");
|
||||
|
||||
let input_text = include_str!("../input.txt");
|
||||
|
||||
let (_, almanac) = parse_input(input_text).unwrap();
|
||||
|
||||
println!(
|
||||
"The lowest location number corresponding to the initial seed numbers is {}",
|
||||
solve_1(&almanac)
|
||||
);
|
||||
|
||||
println!(
|
||||
"The lowest location number for any of the initial seed ranges is {}",
|
||||
solve_2(&almanac)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_1() {
|
||||
let input_text = include_str!("../test_input.txt");
|
||||
|
||||
let (_, almanac) = parse_input(input_text).unwrap();
|
||||
|
||||
assert_eq!(solve_1(&almanac), 35);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2() {
|
||||
let input_text = include_str!("../test_input.txt");
|
||||
|
||||
let (_, almanac) = parse_input(input_text).unwrap();
|
||||
|
||||
assert_eq!(solve_2(&almanac), 46);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user