diff --git a/src/bin/06.rs b/src/bin/06.rs new file mode 100644 index 0000000..e133062 --- /dev/null +++ b/src/bin/06.rs @@ -0,0 +1,106 @@ +use anyhow::bail; +use aoc::*; +use itertools::Itertools; +use std::str::FromStr; + +const INPUT: &str = include_str!("../../input/06"); + +fn main() { + println!("Part 1: {}", part1(INPUT)); + println!("Part 2: {}", part2(INPUT)); +} + +#[test] +fn example() { + assert_example!(part1, "06-test", 4277556); + assert_example!(part2, "06-test", 3263827); +} + +fn part1(input: &str) -> usize { + let (numbers, ops) = parse_input1(input); + let num_problems = numbers[0].len(); + let mut grand_total = 0; + + for problem in 0..num_problems { + let op = ops[problem]; + grand_total += numbers + .iter() + .map(|row| row[problem]) + .reduce(|a, b| op.calc(a, b)) + .unwrap(); + } + + grand_total +} + +fn parse_input1(input: &str) -> (Vec>, Vec) { + let mut rows = Vec::new(); + let mut ops = Vec::new(); + + for line in input.lines() { + if line.contains('+') { + ops = parse_ws_separated(line).collect(); + } else { + rows.push(parse_ws_separated(line).collect()); + } + } + + (rows, ops) +} + +fn part2(input: &str) -> usize { + let lines = input + .lines() + .map(|line| line.chars().collect_vec()) + .collect_vec(); + let lines = transpose(lines) + .into_iter() + .map(|row| row.into_iter().join("").replace(' ', "").to_owned()) + .collect_vec(); + lines.split(|row| row.is_empty()).map(solve_problem).sum() +} + +fn solve_problem(problem: &[String]) -> usize { + let mut problem = problem.iter(); + let first_line = problem.next().unwrap(); + + let (n, op): (usize, Op) = if let Some(n) = first_line.strip_suffix('+') { + (n.parse().unwrap(), Op::Add) + } else if let Some(n) = first_line.strip_suffix('*') { + (n.parse().unwrap(), Op::Mul) + } else { + panic!("first line invalid format: '{first_line}'"); + }; + + problem.fold(n, move |acc, row| { + let n = row.parse().unwrap(); + op.calc(acc, n) + }) +} + +#[derive(Debug, Clone, Copy)] +enum Op { + Add, + Mul, +} + +impl Op { + fn calc(&self, a: usize, b: usize) -> usize { + match self { + Op::Add => a + b, + Op::Mul => a * b, + } + } +} + +impl FromStr for Op { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "+" => Ok(Op::Add), + "*" => Ok(Op::Mul), + _ => bail!("invalid op: '{}'", s), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2520767..a35acb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,3 +127,30 @@ pub const DIRECTIONS8: [IVec2; 8] = [ IVec2::new(0, -1), IVec2::new(1, -1), ]; + +/// Transposes a Vec of Vecs. +/// +/// ```rust +/// # use aoc::transpose; +/// let input = vec![vec![1, 2, 3], vec![4, 5, 6]]; +/// let expected = vec![vec![1, 4], vec![2, 5], vec![3, 6]]; +/// assert_eq!(transpose(input), expected); +/// ``` +/// +/// https://stackoverflow.com/a/64499219 +pub fn transpose(v: Vec>) -> Vec> { + if v.is_empty() { + return v; + } + + let len = v[0].len(); + let mut iters: Vec<_> = v.into_iter().map(|n| n.into_iter()).collect(); + (0..len) + .map(|_| { + iters + .iter_mut() + .map(|n| n.next().unwrap()) + .collect::>() + }) + .collect() +}