diff options
author | cos <cos> | 2020-12-05 09:51:50 +0100 |
---|---|---|
committer | cos <cos> | 2020-12-05 11:13:09 +0100 |
commit | 59290646080f5f1af417ba276355bd4fb7c49c20 (patch) | |
tree | 0b81705fd27b5263481400125199aaa58ac0b6d6 /2020/rust/day05/src/main.rs | |
parent | 6dab82d7ebd2edea2a1220cfbaff977528c6aaca (diff) | |
download | adventofcode-59290646080f5f1af417ba276355bd4fb7c49c20.zip |
Add day05, 2020
Diffstat (limited to '2020/rust/day05/src/main.rs')
-rw-r--r-- | 2020/rust/day05/src/main.rs | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/2020/rust/day05/src/main.rs b/2020/rust/day05/src/main.rs new file mode 100644 index 0000000..4d6072a --- /dev/null +++ b/2020/rust/day05/src/main.rs @@ -0,0 +1,135 @@ +use anyhow::{anyhow, Result}; +use std::env::args; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::ops::RangeInclusive; +use std::path::Path; +use thiserror::Error; + +#[derive(Error, Debug)] +enum BinarySpaceError { + #[error("Space is not fully reduced")] + NotReduced, +} + +struct BinarySpace { + start: usize, + end: usize, +} + +impl BinarySpace { + fn new(range: RangeInclusive<usize>) -> BinarySpace + // FIXME It would be nice with a cleaner interface. The intention was to use something like the + // RangeBounds trait, but it seems to require support of Unbounded ranges, which this + // BinarySpace will not be able to work with. + { + BinarySpace { + start: *range.start(), + end: *range.end(), + } + } + + fn size(&self) -> usize { + self.end - self.start + } + + fn split(&mut self, op: char) { + let size = self.size() + 1; + let half = size / 2; + match op { + 'F' | 'L' => self.end -= half, + 'B' | 'R' => self.start += half, + c => panic!("Invalid op to BinarySpace.split(): {}", c), + } + } + + fn value(&self) -> std::result::Result<usize, BinarySpaceError> { + if self.start == self.end { + Ok(self.start) + } else { + Err(BinarySpaceError::NotReduced) + } + } +} + +fn read_input<T: AsRef<Path>>(filename: T) -> std::io::Result<Vec<String>> { + let f = File::open(filename)?; + let reader = BufReader::new(f); + + reader.lines().collect() +} + +fn parse_boarding_passes(input: &[String]) -> Result<Vec<usize>> { + input.iter().map(|ops| { + let mut row = BinarySpace::new(0..=127); + let mut col = BinarySpace::new(0..=7); + for op in ops.clone().drain(..) { + match op { + 'F' | 'B' => row.split(op), + 'L' | 'R' => col.split(op), + _ => return Err(anyhow!("Invalid op encountered")), + } + } + + Ok(row.value()? * 8 + col.value()?) + }).collect() +} + +fn part1(input: &[usize]) -> Option<usize> { + input.iter().max().copied() +} + +fn part2(input: &[usize]) -> Option<usize> { + let mut ret = None; + + for seat in 1..(128*8) { + if (input.contains(&seat), + input.contains(&(seat-1)), + input.contains(&(seat+1)) + ) == (false, true, true) { + match ret { + None => ret = Some(seat), + Some(_) => return None, /* No more than one solution allowed */ + } + } + } + + ret +} + +fn main() { + let (do_part_1, do_part_2) = aoc::do_parts(); + + let filename = match args().nth(1) { + Some(f) => f, + None => { + eprintln!("Missing input filename"); + std::process::exit(1); + }, + }; + match read_input(filename) { + Ok(input) => { + match parse_boarding_passes(&input) { + Ok(seat_ids) => { + if do_part_1 { + match part1(&seat_ids) { + Some(solution) => + println!("Part1, highest seat ID found to be: {}", solution), + None => eprintln!("Part1, no soluton found"), + } + } + if do_part_2 { + match part2(&seat_ids) { + Some(solution) => + println!("Part2, seat {} is lacking a boarding pass", solution), + None => eprintln!("Part2, no soluton found"), + } + } + }, + Err(err) => eprintln!("Could not parse input: {}", err), + } + + }, + Err(err) => eprintln!("Could not read input: {}", err), + } +} |