use std::fmt::Write; use std::str::FromStr; use anyhow::{bail, Context, Result}; const INPUT: &str = include_str!("../input/day06.txt"); pub fn run() -> Result { let mut res = String::with_capacity(128); writeln!(res, "part 1: {}", part1(INPUT)?)?; writeln!(res, "part 2: {}", part2(INPUT)?)?; Ok(res) } fn part1(input: &str) -> Result { let instructions = input .lines() .map(|line| { line.trim_end() .parse() .context("couldn't parse instruction: {}") }) .collect::>>()?; let mut grid: Vec> = vec![vec![false; 1000]; 1000]; for inst in instructions { match inst.action { Action::TurnOn => { for (x, y) in inst { grid[x][y] = true; } } Action::TurnOff => { for (x, y) in inst { grid[x][y] = false; } } Action::Toggle => { for (x, y) in inst { grid[x][y] = !grid[x][y]; } } } } Ok(grid .iter() .flat_map(|line| line.iter()) .filter(|v| **v) .count()) } fn part2(input: &str) -> Result { let instructions = input .lines() .map(|line| { line.trim_end() .parse() .context("couldn't parse instruction: {}") }) .collect::>>()?; let mut grid: Vec> = vec![vec![0; 1000]; 1000]; for inst in instructions { match inst.action { Action::TurnOn => { for (x, y) in inst { grid[x][y] = grid[x][y].saturating_add(1); } } Action::TurnOff => { for (x, y) in inst { grid[x][y] = grid[x][y].saturating_sub(1); } } Action::Toggle => { for (x, y) in inst { grid[x][y] = grid[x][y].saturating_add(2); } } } } Ok(grid.iter().flat_map(|line| line.iter()).sum()) } enum Action { TurnOn, TurnOff, Toggle, } struct Instruction { action: Action, horizontal: (usize, usize), vertical: (usize, usize), } impl IntoIterator for Instruction { type Item = (usize, usize); type IntoIter = Box>; fn into_iter(self) -> Self::IntoIter { let begin_x = self.horizontal.0; let end_x = self.horizontal.1; let begin_y = self.vertical.0; let end_y = self.vertical.1; let iter = (begin_x..=end_x).flat_map(move |i| (begin_y..=end_y).map(move |j| (i, j))); Box::new(iter) } } impl FromStr for Instruction { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let mut space = s .find(' ') .with_context(|| format!("couldn't parse instruction: {}", s))?; let action = if &s[..space] == "toggle" { Action::Toggle } else if &s[..space] == "turn" { space = s[(space + 1)..] .find(' ') .with_context(|| format!("couldn't parse instruction: {}", s))? + space + 1; if &s[..space] == "turn on" { Action::TurnOn } else if &s[..space] == "turn off" { Action::TurnOff } else { bail!("couldn't identify action: {}", &s[..space]); } } else { bail!("couldn't identify action: {}", s); }; let s = &s[(space + 1)..]; let comma = s .find(',') .with_context(|| format!("couldn't parse instruction: {}", s))?; let x = s[..comma].parse()?; let s = &s[(comma + 1)..]; let space = s .find(' ') .with_context(|| format!("couldn't parse instruction: {}", s))?; let y = s[..space].parse()?; let s = &s[(space + 1)..]; let begin = (x, y); let through = s .find("through ") .with_context(|| format!("couldn't parse instruction: {}", s))?; let s = &s[(through + 8)..]; let comma = s .find(',') .with_context(|| format!("couldn't parse instruction: {}", s))?; let x = s[..comma].parse()?; let s = &s[(comma + 1)..]; let y = s.parse()?; let end = (x, y); Ok(Instruction { action, horizontal: (begin.0, end.0), vertical: (begin.1, end.1), }) } } #[cfg(test)] mod tests { use super::*; #[test] fn part1_provided() { assert_eq!(part1("turn on 0,0 through 999,999").unwrap(), 1_000_000); assert_eq!(part1("toggle 0,0 through 999,0").unwrap(), 1_000); assert_eq!(part1("turn off 499,499 through 500,500").unwrap(), 0); } #[test] fn part1_real() { assert_eq!(part1(INPUT).unwrap(), 543903); } #[test] fn part2_provided() { assert_eq!(part2("turn on 0,0 through 0,0").unwrap(), 1); assert_eq!(part2("toggle 0,0 through 999,999").unwrap(), 2_000_000); } #[test] fn part2_real() { assert_eq!(part2(INPUT).unwrap(), 14687245); } }