summaryrefslogtreecommitdiff
path: root/2021/rust/day02/src/main.rs
blob: d093dbad536b7166f04ff9a5b5ff8bf34a1b4669 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
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<T> = std::result::Result<T, CommandError>;

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<Self> {
        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<T: AsRef<Path>>(filename: T) -> Result<Vec<Command>> {
    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<Item = &'a Command>>(input: I) -> Result<Position> {
    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<Item = &'a Command>>(input: I) -> Result<Position> {
    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(())
}