Advent of Code solutions in Rust

feat: day 18 of AoC 2020

+362 -3
+346
aoc_2020/src/day18.rs
··· 1 + use anyhow::{anyhow, bail}; 2 + use aoc_companion::prelude::*; 3 + use aoc_utils::iter::IterUtils as _; 4 + use itertools::Itertools; 5 + 6 + pub(crate) struct Door { 7 + expressions: Vec<Vec<Token>>, 8 + } 9 + 10 + impl<'input> Solution<'input> for Door { 11 + fn parse(input: &'input str) -> Result<Self> { 12 + input 13 + .lines() 14 + .map(|line| tokenize(line).try_collect()) 15 + .try_collect() 16 + .map(|expressions| Door { expressions }) 17 + } 18 + 19 + fn part1(&self) -> Result<u64> { 20 + self.expressions 21 + .iter() 22 + .map(parse_part1) 23 + .map_ok(|ast| ast.eval()) 24 + .try_sum() 25 + } 26 + 27 + fn part2(&self) -> Result<u64> { 28 + self.expressions 29 + .iter() 30 + .map(parse_part2) 31 + .map_ok(|ast| ast.eval()) 32 + .try_sum() 33 + } 34 + } 35 + 36 + #[derive(Clone, Copy, Debug, PartialEq, Eq)] 37 + enum Token { 38 + LParen, 39 + RParen, 40 + Plus, 41 + Times, 42 + Number(u64), 43 + } 44 + 45 + fn tokenize(expr: &str) -> impl Iterator<Item = Result<Token>> { 46 + expr.bytes() 47 + .filter(|b| !b.is_ascii_whitespace()) 48 + .map(|b| match b { 49 + b'(' => Ok(Token::LParen), 50 + b')' => Ok(Token::RParen), 51 + b'+' => Ok(Token::Plus), 52 + b'*' => Ok(Token::Times), 53 + b'0'..=b'9' => Ok(Token::Number((b - b'0') as u64)), 54 + 0..128 => Err(anyhow!( 55 + "invalid token {:?}", 56 + char::from_u32(b as u32).unwrap() 57 + )), 58 + _ => Err(anyhow!("invalid token: non-ASCII character")), 59 + }) 60 + } 61 + 62 + #[derive(Clone, Debug, PartialEq, Eq)] 63 + enum Ast { 64 + Number(u64), 65 + Add(Box<Ast>, Box<Ast>), 66 + Mul(Box<Ast>, Box<Ast>), 67 + } 68 + 69 + impl Ast { 70 + fn eval(&self) -> u64 { 71 + match self { 72 + Ast::Number(n) => *n, 73 + Ast::Add(lhs, rhs) => lhs.eval() + rhs.eval(), 74 + Ast::Mul(lhs, rhs) => lhs.eval() * rhs.eval(), 75 + } 76 + } 77 + } 78 + 79 + fn parse_part1<'a, I: IntoIterator<Item = &'a Token>>(tokens: I) -> Result<Ast> { 80 + fn do_parse<'a, I: Iterator<Item = &'a Token>>(tokens: &mut I) -> Result<Ast> { 81 + let parse_operand = |tokens: &mut I| -> Result<Ast> { 82 + match tokens.next() { 83 + Some(Token::LParen) => do_parse(tokens), 84 + Some(Token::Number(n)) => Ok(Ast::Number(*n)), 85 + Some(Token::Plus) | Some(Token::Times) => { 86 + Err(anyhow!("expected a number or expression, got an operator")) 87 + } 88 + Some(Token::RParen) | None => Err(anyhow!("unexpected EOL or ')'")), 89 + } 90 + }; 91 + let mut ast = parse_operand(tokens)?; 92 + loop { 93 + ast = match tokens.next() { 94 + Some(Token::Plus) => Ast::Add(Box::new(ast), Box::new(parse_operand(tokens)?)), 95 + Some(Token::Times) => Ast::Mul(Box::new(ast), Box::new(parse_operand(tokens)?)), 96 + Some(Token::RParen) | None => return Ok(ast), 97 + _ => bail!("unexpected token"), 98 + } 99 + } 100 + } 101 + do_parse(&mut tokens.into_iter()) 102 + } 103 + 104 + fn parse_part2<'a, I: IntoIterator<Item = &'a Token>>(tokens: I) -> Result<Ast> { 105 + fn do_parse<'a, I: Iterator<Item = &'a Token>>(tokens: &mut I) -> Result<Ast> { 106 + enum Parens { 107 + Yes(Ast), 108 + No(Ast), 109 + } 110 + 111 + let parse_operand = |tokens: &mut I| -> Result<Ast> { 112 + match tokens.next() { 113 + Some(Token::LParen) => do_parse(tokens), 114 + Some(Token::Number(n)) => Ok(Ast::Number(*n)), 115 + Some(Token::Plus) | Some(Token::Times) => { 116 + Err(anyhow!("expected a number or expression, got an operator")) 117 + } 118 + Some(Token::RParen) | None => Err(anyhow!("unexpected EOL or ')'")), 119 + } 120 + }; 121 + let mut ast = Parens::Yes(parse_operand(tokens)?); 122 + loop { 123 + ast = match (ast, tokens.next()) { 124 + (Parens::No(Ast::Mul(lhs, rhs)), Some(Token::Plus)) => Parens::No(Ast::Mul( 125 + lhs, 126 + Box::new(Ast::Add(rhs, Box::new(parse_operand(tokens)?))), 127 + )), 128 + (Parens::Yes(ast) | Parens::No(ast), Some(Token::Plus)) => { 129 + Parens::No(Ast::Add(Box::new(ast), Box::new(parse_operand(tokens)?))) 130 + } 131 + (Parens::Yes(ast) | Parens::No(ast), Some(Token::Times)) => { 132 + Parens::No(Ast::Mul(Box::new(ast), Box::new(parse_operand(tokens)?))) 133 + } 134 + (Parens::Yes(ast) | Parens::No(ast), Some(Token::RParen) | None) => return Ok(ast), 135 + _ => bail!("unexpected token"), 136 + } 137 + } 138 + } 139 + do_parse(&mut tokens.into_iter()) 140 + } 141 + 142 + #[cfg(test)] 143 + mod tests { 144 + use super::*; 145 + 146 + const EXAMPLE_INPUTS: &[&str] = &[ 147 + "1 + 2 * 3 + 4 * 5 + 6", 148 + "1 + (2 * 3) + (4 * (5 + 6))", 149 + "2 * 3 + (4 * 5)", 150 + "5 + (8 * 3 + 9 + 3 * 4 * 3)", 151 + "5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))", 152 + "((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2", 153 + ]; 154 + 155 + macro_rules! ast { 156 + ($n:literal) => { 157 + Ast::Number($n) 158 + }; 159 + (($lhs:tt + $rhs:tt)) => { 160 + Ast::Add(Box::new(ast!($lhs)), Box::new(ast!($rhs))) 161 + }; 162 + (($lhs:tt * $rhs:tt)) => { 163 + Ast::Mul(Box::new(ast!($lhs)), Box::new(ast!($rhs))) 164 + }; 165 + } 166 + 167 + fn example_asts_part1() -> Vec<Ast> { 168 + vec![ 169 + ast!((((((1 + 2) * 3) + 4) * 5) + 6)), 170 + ast!(((1 + (2 * 3)) + (4 * (5 + 6)))), 171 + ast!(((2 * 3) + (4 * 5))), 172 + ast!((5 + (((((8 * 3) + 9) + 3) * 4) * 3))), 173 + ast!(((5 * 9) * (((((7 * 3) * 3) + 9) * 3) + ((8 + 6) * 4)))), 174 + ast!((((((((2 + 4) * 9) * (((6 + 9) * 8) + 6)) + 6) + 2) + 4) * 2)), 175 + ] 176 + } 177 + fn example_asts_part2() -> Vec<Ast> { 178 + vec![ 179 + ast!((((1 + 2) * (3 + 4)) * (5 + 6))), 180 + ast!(((1 + (2 * 3)) + (4 * (5 + 6)))), 181 + ast!((2 * (3 + (4 * 5)))), 182 + ast!((5 + (((8 * ((3 + 9) + 3)) * 4) * 3))), 183 + ast!(((5 * 9) * (((7 * 3) * (3 + 9)) * (3 + ((8 + 6) * 4))))), 184 + ast!(((((((2 + 4) * 9) * (((6 + 9) * (8 + 6)) + 6)) + 2) + 4) * 2)), 185 + ] 186 + } 187 + 188 + const EXAMPLE_EXPRESSIONS: &[&[Token]] = &[ 189 + &[ 190 + Token::Number(1), 191 + Token::Plus, 192 + Token::Number(2), 193 + Token::Times, 194 + Token::Number(3), 195 + Token::Plus, 196 + Token::Number(4), 197 + Token::Times, 198 + Token::Number(5), 199 + Token::Plus, 200 + Token::Number(6), 201 + ], 202 + &[ 203 + Token::Number(1), 204 + Token::Plus, 205 + Token::LParen, 206 + Token::Number(2), 207 + Token::Times, 208 + Token::Number(3), 209 + Token::RParen, 210 + Token::Plus, 211 + Token::LParen, 212 + Token::Number(4), 213 + Token::Times, 214 + Token::LParen, 215 + Token::Number(5), 216 + Token::Plus, 217 + Token::Number(6), 218 + Token::RParen, 219 + Token::RParen, 220 + ], 221 + &[ 222 + Token::Number(2), 223 + Token::Times, 224 + Token::Number(3), 225 + Token::Plus, 226 + Token::LParen, 227 + Token::Number(4), 228 + Token::Times, 229 + Token::Number(5), 230 + Token::RParen, 231 + ], 232 + &[ 233 + Token::Number(5), 234 + Token::Plus, 235 + Token::LParen, 236 + Token::Number(8), 237 + Token::Times, 238 + Token::Number(3), 239 + Token::Plus, 240 + Token::Number(9), 241 + Token::Plus, 242 + Token::Number(3), 243 + Token::Times, 244 + Token::Number(4), 245 + Token::Times, 246 + Token::Number(3), 247 + Token::RParen, 248 + ], 249 + &[ 250 + Token::Number(5), 251 + Token::Times, 252 + Token::Number(9), 253 + Token::Times, 254 + Token::LParen, 255 + Token::Number(7), 256 + Token::Times, 257 + Token::Number(3), 258 + Token::Times, 259 + Token::Number(3), 260 + Token::Plus, 261 + Token::Number(9), 262 + Token::Times, 263 + Token::Number(3), 264 + Token::Plus, 265 + Token::LParen, 266 + Token::Number(8), 267 + Token::Plus, 268 + Token::Number(6), 269 + Token::Times, 270 + Token::Number(4), 271 + Token::RParen, 272 + Token::RParen, 273 + ], 274 + &[ 275 + Token::LParen, 276 + Token::LParen, 277 + Token::Number(2), 278 + Token::Plus, 279 + Token::Number(4), 280 + Token::Times, 281 + Token::Number(9), 282 + Token::RParen, 283 + Token::Times, 284 + Token::LParen, 285 + Token::Number(6), 286 + Token::Plus, 287 + Token::Number(9), 288 + Token::Times, 289 + Token::Number(8), 290 + Token::Plus, 291 + Token::Number(6), 292 + Token::RParen, 293 + Token::Plus, 294 + Token::Number(6), 295 + Token::RParen, 296 + Token::Plus, 297 + Token::Number(2), 298 + Token::Plus, 299 + Token::Number(4), 300 + Token::Times, 301 + Token::Number(2), 302 + ], 303 + ]; 304 + 305 + #[test] 306 + fn example_expressions_are_tokenized() { 307 + itertools::assert_equal( 308 + EXAMPLE_INPUTS 309 + .iter() 310 + .map(|line| -> Vec<Token> { tokenize(line).try_collect().unwrap() }), 311 + EXAMPLE_EXPRESSIONS.iter().cloned(), 312 + ); 313 + } 314 + 315 + #[test] 316 + fn example_expressions_are_parsed_for_part1() { 317 + itertools::assert_equal( 318 + EXAMPLE_EXPRESSIONS 319 + .iter() 320 + .map(|tokens| parse_part1(*tokens).unwrap()), 321 + example_asts_part1(), 322 + ); 323 + } 324 + 325 + #[test] 326 + fn example_expressions_are_parsed_for_part2() { 327 + itertools::assert_equal( 328 + EXAMPLE_EXPRESSIONS 329 + .iter() 330 + .map(|tokens| parse_part2(*tokens).unwrap()), 331 + example_asts_part2(), 332 + ); 333 + } 334 + 335 + #[test] 336 + fn evaluate_example_expressions() { 337 + itertools::assert_equal( 338 + example_asts_part1().iter().map(Ast::eval), 339 + [71, 51, 26, 437, 12240, 13632], 340 + ); 341 + itertools::assert_equal( 342 + example_asts_part2().iter().map(Ast::eval), 343 + [231, 51, 46, 1445, 669060, 23340], 344 + ); 345 + } 346 + }
+2 -2
aoc_2020/src/main.rs
··· 17 17 mod day15; 18 18 mod day16; 19 19 mod day17; 20 - // mod day18; 20 + mod day18; 21 21 // mod day19; 22 22 // mod day20; 23 23 // mod day21; ··· 48 48 door!(2020-12-15 ~> day15), 49 49 door!(2020-12-16 ~> day16), 50 50 door!(2020-12-17 ~> day17), 51 - // door!(2020-12-18 ~> day18), 51 + door!(2020-12-18 ~> day18), 52 52 // door!(2020-12-19 ~> day19), 53 53 // door!(2020-12-20 ~> day20), 54 54 // door!(2020-12-21 ~> day21),
+14 -1
aoc_utils/src/iter.rs
··· 1 - use std::{iter::FusedIterator, rc::Rc}; 1 + use std::{ 2 + iter::{FusedIterator, Sum}, 3 + rc::Rc, 4 + }; 2 5 3 6 pub trait IterUtils: Iterator { 7 + fn try_sum<T, E>(mut self) -> Result<T, E> 8 + where 9 + Self: Sized + Iterator<Item = Result<T, E>>, 10 + T: Sum, 11 + { 12 + self.try_fold(std::iter::empty::<T>().sum(), |acc, val| { 13 + Ok(AtMostTwo::two(acc, val?).sum()) 14 + }) 15 + } 16 + 4 17 fn try_unzip<A, B, E, FromA, FromB>(mut self) -> Result<(FromA, FromB), E> 5 18 where 6 19 Self: Sized + Iterator<Item = Result<(A, B), E>>,