Implemented input as direct value or as file input
Implemented parsing for day5
This commit is contained in:
parent
0bc8636003
commit
cb7e8465a2
15 changed files with 567 additions and 8 deletions
55
Cargo.lock
generated
55
Cargo.lock
generated
|
@ -9,6 +9,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"insta",
|
||||||
"log",
|
"log",
|
||||||
"regex",
|
"regex",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -118,6 +119,18 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.15.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -149,6 +162,12 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "env_filter"
|
name = "env_filter"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -184,12 +203,42 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "insta"
|
||||||
|
version = "1.39.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"lazy_static",
|
||||||
|
"linked-hash-map",
|
||||||
|
"similar",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "is_terminal_polyfill"
|
||||||
version = "1.70.1"
|
version = "1.70.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.156"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a5f43f184355eefb8d17fc948dbecf6c13be3c141f20d834ae842193a448c72a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linked-hash-map"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.22"
|
version = "0.4.22"
|
||||||
|
@ -249,6 +298,12 @@ version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
|
|
|
@ -10,3 +10,6 @@ clap = { version = "4.5.15", features = ["derive"] }
|
||||||
env_logger = "0.11.5"
|
env_logger = "0.11.5"
|
||||||
log = "0.4.22"
|
log = "0.4.22"
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
insta = "1.39.0"
|
||||||
|
|
60
\
Normal file
60
\
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::{fs, io, path::Path, process::ExitCode};
|
||||||
|
|
||||||
|
use advent_of_code_2023::{cli::AppCliArguments, solutions};
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
fn main() -> ExitCode {
|
||||||
|
let args = AppCliArguments::parse();
|
||||||
|
let solution = solve_given(&args);
|
||||||
|
match solution {
|
||||||
|
Ok(found_solution) => {
|
||||||
|
println!("{}", found_solution);
|
||||||
|
ExitCode::SUCCESS
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
eprintln!("{}", error);
|
||||||
|
ExitCode::FAILURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve_given(args: &AppCliArguments) -> Result<String, CouldNotSolveError> {
|
||||||
|
let all_solutions = solutions::create_solutions();
|
||||||
|
|
||||||
|
let found_task = {
|
||||||
|
let day: u32 = args.day().into();
|
||||||
|
let task: u32 = args.task().into();
|
||||||
|
let found_day = all_solutions
|
||||||
|
.get(day.saturating_sub(1) as usize)
|
||||||
|
.ok_or_else(|| CouldNotSolveError::DayNotFound(day))?;
|
||||||
|
found_day
|
||||||
|
.get(task.saturating_sub(1) as usize)
|
||||||
|
.ok_or_else(|| CouldNotSolveError::TaskNotFound { day, task })
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let solved = (found_task)(args.input());
|
||||||
|
Ok(solved)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_read_from_file_if_demanded(args: &AppCliArguments) -> io::Result<String> {
|
||||||
|
let content = if args.read_as_file() {
|
||||||
|
let path = Path::new(args.input());
|
||||||
|
let input_as_file = fs::read_to_string(path)?;
|
||||||
|
input_as_file
|
||||||
|
} else {
|
||||||
|
args.input().to_string()
|
||||||
|
};
|
||||||
|
Ok(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Error)]
|
||||||
|
enum CouldNotSolveError {
|
||||||
|
#[error("There is no solution for the day {0}")]
|
||||||
|
DayNotFound(u32),
|
||||||
|
#[error("There is not solution for task {task} under the day {day}")]
|
||||||
|
TaskNotFound { day: u32, task: u32 },
|
||||||
|
#[error("Could not read puzzel input from the given file\n {0}")]
|
||||||
|
CouldNotReadFromFile(#[from] io::Error),
|
||||||
|
}
|
0
file_input/.gitkeep
Normal file
0
file_input/.gitkeep
Normal file
33
file_input/day5_example_input.txt
Normal file
33
file_input/day5_example_input.txt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4
|
|
@ -28,4 +28,8 @@ impl AppCliArguments {
|
||||||
pub fn input(&self) -> &str {
|
pub fn input(&self) -> &str {
|
||||||
&self.input
|
&self.input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_as_file(&self) -> bool {
|
||||||
|
self.read_as_file
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod parsing_utils;
|
||||||
pub mod solutions;
|
pub mod solutions;
|
||||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -1,4 +1,4 @@
|
||||||
use std::process::ExitCode;
|
use std::{fs, io, path::Path, process::ExitCode};
|
||||||
|
|
||||||
use advent_of_code_2023::{cli::AppCliArguments, solutions};
|
use advent_of_code_2023::{cli::AppCliArguments, solutions};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
@ -20,7 +20,7 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn solve_given(args: &AppCliArguments) -> Result<String, NoSolutionFound> {
|
fn solve_given(args: &AppCliArguments) -> Result<String, CouldNotSolveError> {
|
||||||
let all_solutions = solutions::create_solutions();
|
let all_solutions = solutions::create_solutions();
|
||||||
|
|
||||||
let found_task = {
|
let found_task = {
|
||||||
|
@ -28,20 +28,34 @@ fn solve_given(args: &AppCliArguments) -> Result<String, NoSolutionFound> {
|
||||||
let task: u32 = args.task().into();
|
let task: u32 = args.task().into();
|
||||||
let found_day = all_solutions
|
let found_day = all_solutions
|
||||||
.get(day.saturating_sub(1) as usize)
|
.get(day.saturating_sub(1) as usize)
|
||||||
.ok_or_else(|| NoSolutionFound::DayNotFound(day))?;
|
.ok_or_else(|| CouldNotSolveError::DayNotFound(day))?;
|
||||||
found_day
|
found_day
|
||||||
.get(task.saturating_sub(1) as usize)
|
.get(task.saturating_sub(1) as usize)
|
||||||
.ok_or_else(|| NoSolutionFound::TaskNotFound { day, task })
|
.ok_or_else(|| CouldNotSolveError::TaskNotFound { day, task })
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let solved = (found_task)(args.input());
|
let puzzel_input = try_read_from_file_if_demanded(args)?;
|
||||||
|
let solved = (found_task)(&puzzel_input);
|
||||||
Ok(solved)
|
Ok(solved)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Error)]
|
fn try_read_from_file_if_demanded(args: &AppCliArguments) -> io::Result<String> {
|
||||||
enum NoSolutionFound {
|
let content = if args.read_as_file() {
|
||||||
|
let path = Path::new(args.input());
|
||||||
|
let input_as_file = fs::read_to_string(path)?;
|
||||||
|
input_as_file
|
||||||
|
} else {
|
||||||
|
args.input().to_string()
|
||||||
|
};
|
||||||
|
Ok(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum CouldNotSolveError {
|
||||||
#[error("There is no solution for the day {0}")]
|
#[error("There is no solution for the day {0}")]
|
||||||
DayNotFound(u32),
|
DayNotFound(u32),
|
||||||
#[error("There is not solution for task {task} under the day {day}")]
|
#[error("There is not solution for task {task} under the day {day}")]
|
||||||
TaskNotFound { day: u32, task: u32 },
|
TaskNotFound { day: u32, task: u32 },
|
||||||
|
#[error("Could not read puzzel input from the given file\n {0}")]
|
||||||
|
CouldNotReadFromFile(#[from] io::Error),
|
||||||
}
|
}
|
||||||
|
|
56
src/parsing_utils.rs
Normal file
56
src/parsing_utils.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
pub fn blocks_of_lines_seperated_by<'a>(
|
||||||
|
input: &'a str,
|
||||||
|
on_skip: impl Fn(&'a str) -> bool,
|
||||||
|
) -> impl Iterator<Item = Vec<&'a str>> {
|
||||||
|
let mut current_block = Vec::new();
|
||||||
|
let mut lines = input.lines();
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
while let Some(next_line) = lines.next() {
|
||||||
|
if on_skip(next_line) {
|
||||||
|
if !current_block.is_empty() {
|
||||||
|
let to_return = std::mem::take(&mut current_block);
|
||||||
|
return Some(to_return);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current_block.push(next_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !current_block.is_empty() {
|
||||||
|
let to_return = std::mem::take(&mut current_block);
|
||||||
|
return Some(to_return);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn blocks_of_lines_seperated_by_empty_lines<'a>(
|
||||||
|
input: &'a str,
|
||||||
|
) -> impl Iterator<Item = Vec<&'a str>> {
|
||||||
|
blocks_of_lines_seperated_by(input, |line| line.trim().is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod testing {
|
||||||
|
use std::vec;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_blocks_by_empty_lines() {
|
||||||
|
const INPUT: &str = "
|
||||||
|
1st
|
||||||
|
2st
|
||||||
|
|
||||||
|
|
||||||
|
3st
|
||||||
|
|
||||||
|
4st
|
||||||
|
|
||||||
|
5st
|
||||||
|
";
|
||||||
|
let expected = vec![vec!["1st", "2st"], vec!["3st"], vec!["4st"], vec!["5st"]];
|
||||||
|
let actual: Vec<Vec<&str>> =
|
||||||
|
blocks_of_lines_seperated_by(INPUT, |line| line.trim().is_empty()).collect();
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,36 @@
|
||||||
|
use mapping_layer::MappingLayer;
|
||||||
|
|
||||||
|
mod mapping_layer;
|
||||||
|
mod parsing;
|
||||||
|
mod range_mapping;
|
||||||
pub fn solve_task_1(input: &str) -> String {
|
pub fn solve_task_1(input: &str) -> String {
|
||||||
|
let _parsed = parsing::parse(input);
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(input: &str) -> ! {
|
fn location_of_one_point(seeds: u32, layers: &[MappingLayer]) -> u32 {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod testing {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// Seed 79, soil 81, fertilizer 81, water 81, light 74, temperature 78, humidity 78, location 82.
|
||||||
|
// Seed 14, soil 14, fertilizer 53, water 49, light 42, temperature 42, humidity 43, location 43.
|
||||||
|
// Seed 55, soil 57, fertilizer 57, water 53, light 46, temperature 82, humidity 82, location 86.
|
||||||
|
// Seed 13, soil 13, fertilizer 52, water 41, light 34, temperature 34, humidity 35, location 35.
|
||||||
|
#[test]
|
||||||
|
fn name() {
|
||||||
|
fn assert_case(seeds: u32, layer: &[MappingLayer], expected: u32) {
|
||||||
|
let actual = location_of_one_point(seeds, layer);
|
||||||
|
assert_eq!(expected, actual, "Given seeds: {}", seeds);
|
||||||
|
}
|
||||||
|
let input = parsing::parse(include_str!("day5/day5_example_input.txt"));
|
||||||
|
let layers = &input.1;
|
||||||
|
assert_case(79, layers, 82);
|
||||||
|
assert_case(14, layers, 43);
|
||||||
|
assert_case(55, layers, 86);
|
||||||
|
assert_case(13, layers, 35);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
33
src/solutions/day5/day5_example_input.txt
Normal file
33
src/solutions/day5/day5_example_input.txt
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
seeds: 79 14 55 13
|
||||||
|
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
|
||||||
|
soil-to-fertilizer map:
|
||||||
|
0 15 37
|
||||||
|
37 52 2
|
||||||
|
39 0 15
|
||||||
|
|
||||||
|
fertilizer-to-water map:
|
||||||
|
49 53 8
|
||||||
|
0 11 42
|
||||||
|
42 0 7
|
||||||
|
57 7 4
|
||||||
|
|
||||||
|
water-to-light map:
|
||||||
|
88 18 7
|
||||||
|
18 25 70
|
||||||
|
|
||||||
|
light-to-temperature map:
|
||||||
|
45 77 23
|
||||||
|
81 45 19
|
||||||
|
68 64 13
|
||||||
|
|
||||||
|
temperature-to-humidity map:
|
||||||
|
0 69 1
|
||||||
|
1 0 69
|
||||||
|
|
||||||
|
humidity-to-location map:
|
||||||
|
60 56 37
|
||||||
|
56 93 4
|
19
src/solutions/day5/mapping_layer.rs
Normal file
19
src/solutions/day5/mapping_layer.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use derive_more::derive;
|
||||||
|
|
||||||
|
use super::range_mapping::RangeMapping;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MappingLayer {
|
||||||
|
label: String,
|
||||||
|
ranges: Vec<RangeMapping>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MappingLayer {
|
||||||
|
pub fn new(label: impl Into<String>, mut ranges: Vec<RangeMapping>) -> Self {
|
||||||
|
ranges.sort_by_key(|element| element.source());
|
||||||
|
Self {
|
||||||
|
label: label.into(),
|
||||||
|
ranges,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/solutions/day5/parsing.rs
Normal file
45
src/solutions/day5/parsing.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use crate::{parsing_utils, solutions::day5::range_mapping::RangeMapping};
|
||||||
|
|
||||||
|
use super::mapping_layer::MappingLayer;
|
||||||
|
|
||||||
|
pub fn parse(input: &str) -> (Vec<u32>, Vec<MappingLayer>) {
|
||||||
|
fn parse_one_layer(lines: Vec<&str>) -> MappingLayer {
|
||||||
|
const ALREADY_GOT_MAPPING_NAME: usize = 1;
|
||||||
|
let mapping_name = *lines.first().unwrap();
|
||||||
|
let mut mappings = Vec::new();
|
||||||
|
|
||||||
|
for next_line in lines.into_iter().skip(ALREADY_GOT_MAPPING_NAME) {
|
||||||
|
let next_mapping: RangeMapping = next_line.parse().unwrap();
|
||||||
|
mappings.push(next_mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
MappingLayer::new(mapping_name.to_string(), mappings)
|
||||||
|
}
|
||||||
|
let mut blocks = parsing_utils::blocks_of_lines_seperated_by_empty_lines(input);
|
||||||
|
let first_block = blocks.next().unwrap();
|
||||||
|
|
||||||
|
assert!(first_block.len() == 1);
|
||||||
|
let seeds = first_block
|
||||||
|
.first()
|
||||||
|
.unwrap()
|
||||||
|
.trim_start_matches("seeds: ")
|
||||||
|
.split(" ")
|
||||||
|
.map(|to_number| to_number.parse().unwrap())
|
||||||
|
.collect();
|
||||||
|
let mappings = blocks.into_iter().map(parse_one_layer).collect();
|
||||||
|
|
||||||
|
(seeds, mappings)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod testing {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_input_day5() {
|
||||||
|
let input = include_str!("day5_example_input.txt");
|
||||||
|
let actual = super::parse(input);
|
||||||
|
let expected_seeds: Vec<u32> = vec![79, 14, 55, 13];
|
||||||
|
assert_eq!(expected_seeds, actual.0);
|
||||||
|
insta::assert_debug_snapshot!(actual.1);
|
||||||
|
}
|
||||||
|
}
|
76
src/solutions/day5/range_mapping.rs
Normal file
76
src/solutions/day5/range_mapping.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct RangeMapping {
|
||||||
|
source: u32,
|
||||||
|
target: u32,
|
||||||
|
range: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum ParseErrorRangeMapping {
|
||||||
|
#[error("Needs to have at least 3 numbers")]
|
||||||
|
LessThan3Numbers,
|
||||||
|
#[error("Every item must be a valid unsigned number")]
|
||||||
|
InvalidFormatForNumbers,
|
||||||
|
}
|
||||||
|
impl FromStr for RangeMapping {
|
||||||
|
type Err = ParseErrorRangeMapping;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut numbers_as_3 = s.split(" ").take(3).map(|unparsed| {
|
||||||
|
unparsed
|
||||||
|
.parse::<u32>()
|
||||||
|
.map_err(|_| ParseErrorRangeMapping::InvalidFormatForNumbers)
|
||||||
|
});
|
||||||
|
let (target, source, range) = (
|
||||||
|
numbers_as_3
|
||||||
|
.next()
|
||||||
|
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
|
||||||
|
numbers_as_3
|
||||||
|
.next()
|
||||||
|
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
|
||||||
|
numbers_as_3
|
||||||
|
.next()
|
||||||
|
.ok_or(ParseErrorRangeMapping::LessThan3Numbers)??,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
target,
|
||||||
|
source,
|
||||||
|
range,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RangeMapping {
|
||||||
|
pub fn source(&self) -> u32 {
|
||||||
|
self.source
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target(&self) -> u32 {
|
||||||
|
self.target
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> u32 {
|
||||||
|
self.range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(test)]
|
||||||
|
mod testing {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_from_line() {
|
||||||
|
const INPUT: &str = "50 98 2";
|
||||||
|
let expected = RangeMapping {
|
||||||
|
source: 98,
|
||||||
|
target: 50,
|
||||||
|
range: 2,
|
||||||
|
};
|
||||||
|
let actual: RangeMapping = INPUT.parse().unwrap();
|
||||||
|
assert_eq!(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
---
|
||||||
|
source: src/solutions/day5/parsing.rs
|
||||||
|
expression: actual.1
|
||||||
|
---
|
||||||
|
[
|
||||||
|
MappingLayer {
|
||||||
|
label: "seed-to-soil map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 50,
|
||||||
|
target: 52,
|
||||||
|
range: 48,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 98,
|
||||||
|
target: 50,
|
||||||
|
range: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "soil-to-fertilizer map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 0,
|
||||||
|
target: 39,
|
||||||
|
range: 15,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 15,
|
||||||
|
target: 0,
|
||||||
|
range: 37,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 52,
|
||||||
|
target: 37,
|
||||||
|
range: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "fertilizer-to-water map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 0,
|
||||||
|
target: 42,
|
||||||
|
range: 7,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 7,
|
||||||
|
target: 57,
|
||||||
|
range: 4,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 11,
|
||||||
|
target: 0,
|
||||||
|
range: 42,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 53,
|
||||||
|
target: 49,
|
||||||
|
range: 8,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "water-to-light map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 18,
|
||||||
|
target: 88,
|
||||||
|
range: 7,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 25,
|
||||||
|
target: 18,
|
||||||
|
range: 70,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "light-to-temperature map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 45,
|
||||||
|
target: 81,
|
||||||
|
range: 19,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 64,
|
||||||
|
target: 68,
|
||||||
|
range: 13,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 77,
|
||||||
|
target: 45,
|
||||||
|
range: 23,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "temperature-to-humidity map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 0,
|
||||||
|
target: 1,
|
||||||
|
range: 69,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 69,
|
||||||
|
target: 0,
|
||||||
|
range: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
MappingLayer {
|
||||||
|
label: "humidity-to-location map:",
|
||||||
|
ranges: [
|
||||||
|
RangeMapping {
|
||||||
|
source: 56,
|
||||||
|
target: 60,
|
||||||
|
range: 37,
|
||||||
|
},
|
||||||
|
RangeMapping {
|
||||||
|
source: 93,
|
||||||
|
target: 56,
|
||||||
|
range: 4,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
Loading…
Add table
Reference in a new issue