[TASK] Solved Day 5
This commit is contained in:
@@ -2,6 +2,8 @@ use std::collections::HashSet;
|
||||
|
||||
use crate::day_solver::DaySolver;
|
||||
use crate::util::Coord;
|
||||
#[cfg(test)]
|
||||
use crate::util::read_file;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Day3 {
|
||||
@@ -129,12 +131,12 @@ impl DaySolver for Day3 {
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day3 = Day3::create("input/day03_example.txt".to_string());
|
||||
let mut day3 = Day3::create(read_file("input/day03_example.txt"));
|
||||
assert_eq!("4361", day3.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day3 = Day3::create("input/day03_example.txt".to_string());
|
||||
let mut day3 = Day3::create(read_file("input/day03_example.txt"));
|
||||
assert_eq!("467835", day3.solve_part2());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use crate::day_solver::DaySolver;
|
||||
#[cfg(test)]
|
||||
use crate::util::read_file;
|
||||
|
||||
pub struct Day4 {
|
||||
cards: Vec<Card>
|
||||
@@ -80,12 +82,12 @@ impl DaySolver for Day4 {
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day = Day4::create("input/day04_example.txt".to_string());
|
||||
let mut day = Day4::create(read_file("input/day04_example.txt"));
|
||||
assert_eq!("13", day.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day = Day4::create("input/day04_example.txt".to_string());
|
||||
let mut day = Day4::create(read_file("input/day04_example.txt"));
|
||||
assert_eq!("30", day.solve_part2());
|
||||
}
|
||||
|
||||
152
src/day5.rs
Normal file
152
src/day5.rs
Normal file
@@ -0,0 +1,152 @@
|
||||
use std::cmp::min;
|
||||
use crate::day_solver::DaySolver;
|
||||
#[cfg(test)]
|
||||
use crate::util::read_file;
|
||||
|
||||
pub struct Day5 {
|
||||
seeds: Vec<usize>,
|
||||
maps: Vec<Map>,
|
||||
}
|
||||
|
||||
struct Map {
|
||||
map_ranges: Vec<MapRange>
|
||||
}
|
||||
|
||||
struct MapRange {
|
||||
source_range_start: usize,
|
||||
dest_range_start: usize,
|
||||
range_length: usize
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn convert(&self, x: usize) -> usize {
|
||||
self.map_ranges
|
||||
.iter().find(|r| r.contains(&x))
|
||||
.map(|r| r.map(&x))
|
||||
.unwrap_or(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl MapRange {
|
||||
fn contains(&self, x: &usize) -> bool {
|
||||
return x >= &self.source_range_start && x < &(self.source_range_start + self.range_length)
|
||||
}
|
||||
|
||||
fn map(&self, x: &usize) -> usize {
|
||||
debug_assert!(self.contains(x));
|
||||
let res = self.dest_range_start + (x - self.source_range_start);
|
||||
// println!("input {} on range {} {} {} maps to {}", x, self.source_range_start, self.dest_range_start, self.range_length, res);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Day5 {
|
||||
|
||||
pub fn create(input: String) -> Self {
|
||||
|
||||
let mut lines = input.lines();
|
||||
|
||||
let seeds_line = lines.next().unwrap();
|
||||
assert!(seeds_line.starts_with("seeds:"));
|
||||
let (_, seeds_str) = seeds_line.split_once(": ").unwrap();
|
||||
let seeds = seeds_str.split_whitespace().map(|s| s.parse().unwrap()).collect();
|
||||
|
||||
let mut maps = Vec::new();
|
||||
while let Some(line) = lines.next() {
|
||||
if line.ends_with("map:") {
|
||||
// time to map the next lines to a map
|
||||
let mut map_ranges = Vec::new();
|
||||
while let Some(map_line) = lines.next() {
|
||||
let mut nrs = map_line.split_whitespace().map(|n| n.parse().unwrap());
|
||||
if let Some(dest_range_start) = nrs.next() {
|
||||
map_ranges.push(MapRange {
|
||||
dest_range_start,
|
||||
source_range_start: nrs.next().unwrap(),
|
||||
range_length: nrs.next().unwrap()
|
||||
});
|
||||
} else {
|
||||
// End of map, so time for the next loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
map_ranges.sort_by(|a, b| a.source_range_start.cmp(&b.source_range_start));
|
||||
maps.push(Map {
|
||||
map_ranges
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Put the input into the day struct
|
||||
return Day5 {
|
||||
seeds,
|
||||
maps
|
||||
}
|
||||
}
|
||||
|
||||
fn map(&self, seed: usize) -> usize {
|
||||
self.maps.iter().fold(seed.to_owned(), |acc, m| m.convert(acc))
|
||||
}
|
||||
}
|
||||
|
||||
impl DaySolver for Day5 {
|
||||
|
||||
|
||||
fn solve_part1(&mut self) -> String {
|
||||
self.seeds.iter()
|
||||
.map(|x| self.map(x.to_owned()))
|
||||
.min()
|
||||
.unwrap().to_string()
|
||||
}
|
||||
|
||||
fn solve_part2(&mut self) -> String {
|
||||
|
||||
let mut min_loc = self.map(self.seeds.first().unwrap().to_owned());
|
||||
for seed_range in self.seeds.chunks(2) {
|
||||
let seed_range_start = seed_range[0];
|
||||
let seed_range_length = seed_range[1];
|
||||
|
||||
let mut seed = seed_range_start;
|
||||
while seed < seed_range_start + seed_range_length {
|
||||
let mut max_skip = seed_range_length;
|
||||
// We're gonna look for the range boundaries and only check the numbers there
|
||||
let mut cur = seed;
|
||||
for map in &self.maps {
|
||||
if let Some(range) = map.map_ranges.iter().find(|r| r.contains(&cur)) {
|
||||
// The max_skip now moves to the end of this range, because we need to check the next number
|
||||
max_skip = min(max_skip, range.source_range_start + range.range_length - cur);
|
||||
cur = range.map(&cur);
|
||||
} else {
|
||||
// We're not in any "mapping" range, so cur doesn't change, but now the max skip is wherever the next range starts:
|
||||
if let Some(range) = map.map_ranges.iter().find(|r| r.source_range_start > cur) {
|
||||
max_skip = min(max_skip, range.source_range_start - cur)
|
||||
}
|
||||
}
|
||||
}
|
||||
if cur < min_loc {
|
||||
min_loc = cur
|
||||
}
|
||||
seed += max_skip
|
||||
}
|
||||
// Or to solve it "brute force" (which takes about 130 seconds in release mode on my PC)
|
||||
// for seed in seed_range_start..(seed_range_start + seed_range_length) {
|
||||
// let loc = self.map(seed);
|
||||
// if loc < min_loc {
|
||||
// min_loc = loc
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return min_loc.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day = Day5::create(read_file("input/day05_example.txt"));
|
||||
assert_eq!("35", day.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day = Day5::create(read_file("input/day05_example.txt"));
|
||||
assert_eq!("46", day.solve_part2());
|
||||
}
|
||||
17
src/dayX.rs
17
src/dayX.rs
@@ -1,15 +1,14 @@
|
||||
use crate::day_solver::DaySolver;
|
||||
|
||||
use super::util;
|
||||
#[cfg(test)]
|
||||
use crate::util::read_file;
|
||||
|
||||
pub struct DayX {
|
||||
}
|
||||
|
||||
impl DayX {
|
||||
|
||||
pub fn create(input_filename: String) -> Self {
|
||||
let lines = util::read_file("input/dayX_example.txt");
|
||||
// let lines = util::read_file("input/dayX.txt");
|
||||
pub fn create(input: String) -> Self {
|
||||
// let lines = input.lines();
|
||||
|
||||
// Put the input into the day struct
|
||||
return DayX {}
|
||||
@@ -30,12 +29,12 @@ impl DaySolver for DayX {
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day = DayX::create("input/dayX_example.txt".to_string());
|
||||
assert_eq!("4361", day.solve_part1());
|
||||
let mut day = DayX::create(read_file("input/dayX_example.txt"));
|
||||
assert_eq!("EXAMPLE_ANSWER", day.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day = DayX::create("input/dayX_example.txt".to_string());
|
||||
assert_eq!("467835", day.solve_part2());
|
||||
let mut day = DayX::create(read_file("input/dayX_example.txt"));
|
||||
assert_eq!("EXAMPLE_ANSWER", day.solve_part2());
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::day1::Day1;
|
||||
use crate::day2::Day2;
|
||||
use crate::day3::Day3;
|
||||
use crate::day4::Day4;
|
||||
use crate::day5::Day5;
|
||||
use crate::day_solver::DaySolver;
|
||||
use crate::util::read_file;
|
||||
|
||||
@@ -13,9 +14,10 @@ mod day1;
|
||||
mod day2;
|
||||
mod day3;
|
||||
mod day4;
|
||||
mod day5;
|
||||
mod day_solver;
|
||||
|
||||
const MAX_DAY: u8 = 4;
|
||||
const MAX_DAY: u8 = 5;
|
||||
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
|
||||
|
||||
fn main() {
|
||||
@@ -70,7 +72,7 @@ fn main() {
|
||||
avg_bench(&bench_results, |b| b.part1),
|
||||
avg_bench(&bench_results, |b| b.part2));
|
||||
} else {
|
||||
println!("Execution took {} μs (init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
|
||||
println!("Execution took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +92,7 @@ fn build_day_solver(day: u8, input: String) -> Option<Box<dyn DaySolver>> {
|
||||
2 => Some(Box::new(Day2::create(input))),
|
||||
3 => Some(Box::new(Day3::create(input))),
|
||||
4 => Some(Box::new(Day4::create(input))),
|
||||
5 => Some(Box::new(Day5::create(input))),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user