summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorArne Dußin2021-12-08 13:34:12 +0100
committerArne Dußin2021-12-08 13:34:12 +0100
commit1ba4621a021a510c81339ec71160f591c099507a (patch)
treed69a4a2fecda2d8043463e5d1c366939afe36fb2 /src
parent11b2fd042a72eb93fb0741e2dec7d0ec035178f8 (diff)
downloadaoc2021-main.tar.gz
aoc2021-main.zip
Add solution to day 8HEADmain
Diffstat (limited to 'src')
-rw-r--r--src/day_8.rs203
-rw-r--r--src/main.rs4
2 files changed, 207 insertions, 0 deletions
diff --git a/src/day_8.rs b/src/day_8.rs
new file mode 100644
index 0000000..5da2978
--- /dev/null
+++ b/src/day_8.rs
@@ -0,0 +1,203 @@
+use std::convert::Infallible;
+use std::ops::Add;
+use std::str::FromStr;
+
+#[derive(Clone, PartialEq, Eq)]
+struct Segment
+{
+ data: String,
+}
+
+struct Instruction
+{
+ inputs: Vec<Segment>,
+ outputs: Vec<Segment>,
+}
+
+fn part1(instructions: &[Instruction])
+{
+ let mut num_immediately_identifyable = 0;
+ for i in instructions {
+ for o in i.outputs() {
+ match o.data().len() {
+ 2 | 4 | 3 | 7 => num_immediately_identifyable += 1,
+ _ => {},
+ }
+ }
+ }
+
+ println!("Solution to part 1: {}", num_immediately_identifyable);
+}
+
+fn part2(instructions: Vec<Instruction>)
+{
+ let sum: u64 = instructions.into_iter().map(|i| i.decode()).sum();
+ println!("Solution to part 2: {}", sum);
+}
+
+pub fn run(input: Vec<String>)
+{
+ let instructions: Vec<Instruction> = input
+ .into_iter()
+ .map(|s| Instruction::from_str(&s).unwrap())
+ .collect();
+
+ part1(&instructions);
+ part2(instructions);
+}
+
+impl Segment
+{
+ pub fn data(&self) -> &String { &self.data }
+
+ pub fn is_superset(&self, of: &Segment) -> bool
+ {
+ of.data().chars().all(|c| self.data.contains(c))
+ }
+}
+
+impl Add for Segment
+{
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self::Output
+ {
+ let data = self.data + &rhs.data;
+ let mut bytes: Vec<u8> = data.bytes().collect();
+
+ bytes.sort();
+ bytes.dedup();
+
+ Self {
+ data: unsafe { String::from_utf8_unchecked(bytes) },
+ }
+ }
+}
+
+impl Instruction
+{
+ pub fn outputs(&self) -> &Vec<Segment> { &self.outputs }
+
+ pub fn decode(mut self) -> u64
+ {
+ let mut codes: [Option<Segment>; 10] =
+ [None, None, None, None, None, None, None, None, None, None];
+ // Decode the easy ones
+ self.inputs.drain_filter(|s| match s.data.len() {
+ 2 => {
+ codes[1] = Some(s.clone());
+ true
+ },
+ 3 => {
+ codes[7] = Some(s.clone());
+ true
+ },
+ 4 => {
+ codes[4] = Some(s.clone());
+ true
+ },
+ 7 => {
+ codes[8] = Some(s.clone());
+ true
+ },
+ _ => false,
+ });
+ assert_eq!(self.inputs.len(), 6);
+
+ // Only 3 has length 5 and contains all 1 segments
+ self.inputs.drain_filter(|s| {
+ if s.data().len() == 5 && s.is_superset(&codes[1].as_ref().unwrap()) {
+ codes[3] = Some(s.clone());
+ true
+ }
+ else {
+ false
+ }
+ });
+ assert_eq!(self.inputs.len(), 5);
+
+ // Combine 3 and 4 to get 9
+ let nine_seg = codes[3].as_ref().unwrap().clone() + codes[4].as_ref().unwrap().clone();
+ self.inputs.drain_filter(|s| s == &nine_seg);
+ codes[9] = Some(nine_seg);
+ assert_eq!(self.inputs.len(), 4);
+
+ // 6 and 0 are the only remaining numbers with 6 segments on, only 0 is a
+ // superset of 1s segments
+ self.inputs.drain_filter(|s| {
+ if s.data().len() == 6 {
+ if s.is_superset(codes[1].as_ref().unwrap()) {
+ codes[0] = Some(s.clone());
+ }
+ else {
+ codes[6] = Some(s.clone());
+ }
+ true
+ }
+ else {
+ false
+ }
+ });
+
+ assert_eq!(self.inputs.len(), 2);
+ // 6 is a superset of 5, but not 2
+ self.inputs.drain_filter(|s| {
+ assert_eq!(s.data().len(), 5);
+ if codes[6].as_ref().unwrap().is_superset(s) {
+ codes[5] = Some(s.clone());
+ }
+ else {
+ codes[2] = Some(s.clone());
+ }
+
+ true
+ });
+
+ let codes: Vec<Segment> = codes.into_iter().map(|c| c.unwrap()).collect();
+
+ let mut acc = 0;
+ for (i, o) in self.outputs.iter().rev().enumerate() {
+ let num = codes.iter().enumerate().find(|(_, s)| s == &o).unwrap().0;
+ acc += num as u64 * 10_u64.pow(i as u32);
+ }
+
+ acc
+ }
+}
+
+impl FromStr for Instruction
+{
+ type Err = Infallible;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err>
+ {
+ let parts: Vec<&str> = s.split('|').collect();
+ assert_eq!(parts.len(), 2);
+
+ Ok(Self {
+ inputs: parts[0]
+ .split_whitespace()
+ .map(|s| Segment::from_str(s).unwrap())
+ .collect(),
+ outputs: parts[1]
+ .split_whitespace()
+ .map(|s| Segment::from_str(s).unwrap())
+ .collect(),
+ })
+ }
+}
+
+impl FromStr for Segment
+{
+ type Err = Infallible;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err>
+ {
+ let mut s = s.to_owned();
+
+ // Sort the letters in the string to make later operations easier.
+ unsafe { s.as_bytes_mut().sort() };
+
+ Ok(Self { data: s })
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 57d56b6..deb98ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![feature(drain_filter)]
+
mod day_1;
mod day_2;
mod day_3;
@@ -5,6 +7,7 @@ mod day_4;
mod day_5;
mod day_6;
mod day_7;
+mod day_8;
use std::fs::File;
use std::io::{BufRead, BufReader};
@@ -38,6 +41,7 @@ fn run(day: u8, input: Vec<String>)
5 => day_5::run(input),
6 => day_6::run(input),
7 => day_7::run(input),
+ 8 => day_8::run(input),
o => panic!("Day {} is not implemented (yet)", o),
}
}