day 5 finished
This commit is contained in:
@@ -90,6 +90,21 @@ fn parse_input(input: &str) -> IResult<&str, Vec<Card>> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
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
|
||||
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
|
||||
|
||||
@@ -1,18 +1,196 @@
|
||||
#![warn(clippy::all, clippy::pedantic)]
|
||||
|
||||
#[must_use] pub fn part1(_input: &str) -> String {
|
||||
"Not Finished".to_string()
|
||||
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: 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)]
|
||||
mod test {
|
||||
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]
|
||||
fn part1_works() {
|
||||
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)]
|
||||
|
||||
#[must_use] pub fn part2(_input: &str) -> String {
|
||||
"Not Finished".to_string()
|
||||
use core::ops::Range;
|
||||
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)]
|
||||
mod test {
|
||||
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]
|
||||
fn part2_works() {
|
||||
let result = part2(INPUT);
|
||||
assert_eq!(result, "Not Finished".to_string());
|
||||
assert_eq!(result, "46".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user