-
Notifications
You must be signed in to change notification settings - Fork 0
/
day19.rs
142 lines (119 loc) · 4.13 KB
/
day19.rs
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! [Day 19: Medicine for Rudolph](https://adventofcode.com/2015/day/19)
use std::collections::HashSet;
use std::env;
use std::fs;
struct Puzzle {
medicine_molecule: String,
replacements: Vec<(String, String)>,
}
impl Puzzle {
fn new() -> Puzzle {
Puzzle {
medicine_molecule: String::new(),
replacements: Vec::new(),
}
}
fn configure(&mut self, filename: &str) {
let mut data: Vec<String> = fs::read_to_string(filename)
.expect("Failed to read input file")
.lines()
.map(ToString::to_string)
.collect();
self.medicine_molecule = data.pop().unwrap();
// Remove empty delimiter
data.pop();
for line in data {
// Get the replacement rule
let mut replacement: Vec<&str> = line.split(" => ").collect();
let to = replacement.pop().unwrap().to_string();
let from = replacement.pop().unwrap().to_string();
self.replacements.push((from, to));
}
}
fn part1(&mut self) -> usize {
let mut molecules = HashSet::new();
for (from, to) in &self.replacements {
// Split the molecule on each atom
let sub_molecules: Vec<String> = self
.medicine_molecule
.split_inclusive(from)
.map(ToString::to_string)
.collect();
// Apply the replacement rule for each atom found
for (i, sub_mol) in sub_molecules.iter().enumerate() {
let mut generated_molecule = sub_molecules.clone();
let sub_mol = sub_mol.replace(from, to);
generated_molecule[i] = sub_mol;
molecules.insert(generated_molecule.concat());
}
}
molecules.remove(&self.medicine_molecule);
molecules.len()
}
/// This resolution works but it's not truly what's asked by the puzzle. We don't know if this
/// method yields the lowest count of iterations required to produce the molecule. Finding the
/// lowest count of iterations is a way harder puzzle and was probably not meant by the author
/// of the puzzle. The puzzle input is probably designed in order for only one answer to rise.
fn part2(&mut self) -> usize {
let mut iterations = 0_usize;
// Reduce the medicine molecule to the electron is equivalent to create the medicine molecule from the electron
while self.medicine_molecule != "e" {
for (from, to) in &self.replacements {
// Reverse apply the replacement only once
if self.medicine_molecule.find(to) != None {
self.medicine_molecule = self.medicine_molecule.replacen(to, from, 1);
break;
}
}
iterations += 1;
}
iterations
}
}
/// Test from the puzzle description
#[test]
fn test1() {
let mut puzzle = Puzzle::new();
puzzle.configure("test01.txt");
assert_eq!(puzzle.part1(), 4);
}
/// Test from the puzzle description
#[test]
fn test2() {
let mut puzzle = Puzzle::new();
puzzle.configure("test02.txt");
assert_eq!(puzzle.part1(), 7);
}
/// Test from the puzzle description
#[ignore = "Heuristic made to solve the problem does not work here"]
#[test]
fn test3() {
let mut puzzle = Puzzle::new();
puzzle.configure("test03.txt");
assert_eq!(puzzle.part2(), 3);
}
/// Test from the puzzle description
#[ignore = "Heuristic made to solve the problem does not work here"]
#[test]
fn test4() {
let mut puzzle = Puzzle::new();
puzzle.configure("test04.txt");
assert_eq!(puzzle.part2(), 6);
}
/// Test from a player's input
#[test]
fn test5() {
let mut puzzle = Puzzle::new();
puzzle.configure("test05.txt");
assert_eq!(puzzle.part1(), 518);
assert_eq!(puzzle.part2(), 200);
}
fn main() {
let mut puzzle = Puzzle::new();
let args: Vec<String> = env::args().collect();
puzzle.configure(args.get(1).expect("No input file"));
let result = puzzle.part1();
println!("{}", result);
let result = puzzle.part2();
println!("{}", result);
}