|
|
|
|
@ -1,26 +1,11 @@
|
|
|
|
|
|
|
|
|
|
use std::{io::prelude::*, fs::File, path::Path, collections::HashMap };
|
|
|
|
|
use std::{io::prelude::*, fs::File, path::Path, collections::HashMap, thread::current };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
// DATA STRUCTS
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
struct SeedRange
|
|
|
|
|
{
|
|
|
|
|
start: i64,
|
|
|
|
|
length: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SeedRange
|
|
|
|
|
{
|
|
|
|
|
fn new(start: i64, length: i64) -> SeedRange
|
|
|
|
|
{
|
|
|
|
|
SeedRange { start, length }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
struct SubMap
|
|
|
|
|
{
|
|
|
|
|
@ -61,6 +46,18 @@ impl SubMap
|
|
|
|
|
|
|
|
|
|
return self.destination_start + distance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn map_reverse(&self, val: i64) -> i64
|
|
|
|
|
{
|
|
|
|
|
if val >= self.destination_start && val <= self.destination_start + self.length
|
|
|
|
|
{
|
|
|
|
|
let distance = val - self.destination_start;
|
|
|
|
|
return self.source_start + distance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
|
@ -68,13 +65,14 @@ struct Map
|
|
|
|
|
{
|
|
|
|
|
name: String,
|
|
|
|
|
sub_maps: Vec<SubMap>,
|
|
|
|
|
lowest_dest_value: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Map
|
|
|
|
|
{
|
|
|
|
|
fn new(name: &str) -> Map
|
|
|
|
|
{
|
|
|
|
|
Map { name: name.to_string(), sub_maps: Vec::new() }
|
|
|
|
|
Map { name: name.to_string(), sub_maps: Vec::new(), lowest_dest_value: i64::MAX }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn map_input(&self, input: i64) -> i64
|
|
|
|
|
@ -89,6 +87,35 @@ impl Map
|
|
|
|
|
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn map_reverse(&self, input: i64) -> i64
|
|
|
|
|
{
|
|
|
|
|
for sub_map in &self.sub_maps
|
|
|
|
|
{
|
|
|
|
|
let source = sub_map.map_reverse(input);
|
|
|
|
|
|
|
|
|
|
if source > -1
|
|
|
|
|
{
|
|
|
|
|
return source;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return input;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SeedRange
|
|
|
|
|
{
|
|
|
|
|
start: i64,
|
|
|
|
|
length: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SeedRange
|
|
|
|
|
{
|
|
|
|
|
fn new(start: i64, length: i64) -> SeedRange
|
|
|
|
|
{
|
|
|
|
|
SeedRange { start, length }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
@ -96,7 +123,7 @@ impl Map
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
fn main()
|
|
|
|
|
{
|
|
|
|
|
let input = load_data("data/input");
|
|
|
|
|
let input = load_data("data/test_input");
|
|
|
|
|
let (seeds, maps, seed_ranges) = parse_input(&input);
|
|
|
|
|
|
|
|
|
|
// TEST STUFF
|
|
|
|
|
@ -108,68 +135,106 @@ fn main()
|
|
|
|
|
|
|
|
|
|
// println!("=========TESTS:\n\tseed-to-soil {} maps to {}\n\n\tseed-to-soil {} maps to {}\n\n\tseed-to-soil {} maps to {}\n",
|
|
|
|
|
// 99, test_1, 53, test_2, 47, test_3);
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// let location_map_idx = maps.len() -1;
|
|
|
|
|
// for (idx, sub_map) in maps[location_map_idx].sub_maps.iter().enumerate()
|
|
|
|
|
// {
|
|
|
|
|
// println!("map index: {} -- Location dest_start: {}", idx, sub_map.destination_start);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let location = find_closest_location(&seeds, &maps);
|
|
|
|
|
println!("Closest location: {}", location);
|
|
|
|
|
println!("================= Closest location: {} =================", location);
|
|
|
|
|
|
|
|
|
|
let second_loc = find_closest_from_range(&seed_ranges, &maps);
|
|
|
|
|
println!("Closest location from range: {}", second_loc);
|
|
|
|
|
let l2 = find_closest_location_from_ranges(&seed_ranges, &maps);
|
|
|
|
|
println!("================= Closest location from ranges: {} =================", l2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_closest_from_range(seed_ranges: &Vec<SeedRange>, maps: &Vec<Map>) -> i64
|
|
|
|
|
fn find_closest_location_from_ranges(seed_ranges: &Vec<SeedRange>, maps: &Vec<Map>) -> i64
|
|
|
|
|
{
|
|
|
|
|
let mut closest_location = i64::MAX;
|
|
|
|
|
// let mut closest_location = i64::MAX;
|
|
|
|
|
|
|
|
|
|
// for seed_range in seed_ranges
|
|
|
|
|
// {
|
|
|
|
|
// println!("Checking seed range: start: {}, length: {}", seed_range.start, seed_range.length);
|
|
|
|
|
|
|
|
|
|
// let start = seed_range.start;
|
|
|
|
|
// let end = seed_range.start + seed_range.length + 1;
|
|
|
|
|
// for seed in start..end
|
|
|
|
|
// println!("Checking next seed range (start {}, {} length)...", seed_range.start, seed_range.length);
|
|
|
|
|
// for seed in seed_range.start..seed_range.start + seed_range.length + 1
|
|
|
|
|
// {
|
|
|
|
|
// let location = find_seed_location(seed, maps);
|
|
|
|
|
|
|
|
|
|
// if location < closest_location
|
|
|
|
|
// {
|
|
|
|
|
// closest_location = location;
|
|
|
|
|
// println!("New lowest location: {}", closest_location);
|
|
|
|
|
// // best_seed = *seed;
|
|
|
|
|
|
|
|
|
|
// // let idx = maps.len() - 1;
|
|
|
|
|
// // if maps[idx].lowest_dest_value == closest_location
|
|
|
|
|
// // {
|
|
|
|
|
// // return closest_location;
|
|
|
|
|
// // }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// OTHER THINGS TO TRY:
|
|
|
|
|
// Super brute force but reject any seeds that are not valid (using the is_seed_valid function).
|
|
|
|
|
// Maybe this will include some seeds that were left out before by mistake?
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// closest_location
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// SUPER BRUTE FORCE ALL SEED VALUES BETWEEN SMALLEST SEED AND LARGEST SEED (INCLUDES INVALID SEEDS!)
|
|
|
|
|
let smallest_seed: i64 = 348350443;
|
|
|
|
|
let largest_seed: i64 = 4092364215 + 1;
|
|
|
|
|
|
|
|
|
|
for seed in smallest_seed..largest_seed
|
|
|
|
|
for location in 0..52510811
|
|
|
|
|
{
|
|
|
|
|
let location = find_seed_location(seed, maps);
|
|
|
|
|
// println!("Current Location: {}", location);
|
|
|
|
|
if location >= 52510810
|
|
|
|
|
{
|
|
|
|
|
println!("FAILED: location value greater than known max!");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if location < closest_location
|
|
|
|
|
// if location == 46
|
|
|
|
|
// {
|
|
|
|
|
// println!("LOCATOIN 46!");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
let seed = get_seed_from_location(location, maps, seed_ranges);
|
|
|
|
|
|
|
|
|
|
if seed > -1
|
|
|
|
|
{
|
|
|
|
|
closest_location = location;
|
|
|
|
|
let valid = match is_seed_valid(seed, seed_ranges)
|
|
|
|
|
{
|
|
|
|
|
true => "VALID",
|
|
|
|
|
false => "INVALID!"
|
|
|
|
|
};
|
|
|
|
|
println!("New lowest location: {} - from seed: {} ({})", closest_location, seed, valid);
|
|
|
|
|
return location;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closest_location
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_seed_valid(value: i64, seed_ranges: &Vec<SeedRange>) -> bool
|
|
|
|
|
fn get_seed_from_location(location: i64, maps: &Vec<Map>, seed_ranges: &Vec<SeedRange>) -> i64
|
|
|
|
|
{
|
|
|
|
|
// let mut skip_first = true;
|
|
|
|
|
let mut current_type_value = location;
|
|
|
|
|
for map in maps.iter().rev()
|
|
|
|
|
{
|
|
|
|
|
// Don't actually want to skip because we need to map
|
|
|
|
|
// the location dest to it's source
|
|
|
|
|
// if skip_first
|
|
|
|
|
// {
|
|
|
|
|
// skip_first = false;
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
current_type_value = map.map_reverse(current_type_value);
|
|
|
|
|
if current_type_value < 0
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if is_valid_seed(current_type_value, seed_ranges)
|
|
|
|
|
{
|
|
|
|
|
return current_type_value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_valid_seed(value: i64, seed_ranges: &Vec<SeedRange>) -> bool
|
|
|
|
|
{
|
|
|
|
|
for range in seed_ranges
|
|
|
|
|
{
|
|
|
|
|
@ -187,9 +252,9 @@ fn find_closest_location(seeds: &Vec<i64>, maps: &Vec<Map>) -> i64
|
|
|
|
|
// let mut best_seed = 0;
|
|
|
|
|
let mut closest_location = i64::MAX;
|
|
|
|
|
|
|
|
|
|
for seed in seeds
|
|
|
|
|
for (idx, seed) in seeds.iter().enumerate()
|
|
|
|
|
{
|
|
|
|
|
println!("Checking seed: {}", seed);
|
|
|
|
|
println!("Checking seed: {} ({} of {})", seed, idx + 1, seeds.len());
|
|
|
|
|
let location = find_seed_location(*seed, maps);
|
|
|
|
|
|
|
|
|
|
if location < closest_location
|
|
|
|
|
@ -237,22 +302,21 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>, Vec<SeedRange>) // String is
|
|
|
|
|
// SEEDS
|
|
|
|
|
if line.contains("seeds:")
|
|
|
|
|
{
|
|
|
|
|
println!("Parsing seed input...");
|
|
|
|
|
let mut start_value: i64 = -1;
|
|
|
|
|
println!("Parseing seed input...");
|
|
|
|
|
let mut start_seed: i64 = -1;
|
|
|
|
|
for val in line.trim_start_matches("seeds:").trim().split(" ")
|
|
|
|
|
{
|
|
|
|
|
let num = val.parse::<i64>().expect(&format!("Failed to parse seed value: {}", val));
|
|
|
|
|
seeds.push(num);
|
|
|
|
|
|
|
|
|
|
if start_value == -1
|
|
|
|
|
if start_seed == -1
|
|
|
|
|
{
|
|
|
|
|
start_value = num;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
seed_ranges.push(SeedRange::new(start_value, num));
|
|
|
|
|
start_value = -1;
|
|
|
|
|
start_seed = num;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
seed_ranges.push(SeedRange::new(start_seed, num));
|
|
|
|
|
start_seed = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
@ -262,6 +326,12 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>, Vec<SeedRange>) // String is
|
|
|
|
|
// NEW MAP
|
|
|
|
|
if line.contains("map:")
|
|
|
|
|
{
|
|
|
|
|
if maps.len() > 0
|
|
|
|
|
{
|
|
|
|
|
let idx = maps.len() - 1;
|
|
|
|
|
maps[idx].sub_maps.sort_by(|a, b| a.destination_start.partial_cmp(&b.destination_start).unwrap());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sub_map_idx = 0;
|
|
|
|
|
current_map = line.trim().trim_end_matches("map:").trim();
|
|
|
|
|
println!("Parsing map: {}", current_map);
|
|
|
|
|
@ -285,8 +355,15 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>, Vec<SeedRange>) // String is
|
|
|
|
|
sub_map.destination_start = vals[0].parse::<i64>().expect(&format!("Failed to parse dest value: {}", vals[0]));
|
|
|
|
|
sub_map.source_start = vals[1].parse::<i64>().expect(&format!("Failed to parse source value: {}", vals[1]));
|
|
|
|
|
sub_map.length = vals[2].parse::<i64>().expect(&format!("Failed to parse length value: {}", vals[2]));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//sub_map.map_values();
|
|
|
|
|
let idx = maps.len() - 1;
|
|
|
|
|
if sub_map.destination_start < maps[idx].lowest_dest_value
|
|
|
|
|
{
|
|
|
|
|
maps[idx].lowest_dest_value = sub_map.destination_start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
maps[idx].sub_maps.push(sub_map.clone());
|
|
|
|
|
sub_map_idx += 1;
|
|
|
|
|
// if let Some(map) = maps.get_mut(current_map)
|
|
|
|
|
@ -299,6 +376,9 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>, Vec<SeedRange>) // String is
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let idx = maps.len() - 1;
|
|
|
|
|
maps[idx].sub_maps.sort_by(|a, b| a.destination_start.partial_cmp(&b.destination_start).unwrap());
|
|
|
|
|
|
|
|
|
|
(seeds, maps, seed_ranges)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|