use nom::{ branch::alt, character::complete::{char as nom_char, line_ending}, combinator::{map, value}, multi::separated_list1, sequence::tuple, IResult, }; use std::fs; fn main() -> Result<(), Box> { let input = fs::read_to_string("inputs/day_2.txt")?; let game_log_part_1 = GameLog::parser(&input).unwrap().1; dbg!(game_log_part_1.total_score()); let game_log_part_2 = GameLog::parser_part_2(&input).unwrap().1; dbg!(game_log_part_2.total_score()); Ok(()) } #[derive(Debug, PartialEq, Eq, Clone)] struct GameLog { moves: Vec, } #[derive(Debug, PartialEq, Eq, Clone)] struct GameRound { opponent: Move, me: Move, } #[derive(Debug, PartialEq, Eq, Clone)] enum Move { Rock, Paper, Scissors, } #[derive(Debug, PartialEq, Eq, Clone)] enum GameResult { Win, Lose, Draw, } impl GameLog { fn parser(input: &str) -> IResult<&str, GameLog> { map(separated_list1(line_ending, GameRound::parser), |moves| { GameLog { moves } })(input) } fn parser_part_2(input: &str) -> IResult<&str, GameLog> { map( separated_list1(line_ending, GameRound::parser_part_2), |moves| GameLog { moves }, )(input) } fn total_score(&self) -> u32 { self.moves.iter().map(|m| m.score()).sum() } } impl GameRound { fn parser(input: &str) -> IResult<&str, GameRound> { map( tuple((Move::opponent_parser, nom_char(' '), Move::player_parser)), |(opponent, _, me)| GameRound { opponent, me }, )(input) } fn parser_part_2(input: &str) -> IResult<&str, GameRound> { map( tuple((Move::opponent_parser, nom_char(' '), GameResult::parser)), |(opponent, _, result)| { use GameResult::*; use Move::*; let me = match opponent { Rock => match result { Win => Paper, Draw => Rock, Lose => Scissors, }, Paper => match result { Win => Scissors, Draw => Paper, Lose => Rock, }, Scissors => match result { Win => Rock, Draw => Scissors, Lose => Paper, }, }; GameRound { opponent, me } }, )(input) } fn score(&self) -> u32 { let victory_points = match self.me.beats(&self.opponent) { GameResult::Lose => 0, GameResult::Draw => 3, GameResult::Win => 6, }; let throw_points = match self.me { Move::Rock => 1, Move::Paper => 2, Move::Scissors => 3, }; victory_points + throw_points } } impl Move { fn opponent_parser(input: &str) -> IResult<&str, Move> { alt(( value(Move::Rock, nom_char('A')), value(Move::Paper, nom_char('B')), value(Move::Scissors, nom_char('C')), ))(input) } fn player_parser(input: &str) -> IResult<&str, Move> { alt(( value(Move::Rock, nom_char('X')), value(Move::Paper, nom_char('Y')), value(Move::Scissors, nom_char('Z')), ))(input) } fn beats(&self, other: &Move) -> GameResult { use GameResult::*; use Move::*; match self { Rock => match other { Rock => Draw, Paper => Lose, Scissors => Win, }, Paper => match other { Rock => Win, Paper => Draw, Scissors => Lose, }, Scissors => match other { Rock => Lose, Paper => Win, Scissors => Draw, }, } } } impl GameResult { fn parser(input: &str) -> IResult<&str, GameResult> { alt(( value(GameResult::Lose, nom_char('X')), value(GameResult::Draw, nom_char('Y')), value(GameResult::Win, nom_char('Z')), ))(input) } }