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