Add day 5
This commit is contained in:
1
2023/day5/.gitignore
vendored
Normal file
1
2023/day5/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
126
2023/day5/Cargo.lock
generated
Normal file
126
2023/day5/Cargo.lock
generated
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day5"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"indoc",
|
||||||
|
"nom",
|
||||||
|
"rayon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "either"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indoc"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.6.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
"rayon-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rayon-core"
|
||||||
|
version = "1.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
11
2023/day5/Cargo.toml
Normal file
11
2023/day5/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "day5"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
indoc = "2.0.4"
|
||||||
|
nom = "7.1.3"
|
||||||
|
rayon = "1.8.0"
|
||||||
322
2023/day5/src/main.rs
Normal file
322
2023/day5/src/main.rs
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
use nom::{
|
||||||
|
bytes::complete::{tag, take_until},
|
||||||
|
character::complete::{char, digit1, multispace0, multispace1},
|
||||||
|
combinator::map,
|
||||||
|
multi::{many1, separated_list1},
|
||||||
|
sequence::{preceded, separated_pair, terminated, tuple},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
fn number(i: &str) -> IResult<&str, usize> {
|
||||||
|
map(digit1, |f: &str| f.parse::<usize>().unwrap())(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spaces(i: &str) -> IResult<&str, Vec<char>> {
|
||||||
|
many1(char(' '))(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ident(i: &str) -> IResult<&str, &str> {
|
||||||
|
take_until(" ")(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Almanac<S: Seeds> {
|
||||||
|
seeds: Box<S>,
|
||||||
|
map_lists: Vec<MapList>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Seeds<Out = S>> Almanac<S> {
|
||||||
|
fn parse(s: &str) -> IResult<&str, Self> {
|
||||||
|
let map_lists = separated_list1(multispace1, MapList::parse);
|
||||||
|
let (rest, (seeds, map_lists)) = terminated(
|
||||||
|
separated_pair(<S as Seeds>::parse, multispace1, map_lists),
|
||||||
|
multispace0,
|
||||||
|
)(s)?;
|
||||||
|
Ok((rest, Self { seeds, map_lists }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SeedList(Vec<usize>);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SeedRanges(Vec<Range<usize>>);
|
||||||
|
|
||||||
|
trait Seeds {
|
||||||
|
type Out: Seeds;
|
||||||
|
fn parse(s: &str) -> IResult<&str, Box<Self::Out>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seeds for SeedList {
|
||||||
|
type Out = Self;
|
||||||
|
fn parse(s: &str) -> IResult<&str, Box<Self::Out>> {
|
||||||
|
let (rest, ids) = preceded(tag("seeds: "), separated_list1(char(' '), number))(s)?;
|
||||||
|
Ok((rest, Box::new(Self(ids))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Seeds for SeedRanges {
|
||||||
|
type Out = Self;
|
||||||
|
fn parse(s: &str) -> IResult<&str, Box<Self::Out>> {
|
||||||
|
let (rest, range) = preceded(
|
||||||
|
tag("seeds: "),
|
||||||
|
separated_list1(char(' '), separated_pair(number, spaces, number)),
|
||||||
|
)(s)?;
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Box::new(Self(
|
||||||
|
range
|
||||||
|
.into_iter()
|
||||||
|
.map(|(start, length)| start..start + length)
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Map {
|
||||||
|
source_range: Range<usize>,
|
||||||
|
destination_range: Range<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
fn parse(s: &str) -> IResult<&str, Self> {
|
||||||
|
let (rest, (destination_start, source_start, length)) = tuple((
|
||||||
|
terminated(number, spaces),
|
||||||
|
terminated(number, spaces),
|
||||||
|
number,
|
||||||
|
))(s)?;
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Self {
|
||||||
|
source_range: source_start..(source_start + length),
|
||||||
|
destination_range: destination_start..(destination_start + length),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map(&self, value: usize) -> Option<usize> {
|
||||||
|
if self.source_range.contains(&value) {
|
||||||
|
let index = value - self.source_range.start;
|
||||||
|
// `nth()` is implemented via a simple addition, there is no actual calling
|
||||||
|
// of `next()`
|
||||||
|
Some(self.destination_range.clone().nth(index).unwrap())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MapList {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
header: String,
|
||||||
|
maps: Vec<Map>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MapList {
|
||||||
|
fn parse(s: &str) -> IResult<&str, Self> {
|
||||||
|
let maps = separated_list1(multispace1, Map::parse);
|
||||||
|
let header = terminated(ident, tag(" map:"));
|
||||||
|
|
||||||
|
let (rest, (header, maps)) = separated_pair(header, multispace1, maps)(s)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
rest,
|
||||||
|
Self {
|
||||||
|
header: header.to_owned(),
|
||||||
|
maps,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map(&self, value: usize) -> usize {
|
||||||
|
for map in &self.maps {
|
||||||
|
if let Some(mapped_value) = map.map(value) {
|
||||||
|
return mapped_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> Result<usize, String> {
|
||||||
|
let (rest, almanac) = Almanac::<SeedList>::parse(input).map_err(|e| e.to_string())?;
|
||||||
|
if !rest.is_empty() {
|
||||||
|
println!("parsing rest found: {rest}");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
let mut lowest_location = usize::MAX;
|
||||||
|
for seed in &almanac.seeds.0 {
|
||||||
|
let mut mapped_value = *seed;
|
||||||
|
for map_list in &almanac.map_lists {
|
||||||
|
mapped_value = map_list.map(mapped_value);
|
||||||
|
}
|
||||||
|
if mapped_value < lowest_location {
|
||||||
|
lowest_location = mapped_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(lowest_location)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> Result<usize, String> {
|
||||||
|
let (rest, almanac) = Almanac::<SeedRanges>::parse(input).map_err(|e| e.to_string())?;
|
||||||
|
if !rest.is_empty() {
|
||||||
|
println!("parsing rest found: {rest}");
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
let lowest_location = almanac
|
||||||
|
.seeds
|
||||||
|
.0
|
||||||
|
.into_par_iter()
|
||||||
|
.map(|seed_range| {
|
||||||
|
println!("{seed_range:?}");
|
||||||
|
// let seeds = seed_range.collect::<Vec<usize>>();
|
||||||
|
let result = seed_range
|
||||||
|
.clone()
|
||||||
|
.map(|seed| {
|
||||||
|
let mut mapped_value = seed;
|
||||||
|
for map_list in &almanac.map_lists {
|
||||||
|
mapped_value = map_list.map(mapped_value);
|
||||||
|
}
|
||||||
|
mapped_value
|
||||||
|
})
|
||||||
|
.min()
|
||||||
|
.unwrap();
|
||||||
|
println!("{seed_range:?} => {result}");
|
||||||
|
result
|
||||||
|
})
|
||||||
|
.min()
|
||||||
|
.unwrap();
|
||||||
|
Ok(lowest_location)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), String> {
|
||||||
|
let input = include_str!("../input");
|
||||||
|
|
||||||
|
println!("Part 1 : {}", part1(input)?);
|
||||||
|
println!("Part 2 : {}", part2(input)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use indoc::indoc;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_01() {
|
||||||
|
let input = indoc! {"
|
||||||
|
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
|
||||||
|
"};
|
||||||
|
|
||||||
|
assert_eq!(part1(&input).unwrap(), 35);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_map() {
|
||||||
|
let input = indoc! {"
|
||||||
|
50 98 2
|
||||||
|
"};
|
||||||
|
|
||||||
|
let (_rest, map) = Map::parse(input).unwrap();
|
||||||
|
assert_eq!(map.map(97), None);
|
||||||
|
assert_eq!(map.map(98), Some(50));
|
||||||
|
assert_eq!(map.map(99), Some(51));
|
||||||
|
assert_eq!(map.map(100), None);
|
||||||
|
assert_eq!(map.map(101), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_map_list() {
|
||||||
|
let input = indoc! {"
|
||||||
|
seed-to-soil map:
|
||||||
|
50 98 2
|
||||||
|
52 50 48
|
||||||
|
"};
|
||||||
|
|
||||||
|
let (_rest, maplist) = MapList::parse(input).unwrap();
|
||||||
|
assert_eq!(maplist.map(79), 81);
|
||||||
|
assert_eq!(maplist.map(14), 14);
|
||||||
|
assert_eq!(maplist.map(55), 57);
|
||||||
|
assert_eq!(maplist.map(13), 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn example_02() {
|
||||||
|
let input = indoc! {"
|
||||||
|
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
|
||||||
|
"};
|
||||||
|
|
||||||
|
assert_eq!(part2(&input).unwrap(), 46);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user