use std::fs; use std::env; use nom::IResult; use nom::multi::separated_list0; use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::i32; use nom::character::complete::space1; use nom::character::complete::space0; use nom::combinator::value; use nom::sequence::tuple; use nom::sequence::preceded; use nom::sequence::separated_pair; use nom::sequence::terminated; #[derive(Clone, Debug, PartialEq, Eq)] enum Color { Red, Green, Blue, } #[derive(Clone, Debug, PartialEq, Eq)] struct Draw { red_count: i32, green_count: i32, blue_count: i32, } #[derive(Clone, Debug, PartialEq, Eq)] struct Game { id: i32, draws: Vec, } fn parse_color(input: &str) -> IResult<&str, Color> { alt(( value(Color::Red, tag("red")), value(Color::Green, tag("green")), value(Color::Blue, tag("blue")), ))(input) } fn parse_game_id(input: &str) -> IResult<&str, i32> { terminated( preceded( tuple((space0, tag("Game"),space1)), i32, ), space0, )(input) } fn parse_number_of_cubes(input: &str) -> IResult<&str, (i32, Color)> { preceded( space0, separated_pair( i32, space1, parse_color, ) )(input) } fn parse_draw(input: &str) -> IResult<&str, Vec<(i32, Color)>> { separated_list0( tag(","), parse_number_of_cubes )(input) } fn parse_game_draws(input: &str) -> IResult<&str, Vec>> { separated_list0( tag(";"), parse_draw )(input) } fn parse_input_line(input: &str) -> IResult<&str, (i32, Vec>)> { separated_pair( parse_game_id, tag(":"), parse_game_draws, )(input) } fn create_game_from_data(data: (i32, Vec>)) -> Game { let (id, draws_data) = data; let mut draws: Vec = Vec::new(); for draw_data in draws_data { let mut draw = Draw{ red_count: 0, green_count: 0, blue_count: 0, }; for color_count in draw_data { let (count, color) = color_count; match color { Color::Red => draw.red_count += count, Color::Green => draw.green_count += count, Color::Blue => draw.blue_count += count, } } draws.push(draw); } Game{ id, draws, } } fn parse_game(input: &str) -> Result { match parse_input_line(input) { Ok((rest, data)) => if rest == "" { Ok(create_game_from_data(data)) } else { Err(format!("Incomplete parse, remaining: {}", rest)) }, Err(error) => Err(error.to_string()), } } fn main() { let mut result = 0; let args: Vec = env::args().collect(); let file_path = &args[1]; let input_data = fs::read_to_string(file_path).unwrap_or_else( |_|{panic!("Can't read file {}", file_path)}); for line in input_data.lines() { let game = parse_game(line).unwrap_or_else(|e|panic!("Failed to parse line: {}", e)); let mut max_red = 0; let mut max_green = 0; let mut max_blue = 0; for draw in &game.draws { if draw.red_count > max_red { max_red = draw.red_count; } if draw.green_count > max_green { max_green = draw.green_count; } if draw.blue_count > max_blue { max_blue = draw.blue_count; } } result += max_red * max_green * max_blue; } println!("{}", result); }