summaryrefslogtreecommitdiffstats
path: root/src/day_12.rs
diff options
context:
space:
mode:
authorToby Vincent <tobyv@tobyvin.dev>2023-12-12 18:39:18 -0600
committerToby Vincent <tobyv@tobyvin.dev>2023-12-12 18:39:18 -0600
commit83ecd48629924bec2852e07c87825ce70c3db5bf (patch)
treec74b2ea9d6f1042bbc611938b0e9ae9bb93ceea7 /src/day_12.rs
parentc791edce9a365beeeb3288b3060c37ae48a1a4e2 (diff)
feat(wip): impl day 12, part 1 (brute forced)
Diffstat (limited to 'src/day_12.rs')
-rw-r--r--src/day_12.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/src/day_12.rs b/src/day_12.rs
new file mode 100644
index 0000000..6b755c9
--- /dev/null
+++ b/src/day_12.rs
@@ -0,0 +1,143 @@
+use crate::{Problem, Solution};
+
+pub struct Day12;
+
+impl Problem for Day12 {
+ const DAY: u8 = 12;
+
+ const INPUT: &'static str = include_str!("../input/day_12.txt");
+}
+
+impl Solution for Day12 {
+ type Answer1 = usize;
+
+ type Answer2 = usize;
+
+ fn part_1(input: &str) -> anyhow::Result<Self::Answer1> {
+ let rows = input
+ .lines()
+ .map(std::str::FromStr::from_str)
+ .try_collect::<Vec<Row>>()?;
+
+ Ok(rows
+ .into_iter()
+ .map(|row| {
+ row.permutations()
+ .into_iter()
+ .filter(|p| row.check_group(p))
+ .count()
+ })
+ .sum())
+ }
+
+ fn part_2(input: &str) -> anyhow::Result<Self::Answer2> {
+ todo!()
+ }
+}
+
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
+struct Row {
+ states: Vec<State>,
+ groups: Vec<usize>,
+}
+
+impl Row {
+ fn unknowns(&self) -> usize {
+ self.states.iter().filter(|&&s| s == State::Unknown).count()
+ }
+
+ fn permutations(&self) -> Vec<Vec<State>> {
+ (0..self.unknowns())
+ .fold(vec![vec![]], |acc, _| {
+ acc.into_iter()
+ .flat_map(|mut p1| {
+ let mut p2 = p1.to_vec();
+ p2.push(State::Bad);
+ p1.push(State::Good);
+ [p1, p2]
+ })
+ .collect()
+ })
+ .into_iter()
+ .map(|s| {
+ let mut iter = s.into_iter();
+ let mut row = self.states.to_vec();
+ for s in row.iter_mut() {
+ if let State::Unknown = s {
+ *s = iter.next().unwrap()
+ };
+ }
+ row
+ })
+ .collect()
+ }
+
+ fn check_group(&self, states: &[State]) -> bool {
+ let mut counts = self.groups.iter();
+
+ states.group_by(|a, b| a == b).all(|group| {
+ group.iter().all(|s| *s == State::Good)
+ || counts.next().is_some_and(|n| *n == group.len())
+ }) && counts.next().is_none()
+ }
+}
+
+impl std::str::FromStr for Row {
+ type Err = anyhow::Error;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let (states, groups) = s.split_once(' ').unwrap();
+
+ let states = states
+ .chars()
+ .map(TryFrom::try_from)
+ .try_collect::<Vec<State>>()?;
+
+ let groups = groups
+ .split(',')
+ .map(std::str::FromStr::from_str)
+ .try_collect::<Vec<usize>>()?;
+
+ Ok(Self { states, groups })
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum State {
+ Good,
+ Bad,
+ Unknown,
+}
+
+impl TryFrom<char> for State {
+ type Error = anyhow::Error;
+
+ fn try_from(value: char) -> Result<Self, Self::Error> {
+ use State::*;
+ match value {
+ '.' => Ok(Good),
+ '#' => Ok(Bad),
+ '?' => Ok(Unknown),
+ c => Err(anyhow::format_err!("Unknown spring condition: {c}")),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ const INPUT: &str = indoc::indoc! {"
+ ???.### 1,1,3
+ .??..??...?##. 1,1,3
+ ?#?#?#?#?#?#?#? 1,3,1,6
+ ????.#...#... 4,1,1
+ ????.######..#####. 1,6,5
+ ?###???????? 3,2,1
+ "};
+
+ #[test]
+ fn test_part_1() -> anyhow::Result<()> {
+ Ok(assert_eq!(21, Day12::part_1(INPUT)?))
+ }
+}