day 5 finished
This commit is contained in:
@@ -90,6 +90,21 @@ fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use rstest::rstest;
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
#[case("Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53", Some(8))]
|
||||||
|
#[case("Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19", Some(2))]
|
||||||
|
#[case("Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1", Some(2))]
|
||||||
|
#[case("Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83", Some(1))]
|
||||||
|
#[case("Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36", None)]
|
||||||
|
#[case("Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11", None)]
|
||||||
|
fn line_test(#[case] line: &str, #[case] expected: Option<usize>) {
|
||||||
|
let (input, card) = parse_card(line).expect("card should be parsed");
|
||||||
|
assert_eq!(input, "");
|
||||||
|
assert_eq!(card.get_score(), expected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
const INPUT: &str = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
const INPUT: &str = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
|
||||||
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||||
|
|||||||
@@ -1,18 +1,196 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
#[must_use] pub fn part1(_input: &str) -> String {
|
use nom::{
|
||||||
"Not Finished".to_string()
|
bytes::complete::tag,
|
||||||
|
character::complete,
|
||||||
|
combinator::opt,
|
||||||
|
multi::separated_list1,
|
||||||
|
sequence::{separated_pair, terminated, tuple},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct ParseTypeError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
enum Type {
|
||||||
|
Seed,
|
||||||
|
Soil,
|
||||||
|
Fertilizer,
|
||||||
|
Water,
|
||||||
|
Light,
|
||||||
|
Temperature,
|
||||||
|
Humidity,
|
||||||
|
Location,
|
||||||
|
}
|
||||||
|
impl FromStr for Type {
|
||||||
|
type Err = ParseTypeError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"seed" => Ok(Self::Seed),
|
||||||
|
"soil" => Ok(Self::Soil),
|
||||||
|
"fertilizer" => Ok(Self::Fertilizer),
|
||||||
|
"water" => Ok(Self::Water),
|
||||||
|
"light" => Ok(Self::Light),
|
||||||
|
"temperature" => Ok(Self::Temperature),
|
||||||
|
"humidity" => Ok(Self::Humidity),
|
||||||
|
"location" => Ok(Self::Location),
|
||||||
|
_ => Err(ParseTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ItemMapEntry {
|
||||||
|
pub to: u64,
|
||||||
|
pub from: u64,
|
||||||
|
pub count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemMapEntry {
|
||||||
|
fn to_out(&self, from: u64) -> Option<u64> {
|
||||||
|
if from < self.from || self.from + self.count < from {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.to + (from - self.from))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ItemMap {
|
||||||
|
pub from_type: Type,
|
||||||
|
pub to_type: Type,
|
||||||
|
pub mapping: Vec<ItemMapEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemMap {
|
||||||
|
fn map(&self, from: u64) -> u64 {
|
||||||
|
self.mapping
|
||||||
|
.iter()
|
||||||
|
.find_map(|x| x.to_out(from))
|
||||||
|
.or(Some(from))
|
||||||
|
.expect("always")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn part1(input: &str) -> String {
|
||||||
|
let (_input, (mut to_process, maps)) = parse_input(input).expect("aoc always has input");
|
||||||
|
//println!("{_input}");
|
||||||
|
let mut from_type = Type::Seed;
|
||||||
|
while from_type != Type::Location {
|
||||||
|
let current_map = maps
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.from_type == from_type)
|
||||||
|
.expect("should always find");
|
||||||
|
to_process = to_process
|
||||||
|
.iter()
|
||||||
|
.map(|x| current_map.map(*x))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
//println!("{to_process:#?}");
|
||||||
|
from_type = current_map.to_type;
|
||||||
|
}
|
||||||
|
//println!("{to_process:#?}");
|
||||||
|
to_process
|
||||||
|
.iter()
|
||||||
|
.min()
|
||||||
|
.expect("always a number")
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_item_map_entry(input: &str) -> IResult<&str, ItemMapEntry> {
|
||||||
|
let (input, to) = complete::u64(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
let (input, from) = complete::u64(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
let (input, count) = complete::u64(input)?;
|
||||||
|
Ok((input, ItemMapEntry { to, from, count }))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_to_from(input: &str) -> IResult<&str, (Type, Type)> {
|
||||||
|
let (input, (to_type, from_type)) =
|
||||||
|
separated_pair(complete::alpha1, tag("-to-"), complete::alpha1)(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
(
|
||||||
|
to_type.parse().expect("there will be a to type"),
|
||||||
|
from_type.parse().expect("there will be a from type"),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map(input: &str) -> IResult<&str, ItemMap> {
|
||||||
|
let (input, (from_type, to_type)) =
|
||||||
|
terminated(parse_to_from, tuple((complete::space1, tag("map:"))))(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
let (input, mapping) = separated_list1(complete::line_ending, parse_item_map_entry)(input)?;
|
||||||
|
let (input, _) = opt(complete::line_ending)(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ItemMap {
|
||||||
|
from_type,
|
||||||
|
to_type,
|
||||||
|
mapping,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_seeds(input: &str) -> IResult<&str, Vec<u64>> {
|
||||||
|
let (input, _) = tag("seeds:")(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
separated_list1(complete::space1, complete::u64)(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, (Vec<u64>, Vec<ItemMap>)> {
|
||||||
|
let (input, seeds) = terminated(parse_seeds, complete::line_ending)(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
let (input, maps) = separated_list1(complete::line_ending, parse_map)(input)?;
|
||||||
|
Ok((input, (seeds, maps)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const INPUT: &str = "";
|
const INPUT: &str = "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";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part1_works() {
|
fn part1_works() {
|
||||||
let result = part1(INPUT);
|
let result = part1(INPUT);
|
||||||
assert_eq!(result, "Not Finished".to_string());
|
assert_eq!(result, "35".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,277 @@
|
|||||||
#![warn(clippy::all, clippy::pedantic)]
|
#![warn(clippy::all, clippy::pedantic)]
|
||||||
|
|
||||||
#[must_use] pub fn part2(_input: &str) -> String {
|
use core::ops::Range;
|
||||||
"Not Finished".to_string()
|
use itertools::Itertools;
|
||||||
|
use nom::{
|
||||||
|
bytes::complete::tag,
|
||||||
|
character::complete,
|
||||||
|
combinator::opt,
|
||||||
|
multi::separated_list1,
|
||||||
|
sequence::{separated_pair, terminated, tuple},
|
||||||
|
IResult,
|
||||||
|
};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
struct ParseTypeError;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
enum Type {
|
||||||
|
Seed,
|
||||||
|
Soil,
|
||||||
|
Fertilizer,
|
||||||
|
Water,
|
||||||
|
Light,
|
||||||
|
Temperature,
|
||||||
|
Humidity,
|
||||||
|
Location,
|
||||||
|
}
|
||||||
|
impl FromStr for Type {
|
||||||
|
type Err = ParseTypeError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"seed" => Ok(Self::Seed),
|
||||||
|
"soil" => Ok(Self::Soil),
|
||||||
|
"fertilizer" => Ok(Self::Fertilizer),
|
||||||
|
"water" => Ok(Self::Water),
|
||||||
|
"light" => Ok(Self::Light),
|
||||||
|
"temperature" => Ok(Self::Temperature),
|
||||||
|
"humidity" => Ok(Self::Humidity),
|
||||||
|
"location" => Ok(Self::Location),
|
||||||
|
_ => Err(ParseTypeError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ItemMapEntry {
|
||||||
|
pub to: Range<u64>,
|
||||||
|
pub from: Range<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ItemMap {
|
||||||
|
pub from_type: Type,
|
||||||
|
pub to_type: Type,
|
||||||
|
pub mapping: Vec<ItemMapEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemMap {
|
||||||
|
fn input_to_output(&self, input: &Range<u64>) -> Vec<Range<u64>> {
|
||||||
|
/*if let Some(within) = self
|
||||||
|
.mapping
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.from.contains(&input.start) && x.from.contains(&input.end))
|
||||||
|
{
|
||||||
|
//fully contained
|
||||||
|
let offset = input.start - within.from.start;
|
||||||
|
let to_start = within.to.start + offset;
|
||||||
|
let to_end = to_start + u64::abs_diff(input.end, input.start);
|
||||||
|
if to_start == 0 {
|
||||||
|
println!("{input:#?}");
|
||||||
|
}
|
||||||
|
return vec![to_start..to_end];
|
||||||
|
}*/
|
||||||
|
let mut output = Vec::new();
|
||||||
|
let mut input = input.start..input.end;
|
||||||
|
loop {
|
||||||
|
input =
|
||||||
|
if let Some(within) = self.mapping.iter().find(|x| x.from.contains(&input.start)) {
|
||||||
|
//println!("front - {input:?} - {within:#?} - {:?}", self.from_type);
|
||||||
|
let (to_start, to_end) = if within.to.start > within.from.start {
|
||||||
|
let offset = within.to.start - within.from.start;
|
||||||
|
let end = if input.end + offset > within.to.end {
|
||||||
|
within.to.end
|
||||||
|
} else {
|
||||||
|
input.end + offset
|
||||||
|
};
|
||||||
|
(input.start + offset, end)
|
||||||
|
} else {
|
||||||
|
let offset = within.from.start - within.to.start;
|
||||||
|
let end = if input.end - offset > within.to.end {
|
||||||
|
within.to.end
|
||||||
|
} else {
|
||||||
|
input.end - offset
|
||||||
|
};
|
||||||
|
(input.start - offset, end)
|
||||||
|
};
|
||||||
|
output.push(to_start..to_end);
|
||||||
|
if input.end <= within.from.end {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
within.from.end..input.end
|
||||||
|
} else if let Some(within) = self
|
||||||
|
.mapping
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.from.contains(&(input.end - 1)))
|
||||||
|
{
|
||||||
|
//println!("end - {input:?} - {within:#?} - {:?}", self.from_type);
|
||||||
|
let (to_start, to_end) = if within.to.start > within.from.start {
|
||||||
|
let offset = within.to.start - within.from.start;
|
||||||
|
let start = if input.start + offset < within.to.start {
|
||||||
|
within.to.start
|
||||||
|
} else {
|
||||||
|
input.start + offset
|
||||||
|
};
|
||||||
|
(start, input.end + offset)
|
||||||
|
} else {
|
||||||
|
let offset = within.from.start - within.to.start;
|
||||||
|
let start = if input.start + offset < within.to.start {
|
||||||
|
within.to.start
|
||||||
|
} else {
|
||||||
|
input.start + offset
|
||||||
|
};
|
||||||
|
(start, input.end - offset)
|
||||||
|
};
|
||||||
|
output.push(to_start..to_end);
|
||||||
|
if input.start >= within.from.start {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
input.start..within.from.start
|
||||||
|
} else {
|
||||||
|
//println!("else - {input:#?} - {:?}", self.from_type);
|
||||||
|
output.push(input.clone());
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn part2(input: &str) -> String {
|
||||||
|
let (_input, (mut to_process, maps)) = parse_input(input).expect("aoc always has input");
|
||||||
|
//println!("{_input}");
|
||||||
|
let mut from_type = Type::Seed;
|
||||||
|
while from_type != Type::Location {
|
||||||
|
let current_map = maps
|
||||||
|
.iter()
|
||||||
|
.find(|x| x.from_type == from_type)
|
||||||
|
.expect("should always find");
|
||||||
|
to_process = to_process
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| current_map.input_to_output(x))
|
||||||
|
.unique()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
//println!("{to_process:#?}");
|
||||||
|
from_type = current_map.to_type;
|
||||||
|
}
|
||||||
|
//println!("{to_process:#?}");
|
||||||
|
to_process
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.start)
|
||||||
|
.min()
|
||||||
|
.expect("always a number")
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_item_map_entry(input: &str) -> IResult<&str, ItemMapEntry> {
|
||||||
|
let (input, to) = complete::u64(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
let (input, from) = complete::u64(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
let (input, count) = complete::u64(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ItemMapEntry {
|
||||||
|
to: to..(to + count),
|
||||||
|
from: from..(from + count),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_to_from(input: &str) -> IResult<&str, (Type, Type)> {
|
||||||
|
let (input, (to_type, from_type)) =
|
||||||
|
separated_pair(complete::alpha1, tag("-to-"), complete::alpha1)(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
(
|
||||||
|
to_type.parse().expect("there will be a to type"),
|
||||||
|
from_type.parse().expect("there will be a from type"),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_map(input: &str) -> IResult<&str, ItemMap> {
|
||||||
|
let (input, (from_type, to_type)) =
|
||||||
|
terminated(parse_to_from, tuple((complete::space1, tag("map:"))))(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
let (input, mapping) = separated_list1(complete::line_ending, parse_item_map_entry)(input)?;
|
||||||
|
let (input, _) = opt(complete::line_ending)(input)?;
|
||||||
|
Ok((
|
||||||
|
input,
|
||||||
|
ItemMap {
|
||||||
|
from_type,
|
||||||
|
to_type,
|
||||||
|
mapping,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_seed_range(input: &str) -> IResult<&str, Range<u64>> {
|
||||||
|
let (input, (seed, count)) =
|
||||||
|
separated_pair(complete::u64, complete::space1, complete::u64)(input)?;
|
||||||
|
Ok((input, seed..(seed + count)))
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO need to change so that it operates on the ranges and not on the actual numbers
|
||||||
|
|
||||||
|
fn parse_seeds(input: &str) -> IResult<&str, Vec<Range<u64>>> {
|
||||||
|
let (input, _) = tag("seeds:")(input)?;
|
||||||
|
let (input, _) = complete::space1(input)?;
|
||||||
|
separated_list1(complete::space1, parse_seed_range)(input)
|
||||||
|
//println!("{seed_ranges:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_input(input: &str) -> IResult<&str, (Vec<Range<u64>>, Vec<ItemMap>)> {
|
||||||
|
let (input, seeds) = terminated(parse_seeds, complete::line_ending)(input)?;
|
||||||
|
let (input, _) = complete::line_ending(input)?;
|
||||||
|
let (input, maps) = separated_list1(complete::line_ending, parse_map)(input)?;
|
||||||
|
//println!("{seeds:?}");
|
||||||
|
Ok((input, (seeds, maps)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const INPUT: &str = "";
|
const INPUT: &str = "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";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn part2_works() {
|
fn part2_works() {
|
||||||
let result = part2(INPUT);
|
let result = part2(INPUT);
|
||||||
assert_eq!(result, "Not Finished".to_string());
|
assert_eq!(result, "46".to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user