summary refs log tree commit diff
path: root/19/src/main.rs
blob: 5bbdcc65ba24faee2a1c88669c3f83064bae70aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use advent_lib::prelude::*;

use std::collections::BTreeMap;
//use std::convert::TryFrom;

#[derive(Clone,Debug)]
enum Rule {
  Alternatives(Vec<Vec<i64>>),
  Character(char),
}


fn main() -> Result<()> {
  let mut args = std::env::args();
  if args.len() != 2 {
    eprintln!("Usage: advent input");
  }
  let _ = args.next();
  let filename = args.next().unwrap();

  let input = advent_lib::read_lines_file(&filename)?;
  let input = advent_lib::group_lines_by_blanks(input);

  let mut rules: BTreeMap<i64,Rule> = BTreeMap::new();
  for line in &input[0] {
    let colon_point = line.find(':').unwrap();
    let (rule_number, rest) = line.split_at(colon_point);
    let rule_number = rule_number.parse::<i64>().unwrap();
    let (_, rest) = rest.split_at(2);
    let words: Vec<&str> = rest.split(' ').collect();

    let mut chars = words[0].chars();
    if words.len() == 1 && chars.next() == Some('"') {
      let c = chars.next().unwrap();

      rules.insert(rule_number, Rule::Character(c));
    } else {
      let alternatives_inputs = words.split(|word| *word == "|");

      let mut alternatives: Vec<Vec<i64>> = Vec::new();
      for alternative_words in alternatives_inputs {
        let mut alternative: Vec<i64> = Vec::new();
        for word in alternative_words {
          alternative.push(word.parse::<i64>().unwrap());
        }
        alternatives.push(alternative);
      }

      rules.insert(rule_number, Rule::Alternatives(alternatives));
    }
  }

  let mut match_count = 0;
  for line in &input[1] {
    if is_match(&rules, 0, line) {
      match_count += 1;
    }
  }

  println!("{:?}", match_count);

  rules.insert(8, Rule::Alternatives(vec![vec![42], vec![42, 8]]));
  rules.insert(11, Rule::Alternatives(vec![vec![42, 31], vec![42, 11, 31]]));

  let mut match_count = 0;
  for line in &input[1] {
    if is_match(&rules, 0, line) {
      match_count += 1;
    }
  }

  println!("{:?}", match_count);

  Ok(())
}


fn is_match(rules: &BTreeMap<i64,Rule>, root_rule: i64, input: &str) -> bool {
  let match_results = is_match_helper(rules, root_rule, input);
  for rest in match_results {
    if rest.len() == 0 {
      return true;
    }
  }
  false
}

fn is_match_helper<'a>(rules: &BTreeMap<i64,Rule>, root_rule: i64, input: &'a str)
  -> Vec<&'a str>
{
  let mut results = Vec::new();

  match rules.get(&root_rule).unwrap() {
    Rule::Alternatives(alternatives) => {
      for alternative in alternatives {
        let mut possible_rests = Vec::new();
        possible_rests.push(input);

        for sub_rule in alternative {
          let mut new_possible_rests = Vec::new();

          for rest in possible_rests {
            let sub_matches = is_match_helper(rules, *sub_rule, rest);
            for new_rest in sub_matches {
              new_possible_rests.push(new_rest);
            }
          }

          possible_rests = new_possible_rests;
        }

        for rest in possible_rests {
          results.push(rest);
        }
      }
    },
    Rule::Character(character) => {
      if input.len() >= 1 && input.chars().next() == Some(*character) {
        let (_, rest) = input.split_at(1);
        results.push(rest);
      }
    },
  }

  results
}