|
|
|
|
@ -6,6 +6,21 @@ use std::{io::prelude::*, fs::File, path::Path, collections::HashMap };
|
|
|
|
|
// 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
|
|
|
|
|
{
|
|
|
|
|
@ -82,7 +97,7 @@ impl Map
|
|
|
|
|
fn main()
|
|
|
|
|
{
|
|
|
|
|
let input = load_data("data/input");
|
|
|
|
|
let (seeds, maps) = parse_input(&input);
|
|
|
|
|
let (seeds, maps, seed_ranges) = parse_input(&input);
|
|
|
|
|
|
|
|
|
|
// TEST STUFF
|
|
|
|
|
// println!("SEEDS: {:#?}, MAPS: {:#?}", seeds, maps);
|
|
|
|
|
@ -97,6 +112,74 @@ fn main()
|
|
|
|
|
|
|
|
|
|
let location = find_closest_location(&seeds, &maps);
|
|
|
|
|
println!("Closest location: {}", location);
|
|
|
|
|
|
|
|
|
|
let second_loc = find_closest_from_range(&seed_ranges, &maps);
|
|
|
|
|
println!("Closest location from range: {}", second_loc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_closest_from_range(seed_ranges: &Vec<SeedRange>, maps: &Vec<Map>) -> i64
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
// {
|
|
|
|
|
// let location = find_seed_location(seed, maps);
|
|
|
|
|
|
|
|
|
|
// if location < closest_location
|
|
|
|
|
// {
|
|
|
|
|
// closest_location = location;
|
|
|
|
|
// println!("New lowest location: {}", closest_location);
|
|
|
|
|
// // best_seed = *seed;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 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?
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
{
|
|
|
|
|
let location = find_seed_location(seed, maps);
|
|
|
|
|
|
|
|
|
|
if location < closest_location
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closest_location
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_seed_valid(value: i64, seed_ranges: &Vec<SeedRange>) -> bool
|
|
|
|
|
{
|
|
|
|
|
for range in seed_ranges
|
|
|
|
|
{
|
|
|
|
|
if value >= range.start && value <= range.start + range.length
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_closest_location(seeds: &Vec<i64>, maps: &Vec<Map>) -> i64
|
|
|
|
|
@ -125,7 +208,7 @@ fn find_seed_location(seed: i64, maps: &Vec<Map>) -> i64
|
|
|
|
|
let mut current_value = seed;
|
|
|
|
|
for map in maps
|
|
|
|
|
{
|
|
|
|
|
println!("Mapping {}...", map.name);
|
|
|
|
|
// println!("Mapping {}...", map.name);
|
|
|
|
|
current_value = map.map_input(current_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -135,9 +218,10 @@ fn find_seed_location(seed: i64, maps: &Vec<Map>) -> i64
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
// INPUT PARSING
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
|
fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>) // String is the map name
|
|
|
|
|
fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>, Vec<SeedRange>) // String is the map name
|
|
|
|
|
{
|
|
|
|
|
let mut seeds: Vec<i64> = Vec::new();
|
|
|
|
|
let mut seed_ranges: Vec<SeedRange> = Vec::new();
|
|
|
|
|
|
|
|
|
|
let mut maps: Vec<Map> = Vec::new();
|
|
|
|
|
let mut current_map = "";
|
|
|
|
|
@ -153,11 +237,22 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>) // String is the map name
|
|
|
|
|
// SEEDS
|
|
|
|
|
if line.contains("seeds:")
|
|
|
|
|
{
|
|
|
|
|
println!("Parseing seed input...");
|
|
|
|
|
println!("Parsing seed input...");
|
|
|
|
|
let mut start_value: 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
|
|
|
|
|
{
|
|
|
|
|
start_value = num;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
seed_ranges.push(SeedRange::new(start_value, num));
|
|
|
|
|
start_value = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
@ -179,7 +274,7 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>) // String is the map name
|
|
|
|
|
// NEW SUB MAP
|
|
|
|
|
let mut sub_map = SubMap::new();
|
|
|
|
|
let vals: Vec<&str> = line.trim().split(" ").collect();
|
|
|
|
|
println!("Parsing sub map: {}", sub_map_idx);
|
|
|
|
|
// println!("Parsing sub map: {}", sub_map_idx);
|
|
|
|
|
|
|
|
|
|
if vals.len() < 3
|
|
|
|
|
{
|
|
|
|
|
@ -204,7 +299,7 @@ fn parse_input(input: &str) -> (Vec<i64>, Vec<Map>) // String is the map name
|
|
|
|
|
// }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(seeds, maps)
|
|
|
|
|
(seeds, maps, seed_ranges)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|