summaryrefslogtreecommitdiff
path: root/2021/rust/day02/src/main.rs
diff options
context:
space:
mode:
authorcos <cos>2021-12-02 10:37:10 +0100
committercos <cos>2021-12-02 10:41:51 +0100
commit8d207663b8c561e27e1dd50a7523ed6b62c8e1cd (patch)
treede17623979a2a1e6a6e8d5c5d0bedd24aac26cf8 /2021/rust/day02/src/main.rs
parentf4400c6410f4b932eb738760996e0dcc271157f1 (diff)
downloadadventofcode-8d207663b8c561e27e1dd50a7523ed6b62c8e1cd.zip
Add day02, 2021
Diffstat (limited to '2021/rust/day02/src/main.rs')
-rw-r--r--2021/rust/day02/src/main.rs140
1 files changed, 140 insertions, 0 deletions
diff --git a/2021/rust/day02/src/main.rs b/2021/rust/day02/src/main.rs
new file mode 100644
index 0000000..4178ecc
--- /dev/null
+++ b/2021/rust/day02/src/main.rs
@@ -0,0 +1,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(input: &[Command]) -> 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(input: &[Command]) -> 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(())
+}