summaryrefslogtreecommitdiff
path: root/2022/rust/day02/src/main.rs
blob: d98d7b1d282e37d149d607894b1ef262bd044825 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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<T: AsRef<Path>>(filename: T) -> Result<Vec<Game>> {
    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<usize> {
    Ok(input.iter().map(|game| game.score()).sum())
}

fn part2(input: &mut [Game]) -> Result<usize> {
    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(())
}