use { anyhow::{ anyhow, Context, Result, }, std::{ env::args, fs::File, io::{ BufRead, BufReader, }, path::Path, }, }; #[derive(Clone,Copy)] enum Play { Rock = 1, Paper = 2, Scissors = 3, } impl Play { fn strategy(&self, desired: Outcome) -> Play { match (self, desired) { (Play::Rock, Outcome::Lose) => Play::Scissors, (Play::Rock, Outcome::Draw) => Play::Rock, (Play::Rock, Outcome::Win) => Play::Paper, (Play::Paper, Outcome::Lose) => Play::Rock, (Play::Paper, Outcome::Draw) => Play::Paper, (Play::Paper, Outcome::Win) => Play::Scissors, (Play::Scissors, Outcome::Lose) => Play::Paper, (Play::Scissors, Outcome::Draw) => Play::Scissors, (Play::Scissors, Outcome::Win) => Play::Rock, } } } #[derive(Clone,Copy)] enum Outcome { Lose = 0, Draw = 3, Win = 6, } struct Game { opponent: Play, play: Play, outcome: Outcome, } impl Game { fn outcome(&self) -> Outcome { match (&self.play, &self.opponent) { (Play::Rock, Play::Rock) => Outcome::Draw, (Play::Rock, Play::Paper) => Outcome::Lose, (Play::Rock, Play::Scissors) => Outcome::Win, (Play::Paper, Play::Rock) => Outcome::Win, (Play::Paper, Play::Paper) => Outcome::Draw, (Play::Paper, Play::Scissors) => Outcome::Lose, (Play::Scissors, Play::Rock) => Outcome::Lose, (Play::Scissors, Play::Paper) => Outcome::Win, (Play::Scissors, Play::Scissors) => Outcome::Draw, } } fn update(&mut self) { self.play = self.opponent.strategy(self.outcome); } fn score(&self) -> usize { self.play as usize + self.outcome() as usize } } fn read_input>(filename: T) -> Result> { let reader = BufReader::new(File::open(filename)?); reader.lines().map( |v| { let s = v?; let fields: Vec<_> = s.split(' ').collect(); let opponent = match *(fields.first().ok_or_else(|| anyhow!("Parse error"))?) { "A" => Ok(Play::Rock), "B" => Ok(Play::Paper), "C" => Ok(Play::Scissors), _ => Err(anyhow!("Invalid input")), }?; let ( play, outcome ) = match *(fields.get(1).ok_or_else(|| anyhow!("Parse error"))?) { "X" => Ok((Play::Rock, Outcome::Lose)), "Y" => Ok((Play::Paper, Outcome::Draw)), "Z" => Ok((Play::Scissors, Outcome::Win)), _ => Err(anyhow!("Invalid input")), }?; Ok(Game { opponent, play, outcome }) } ).collect() } fn part1(input: &[Game]) -> Result { Ok(input.iter().map(|game| game.score()).sum()) } fn part2(input: &mut [Game]) -> Result { Ok(input.iter_mut().map(|game| { game.update(); game.score() }).sum()) } fn main() -> Result<()> { let ( do_part_1, do_part_2 ) = aoc::do_parts(); let filename = args().nth(1).ok_or_else(|| anyhow!("Missing input filename"))?; let mut input = read_input(filename).context("Could not read input")?; if do_part_1 { let solution = part1(&input).context("No solution for part 1")?; println!("Part1, solution found to be: {}", solution); } if do_part_2 { let solution = part2(&mut input).context("No solution for part 2")?; println!("Part2, solution found to be: {}", solution); } Ok(()) }