Skip to content

Commit ecdabe6

Browse files
committed
day 18
1 parent f6928dd commit ecdabe6

File tree

6 files changed

+529
-0
lines changed

6 files changed

+529
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,8 @@ Part Two: 2564529489989
7070
--- Day 17: Conway Cubes ---
7171
Part One: 237
7272
Part Two: 2448
73+
74+
--- Day 18: Operation Order ---
75+
Part One: 86311597203806
76+
Part Two: 276894767062189
7377
```

inputs/18-example.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
1 + 2 * 3 + 4 * 5 + 6
2+
1 + (2 * 3) + (4 * (5 + 6))
3+
2 * 3 + (4 * 5)
4+
5 + (8 * 3 + 9 + 3 * 4 * 3)
5+
5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))
6+
((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2

inputs/18-input.txt

Lines changed: 373 additions & 0 deletions
Large diffs are not rendered by default.

src/day18.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2+
enum Token {
3+
N(u64),
4+
O(char),
5+
}
6+
7+
fn parse_input(input: &str) -> Vec<&str> {
8+
input.trim().lines().map(|s| s.trim()).collect()
9+
}
10+
11+
fn tokenize(s: &str) -> Vec<Token> {
12+
s.chars()
13+
.filter(|c| c.is_numeric() || ['(', ')', '+', '*'].contains(c))
14+
.map(|c| {
15+
if c.is_numeric() {
16+
Token::N(c.to_digit(10).unwrap() as u64)
17+
} else {
18+
Token::O(c)
19+
}
20+
})
21+
.collect()
22+
}
23+
24+
fn evaluate(expr: &str, rpn: &dyn Fn(Vec<Token>) -> Vec<Token>) -> u64 {
25+
let tokens = tokenize(expr);
26+
let tokens = rpn(tokens);
27+
let mut stack = Vec::new();
28+
for token in tokens.into_iter() {
29+
match token {
30+
Token::N(n) => stack.push(n),
31+
Token::O(op) => {
32+
let rhs = stack.pop().unwrap();
33+
let lhs = stack.pop().unwrap();
34+
stack.push(match op {
35+
'+' => lhs + rhs,
36+
'*' => lhs * rhs,
37+
_ => unreachable!(),
38+
});
39+
}
40+
}
41+
}
42+
stack.pop().unwrap()
43+
}
44+
45+
pub fn part_one(input: &str) -> u64 {
46+
fn rpn(tokens: Vec<Token>) -> Vec<Token> {
47+
let mut s1 = Vec::new();
48+
let mut s2 = Vec::new();
49+
for token in tokens {
50+
match token {
51+
Token::N(_) => s2.push(token),
52+
Token::O(_) => {
53+
if s1.is_empty() || token == Token::O('(') {
54+
s1.push(token);
55+
continue;
56+
}
57+
if token == Token::O(')') {
58+
while let Some(last) = s1.pop() {
59+
if last == Token::O('(') {
60+
break;
61+
}
62+
s2.push(last);
63+
}
64+
continue;
65+
}
66+
let last = s1.pop().unwrap();
67+
if last == Token::O('(') {
68+
s1.push(last);
69+
s1.push(token);
70+
continue;
71+
}
72+
s2.push(last);
73+
s1.push(token);
74+
}
75+
}
76+
}
77+
while let Some(last) = s1.pop() {
78+
s2.push(last);
79+
}
80+
s2
81+
}
82+
83+
let expressions = parse_input(input);
84+
expressions.iter().map(|expr| evaluate(expr, &rpn)).sum()
85+
}
86+
87+
pub fn part_two(input: &str) -> u64 {
88+
fn rpn(tokens: Vec<Token>) -> Vec<Token> {
89+
let mut s1 = Vec::new();
90+
let mut s2 = Vec::new();
91+
for token in tokens {
92+
match token {
93+
Token::N(_) => s2.push(token),
94+
Token::O(_) => {
95+
if s1.is_empty() || token == Token::O('(') {
96+
s1.push(token);
97+
continue;
98+
}
99+
if token == Token::O(')') {
100+
while let Some(last) = s1.pop() {
101+
if last == Token::O('(') {
102+
break;
103+
}
104+
s2.push(last);
105+
}
106+
continue;
107+
}
108+
let last = s1.pop().unwrap();
109+
if last == Token::O('(') {
110+
s1.push(last);
111+
s1.push(token);
112+
continue;
113+
}
114+
if last == Token::O('*') && token == Token::O('+') {
115+
s1.push(last);
116+
s1.push(token);
117+
continue;
118+
}
119+
s2.push(last);
120+
s1.push(token);
121+
}
122+
}
123+
}
124+
while let Some(last) = s1.pop() {
125+
s2.push(last);
126+
}
127+
s2
128+
}
129+
let expressions = parse_input(input);
130+
expressions.iter().map(|expr| evaluate(expr, &rpn)).sum()
131+
}
132+
133+
#[cfg(test)]
134+
mod tests {
135+
use super::*;
136+
use crate::read_example;
137+
138+
#[test]
139+
fn example() {
140+
let input = read_example(18);
141+
assert_eq!(part_one(&input), 71 + 51 + 26 + 437 + 12240 + 13632);
142+
assert_eq!(part_two(&input), 231 + 51 + 46 + 1445 + 669060 + 23340);
143+
}
144+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod day14;
1717
pub mod day15;
1818
pub mod day16;
1919
pub mod day17;
20+
pub mod day18;
2021

2122
pub fn read_as_string(day: u8, filename: &str) -> String {
2223
let filename = format!("inputs/{:02}-{}.txt", day, filename);

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn main() {
3333
puzzle!(day15, "Rambunctious Recitation"),
3434
puzzle!(day16, "Ticket Translation"),
3535
puzzle!(day17, "Conway Cubes"),
36+
puzzle!(day18, "Operation Order"),
3637
];
3738

3839
let filename = match env::args().find(|a| a == "--example") {

0 commit comments

Comments
 (0)