use { anyhow::{ anyhow, Context, Result, }, std::{ env::args, fmt::{ Debug, Display, }, fs::File, io::{ BufRead, BufReader, }, path::Path, str::FromStr, }, }; struct Position { x: isize, y: isize, } impl Display for Position { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "(x: {}, y: {})", self.x, self.y) } } #[derive(PartialEq)] enum Command { Down(usize), Forward(usize), Up(usize), } struct CommandError { s: String, } impl CommandError { fn new(s: &str) -> Self { Self { s: String::from(s), } } } type CommandResult = std::result::Result; impl Display for CommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "invalid command string: {}", self.s) } } impl Debug for CommandError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "invalid command string: {}", self.s) } } impl FromStr for Command { type Err = CommandError; fn from_str(s: &str) -> CommandResult { let (direction, steps) = s.split_once(' ').ok_or_else(|| CommandError::new(s))?; let count = steps.parse().map_err(|_| CommandError::new(s))?; if direction == "up" { Ok(Command::Up(count)) } else if direction == "down" { Ok(Command::Down(count)) } else if direction == "forward" { Ok(Command::Forward(count)) } else { Err(CommandError::new(s)) } } } fn read_input>(filename: T) -> Result> { let reader = BufReader::new(File::open(filename)?); reader.lines().map( |v| v?.parse().map_err(|err| anyhow!("Could not parse input: {}", err)) ).collect() } fn part1<'a, I: IntoIterator>(input: I) -> Result { let mut position = Position { x: 0, y:0 }; for command in input { match command { Command::Up(steps) => position.y -= *steps as isize, Command::Down(steps) => position.y += *steps as isize, Command::Forward(steps) => position.x += *steps as isize, } } Ok(position) } fn part2<'a, I: IntoIterator>(input: I) -> Result { let mut position = Position { x: 0, y:0 }; let mut aim = 0; for command in input { match command { Command::Up(steps) => aim -= *steps as isize, Command::Down(steps) => aim += *steps as isize, Command::Forward(steps) => { position.x += *steps as isize; position.y += aim * *steps as isize; }, } } Ok(position) } fn main() -> Result<()> { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = args().nth(1).ok_or(anyhow!("Missing input filename"))?; let input = read_input(filename).context("Could not read input")?; if do_part_1 { let position = part1(&input).context("No solution for part 1")?; let solution = position.x * position.y; println!("Part1, resulting position: {}. Door answer: {}", position, solution); } if do_part_2 { let position = part2(&input).context("No solution for part 1")?; let solution = position.x * position.y; println!("Part2, resulting position: {}. Door answer: {}", position, solution); } Ok(()) }