struct Puzzle {
operands: Vec<Vec<u64>>,
- operators: Vec<char>,
+ operators: Vec<(char, usize)>, // store the idx of the operator (for part2)
+ value_col: Vec<(u64, usize)>,
}
impl Puzzle {
fn new(input: &str) -> Self {
- let mut operands: Vec<Vec<u64>> = Vec::new();
- let mut operators: Vec<char> = Vec::new();
+ let mut operands = Vec::new();
+ let mut operators = Vec::new();
let mut it = input.lines().peekable();
while let Some(line) = it.next() {
if it.peek().is_none() { // last line
- operators = line.split_whitespace()
- .map(|word| word.chars().next().unwrap())
+ operators = line.chars()
+ .enumerate()
+ .filter(|(_, c)| *c != ' ')
+ .map(|(i, c)| (c, i))
.collect();
} else {
line.split_whitespace()
});
}
}
- Puzzle { operands, operators }
+
+ // extract the value in columns with the index of the left-most digit
+ let mut value_col = Vec::new();
+ let chars: Vec<Vec<char>> = input.lines()
+ .map(|line| {
+ line.chars().collect()
+ })
+ .collect();
+ for i in 0..chars[0].len() {
+ let mut value = String::new();
+ for j in 0..(chars.len()-1) { // do not read the last line (the operators)
+ value.push(chars[j][i]);
+ }
+ if !value.trim().is_empty() {
+ value_col.push((value.trim().parse::<u64>().unwrap(), i));
+ }
+ }
+
+ Puzzle { operands, operators, value_col }
}
fn part1(&self) -> u64 {
- self.operators.iter().enumerate().map(|(i, op)| {
+ self.operators.iter().map(|(c, _)| c).enumerate().map(|(i, op)| {
let res: u64 = match op {
'+' => self.operands[i].iter().sum(),
'*' => self.operands[i].iter().product(),
})
.sum()
}
+
+ // in this part, read the operands in column
+ // [
+ // 123 -> 1, 2, 3
+ // 45 -> 0, 4, 5
+ // 6 -> 0, 0, 6
+ // ]
+ // -> [ 1, 24, 356 ]
+ fn part2(&self) -> u64 {
+ // consume the operators and operands in reverse order (from right to left)
+ // so we can rely on the idx of the operator
+ let mut res: u64 = 0;
+ let mut it_operator = self.operators.iter().rev();
+ let mut it_operand = self.value_col.iter().rev().peekable();
+ while let Some(op) = it_operator.next() {
+ let mut tmp: u64 = match op.0 {
+ '+' => 0,
+ '*' => 1,
+ _ => unreachable!(),
+ };
+ while let Some(value) = it_operand.next() {
+ match op.0 {
+ '+' => { tmp += value.0; },
+ '*' => { tmp *= value.0; },
+ _ => unreachable!(),
+ };
+ if it_operand.peek().is_some() && it_operand.peek().unwrap().1 < op.1 {
+ break;
+ }
+ }
+ res += tmp;
+ }
+ res
+ }
}
pub fn run(input: &str) -> Result<(), Box<dyn Error>> {
let p = Puzzle::new(input);
println!("part1: {}", p.part1());
+ println!("part2: {}", p.part2());
Ok(())
}
fn test_part1() {
assert_eq!(4277556, Puzzle::new(TEST_INPUT).part1());
}
+ #[test]
+ fn test_part2() {
+ assert_eq!(3263827, Puzzle::new(TEST_INPUT).part2());
+ }
}