summaryrefslogtreecommitdiff
path: root/2022/rust/day03
diff options
context:
space:
mode:
authorcos <cos>2022-12-05 20:54:23 +0000
committercos <cos>2022-12-05 21:22:56 +0000
commitf26a4b8a1f648842e4ce8676754a66e0189e41e1 (patch)
tree2a103e6572c29a20d213229f6e762dc948686993 /2022/rust/day03
parent002f3d163bd606434d5e73e1658caad29690f03b (diff)
downloadadventofcode-f26a4b8a1f648842e4ce8676754a66e0189e41e1.zip
Add day03, 2022
Diffstat (limited to '2022/rust/day03')
-rw-r--r--2022/rust/day03/Cargo.toml9
-rw-r--r--2022/rust/day03/src/main.rs103
2 files changed, 112 insertions, 0 deletions
diff --git a/2022/rust/day03/Cargo.toml b/2022/rust/day03/Cargo.toml
new file mode 100644
index 0000000..115d7fa
--- /dev/null
+++ b/2022/rust/day03/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "day03"
+version = "0.1.0"
+authors = ["cos <cos>"]
+edition = "2021"
+
+[dependencies]
+aoc = { path = "../../../common/rust/aoc" }
+anyhow = "1.0"
diff --git a/2022/rust/day03/src/main.rs b/2022/rust/day03/src/main.rs
new file mode 100644
index 0000000..a3e4fb3
--- /dev/null
+++ b/2022/rust/day03/src/main.rs
@@ -0,0 +1,103 @@
+use {
+ anyhow::{
+ anyhow,
+ Context,
+ Result,
+ },
+ std::{
+ env::args,
+ fs::File,
+ io::{
+ BufRead,
+ BufReader,
+ },
+ path::Path,
+ },
+};
+
+fn item_priority(item: char) -> Option<usize> {
+ match item {
+ 'a'..='z' => Some((item as u32 - 'a' as u32 + 1) as usize),
+ 'A'..='Z' => Some((item as u32 - 'A' as u32 + 27) as usize),
+ _ => None,
+ }
+}
+
+#[derive(Debug)]
+struct Rucksack {
+ compartments: Vec<String>,
+}
+
+impl Rucksack {
+ fn new(items: &str) -> Self {
+ let (first, second) = items.split_at(items.len()/2);
+ let compartments = vec![String::from(first), String::from(second)];
+ Self {
+ compartments,
+ }
+ }
+
+ fn contains(&self, needle: char) -> bool {
+ self.compartments[0].contains(needle) || self.compartments[1].contains(needle)
+ }
+
+ fn common_items(&self) -> String {
+ self.compartments[0].chars().filter(|item| self.compartments[1].contains(*item)).collect()
+ }
+
+ fn find_badge(&self, other: &Rucksack) -> String {
+ self.compartments[0].chars().chain(self.compartments[1].chars()).filter(|item|
+ other.contains(*item)).collect()
+ }
+}
+
+fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<Rucksack>> {
+ let reader = BufReader::new(File::open(filename)?);
+
+ reader.lines().map(
+ |v| {
+ let s = v?;
+ Ok(Rucksack::new(&s))
+ }
+ ).collect()
+}
+
+fn part1(input: &[Rucksack]) -> Result<usize> {
+ Ok(input.iter().filter_map(|rucksack| {
+ if let Some(item) = rucksack.common_items().chars().next() {
+ item_priority(item)
+ } else {
+ None
+ }
+ }).sum())
+}
+
+fn part2(input: &[Rucksack]) -> Result<usize> {
+ Ok(input.chunks(3).filter_map(|rucksacks| {
+ let some_candidates = rucksacks[0].find_badge(&rucksacks[1]);
+ let other_candidates = rucksacks[0].find_badge(&rucksacks[2]);
+ let common: String = some_candidates.chars().filter(|item|
+ other_candidates.contains(*item)).collect();
+ if let Some(item) = common.chars().next() {
+ item_priority(item)
+ } else {
+ None
+ }
+ }).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 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(&input).context("No solution for part 2")?;
+ println!("Part2, solution found to be: {}", solution);
+ }
+ Ok(())
+}