advent_of_code/year2015/
day12.rs1use crate::input::Input;
2use std::collections::HashMap;
3
4#[allow(clippy::use_self)]
5#[derive(Eq, PartialEq, Debug)]
6enum JsonValue<'a> {
7 String(&'a [u8]),
8 Number(i32),
9 Array(Vec<JsonValue<'a>>),
10 Object(HashMap<&'a [u8], JsonValue<'a>>),
11 Comma,
12 Colon,
13 EndOfArray,
14 EndOfObject,
15 EndOfInput,
16}
17
18fn parse<'a>(input: &'a [u8], current_idx: &mut usize) -> Result<JsonValue<'a>, String> {
19 if *current_idx == input.len() {
20 return Ok(JsonValue::EndOfInput);
21 }
22 let next_char = input[*current_idx];
23 *current_idx += 1;
24
25 Ok(match next_char {
26 b'{' => {
27 let mut object_map = HashMap::new();
28 loop {
29 let mut next_key = parse(input, current_idx)?;
30 if next_key == JsonValue::Comma {
31 next_key = parse(input, current_idx)?;
32 }
33
34 if JsonValue::EndOfObject == next_key {
35 break JsonValue::Object(object_map);
36 } else if let JsonValue::String(key) = next_key {
37 let next_colon = parse(input, current_idx)?;
38 if next_colon != JsonValue::Colon {
39 return Err("Invalid JSON - key not followed by colon".to_string());
40 }
41
42 let next_value = parse(input, current_idx)?;
43 object_map.insert(key, next_value);
44 } else {
45 return Err(format!(
46 "Not key or colon in object: {:?} (index={})",
47 next_key, *current_idx
48 ));
49 }
50 }
51 }
52 b'}' => JsonValue::EndOfObject,
53 b':' => JsonValue::Colon,
54 b'[' => {
55 let mut array = Vec::new();
56 loop {
57 let next_value = parse(input, current_idx)?;
58 if JsonValue::EndOfArray == next_value {
59 break JsonValue::Array(array);
60 } else if JsonValue::EndOfInput == next_value {
61 return Err("Invalid JSON".to_string());
62 } else if JsonValue::Comma == next_value {
63 } else {
65 array.push(next_value);
66 }
67 }
68 }
69 b']' => JsonValue::EndOfArray,
70 b',' => JsonValue::Comma,
71 b'"' => {
72 for (idx, &read_char) in input.iter().enumerate().skip(*current_idx) {
73 if read_char == b'"' {
74 let start_idx = *current_idx;
75 *current_idx = idx + 1;
76 return Ok(JsonValue::String(&input[start_idx..idx]));
77 }
78 }
79 return Err("Invalid input - no end of string".to_string());
80 }
81 b'0'..=b'9' | b'-' => {
82 let mut idx = *current_idx;
83 let (next_char, sign) = if next_char == b'-' {
84 let res = (input[idx], -1);
85 idx += 1;
86 res
87 } else {
88 (next_char, 1)
89 };
90 let mut value = sign * i32::from(next_char - b'0');
91
92 loop {
93 let read_char = if idx == input.len() { b' ' } else { input[idx] };
94 if read_char.is_ascii_digit() {
95 value = value
96 .checked_mul(10_i32)
97 .and_then(|v| v.checked_add(sign * i32::from(read_char - b'0')))
98 .ok_or("Non-i32 number")?;
99 } else {
100 *current_idx = idx;
101 break JsonValue::Number(value);
102 }
103 idx += 1;
104 }
105 }
106 _ => {
107 return Err(format!(
108 "Invalid char: '{}' at index={}",
109 next_char as char, *current_idx
110 ));
111 }
112 })
113}
114
115fn sum_json_value(value: &JsonValue, part2: bool) -> i32 {
116 match value {
117 JsonValue::Number(n) => *n,
118 JsonValue::Array(vec) => vec.iter().map(|value| sum_json_value(value, part2)).sum(),
119 JsonValue::Object(map) => {
120 if part2
121 && map
122 .values()
123 .any(|value| value == &JsonValue::String(b"red"))
124 {
125 0
126 } else {
127 map.values().map(|value| sum_json_value(value, part2)).sum()
128 }
129 }
130 _ => 0,
131 }
132}
133
134pub fn solve(input: &Input) -> Result<i32, String> {
135 let mut current_idx = 0_usize;
136 let json_value = parse(input.text.as_bytes(), &mut current_idx)?;
137 let sum = sum_json_value(&json_value, input.is_part_two());
138 Ok(sum)
139}
140
141#[test]
142pub fn test_parse() {
143 let mut current_idx = 0_usize;
144 assert_eq!(
145 Ok(JsonValue::Number(1234)),
146 parse(b"1234", &mut current_idx)
147 );
148
149 current_idx = 0;
150 assert_eq!(
151 Ok(JsonValue::String(b"1234")),
152 parse(b"\"1234\"", &mut current_idx)
153 );
154
155 current_idx = 0;
156 assert_eq!(
157 Ok(JsonValue::Number(i32::MAX)),
158 parse(b"2147483647", &mut current_idx)
159 );
160
161 current_idx = 0;
162 assert_eq!(
163 Ok(JsonValue::Number(i32::MIN)),
164 parse(b"-2147483648", &mut current_idx)
165 );
166
167 for input in [
168 b"2147483648".as_slice(),
169 b"-2147483649".as_slice(),
170 b"9000000000".as_slice(),
171 b"-9000000000".as_slice(),
172 ] {
173 current_idx = 0;
174 assert_eq!(
175 Err("Non-i32 number".to_string()),
176 parse(input, &mut current_idx)
177 );
178 }
179
180 current_idx = 0;
181 assert_eq!(
182 Ok(JsonValue::Array(vec![
183 JsonValue::Number(123),
184 JsonValue::String(b"abc")
185 ])),
186 parse(b"[123,\"abc\"]", &mut current_idx)
187 );
188
189 current_idx = 0;
190 let mut expected_map = HashMap::new();
191 let key1 = b"key1";
192 let key2 = b"key2";
193 let key3 = b"key3";
194 expected_map.insert(&key1[..], JsonValue::Number(123));
195 expected_map.insert(&key2[..], JsonValue::String(b"abc"));
196 expected_map.insert(
197 &key3[..],
198 JsonValue::Array(vec![JsonValue::Number(-345), JsonValue::String(b"abc")]),
199 );
200 assert_eq!(
201 Ok(JsonValue::Object(expected_map)),
202 parse(
203 b"{\"key1\":123,\"key2\":\"abc\",\"key3\":[-345,\"abc\"]}",
204 &mut current_idx
205 )
206 );
207}
208
209#[test]
210pub fn tests() {
211 test_part_one!("{\"a\":{\"b\":4},\"c\":-1}" => 3);
212 test_part_one!("[1,2,3]" => 6);
213 test_part_one!("{\"a\":2,\"b\":4}" => 6);
214 test_part_one!("[[[3]]]" => 3);
215 test_part_one!("{\"a\":[-1,1]}" => 0);
216 test_part_one!("[-1,{\"a\":1}]" => 0);
217 test_part_one!("[]" => 0);
218 test_part_one!("{}" => 0);
219
220 test_part_two!("[1,{\"c\":\"red\",\"b\":2},3]" => 4);
221
222 let real_input = include_str!("day12_input.txt");
223 test_part_one!(real_input => 111_754);
224 test_part_two!(real_input => 65_402);
225}