[TASK] Solved Day 5

This commit is contained in:
2023-12-05 21:15:47 +01:00
parent ead47e232e
commit b3cfc775e4
7 changed files with 449 additions and 15 deletions

View File

@@ -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());
}

View File

@@ -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
View 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());
}

View File

@@ -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());
}

View File

@@ -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
}
}