Add day 5

This commit is contained in:
2023-12-05 11:22:37 +01:00
committed by Hannes Körber
parent 7f72deb7cb
commit f4b8d4108c
4 changed files with 460 additions and 0 deletions

1
2023/day5/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

126
2023/day5/Cargo.lock generated Normal file
View 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
View 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
View 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);
}
}