summaryrefslogtreecommitdiff
path: root/2022/rust/day04/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to '2022/rust/day04/src/main.rs')
-rw-r--r--2022/rust/day04/src/main.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/2022/rust/day04/src/main.rs b/2022/rust/day04/src/main.rs
new file mode 100644
index 0000000..d32f1a3
--- /dev/null
+++ b/2022/rust/day04/src/main.rs
@@ -0,0 +1,103 @@
+use {
+ anyhow::{
+ anyhow,
+ Context,
+ Result,
+ },
+ regex::Regex,
+ std::{
+ env::args,
+ fmt,
+ fs::File,
+ io::{
+ BufRead,
+ BufReader,
+ },
+ path::Path,
+ },
+};
+
+struct SectionAssignment {
+ start: usize,
+ end: usize,
+}
+
+impl SectionAssignment {
+ fn new(start: usize, end: usize) -> Self {
+ Self {
+ start,
+ end,
+ }
+ }
+
+ fn contains(&self, other: &SectionAssignment) -> bool {
+ self.start <= other.start && self.end >= other.end
+ }
+
+ fn overlaps(&self, other: &SectionAssignment) -> bool {
+ (self.start >= other.start && self.start <= other.end) ||
+ (self.end >= other.start && self.end <= other.end) ||
+ (other.start >= self.start && other.start <= self.end) ||
+ (other.end >= self.start && other.end <= self.end)
+ }
+}
+
+impl fmt::Debug for SectionAssignment {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ (0..=9).map(|i| if i >= self.start && i <= self.end {
+ write!(f, "{i}")
+ } else {
+ write!(f, ".")
+ }).last();
+ write!(f, " {}-{}", self.start, self.end)?;
+ Ok(())
+ }
+}
+
+type AssignmentPair = [SectionAssignment; 2];
+
+fn read_input<T: AsRef<Path>>(filename: T) -> Result<Vec<AssignmentPair>> {
+ let reader = BufReader::new(File::open(filename)?);
+ let re = Regex::new(r"(?P<s0>[0-9]+)-(?P<e0>[0-9]+),(?P<s1>[0-9]+)-(?P<e1>[0-9]+)")
+ .context("Regex compilation failed.")?;
+
+ reader.lines().map(
+ |v| {
+ let s = v?;
+ let caps = re.captures(&s).ok_or_else(|| anyhow!("Regex matching failed."))?;
+ match (caps.name("s0"), caps.name("e0"), caps.name("s1"), caps.name("e1")) {
+ ( Some(s0), Some(e0), Some(s1), Some(e1) ) => Ok([
+ SectionAssignment::new(s0.as_str().parse()?, e0.as_str().parse()?),
+ SectionAssignment::new(s1.as_str().parse()?, e1.as_str().parse()?),
+ ]),
+ _ => Err(anyhow!("Parse error.")),
+ }
+ }
+ ).collect()
+}
+
+fn part1(input: &[AssignmentPair]) -> Result<usize> {
+ Ok(input.iter().filter(|pair|
+ pair[0].contains(&pair[1]) ||
+ pair[1].contains(&pair[0])).count())
+}
+
+fn part2(input: &[AssignmentPair]) -> Result<usize> {
+ Ok(input.iter().filter(|pair| pair[0].overlaps(&pair[1])).count())
+}
+
+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(())
+}