day_08.zig (8026B)
1 const std = @import("std"); 2 const debug = std.debug; 3 const fmt = std.fmt; 4 const io = std.io; 5 const mem = std.mem; 6 const os = std.os; 7 8 const test_nums = []u32 { 2, 3, 0, 3, 10, 11, 12, 1, 1, 0, 1, 99, 2, 1, 1, 2, }; 9 const debug_logging: bool = false; 10 11 pub fn main() !void { 12 var allocator = &std.heap.DirectAllocator.init().allocator; 13 14 var nums: []u32 = undefined; 15 { 16 const input = try getFileContents(allocator, "input_08.txt"); 17 defer allocator.free(input); 18 nums = try getNums(allocator, input); 19 } 20 defer allocator.free(nums); 21 22 const nodes = try deserializeNodes(allocator, nums); 23 defer allocator.free(nodes); 24 defer { 25 // Yes, all this free-ing is rather silly for our purposes. Just 26 // trying out defer. 27 for (nodes) |n| { 28 n.deinit(allocator); 29 } 30 } 31 32 const result1 = sumAllMetadataEntries(nodes); 33 debug.warn("08-1: {}\n", result1); 34 35 const result2 = getRootNodeValue(nodes); 36 debug.warn("08-2: {}\n", result2); 37 } 38 39 fn getRootNodeValue(nodes: []Node) u32 { 40 var root_node = Node.linearSearch(nodes, 0) orelse unreachable; 41 return getNodeValue(nodes, root_node); 42 } 43 44 fn getNodeValue(nodes: []Node, node: Node) u32 { 45 var value: u32 = 0; 46 47 if (node.num_children == 0) { 48 value = node.sumMetadata(); 49 } else { 50 for (node.metadata_entries) |e| { 51 if (e > 0 and e - 1 < node.child_ids.len) { 52 var child_id = node.child_ids[e - 1]; 53 var child = Node.linearSearch(nodes, child_id) orelse unreachable; 54 value += getNodeValue(nodes, child); 55 } 56 } 57 } 58 59 return value; 60 } 61 62 test "deserialize and get root node value" { 63 var allocator = debug.global_allocator; 64 var nodes = try deserializeNodes(allocator, test_nums); 65 debug.assert(66 == getRootNodeValue(nodes)); 66 } 67 68 fn sumAllMetadataEntries(nodes: []Node) u32 { 69 var metadata_sum: u32 = 0; 70 for (nodes) |n| { 71 for (n.metadata_entries) |e| { 72 metadata_sum += e; 73 } 74 } 75 76 return metadata_sum; 77 } 78 79 /// Caller is responsible for freeing returned nodes. 80 fn deserializeNodes(allocator: *mem.Allocator, nums: []const u32) ![]Node { 81 const Task = enum { 82 GetHeader, 83 Descend, 84 Ascend, 85 GetMetadata, 86 }; 87 88 var nodes = std.ArrayList(Node).init(allocator); 89 90 // I need to manage this myself since I've decided not to use recursion 91 var node_stack = std.ArrayList(Node).init(allocator); 92 defer node_stack.deinit(); 93 94 // This is only to get the process started, and should never be included in 95 // the returned list of nodes 96 var root_node = Node { 97 .id = 0, 98 .num_children = 1, 99 .num_metadata = 0, 100 .num_children_found = 0, 101 .child_ids = try allocator.alloc(usize, 1), 102 .metadata_entries = undefined, 103 }; 104 105 var parent_node: Node = root_node; 106 var current_node: Node = undefined; 107 var task: Task = Task.GetHeader; 108 var num_index: usize = 0; 109 110 while (num_index < nums.len) { 111 logDebug("# {}\n", @tagName(task)); 112 switch (task) { 113 Task.GetHeader => { 114 var node_index = num_index; 115 var num_children = consumeNum(nums, &num_index); 116 var num_metadata = consumeNum(nums, &num_index); 117 current_node = try Node.init(node_index, num_children, num_metadata, allocator); 118 parent_node.child_ids[parent_node.num_children_found] = current_node.id; 119 if (current_node.num_children != 0) { 120 task = Task.Descend; 121 } else { 122 task = Task.GetMetadata; 123 } 124 }, 125 Task.Descend => { 126 try node_stack.append(parent_node); 127 parent_node = current_node; 128 task = Task.GetHeader; 129 }, 130 Task.Ascend => { 131 current_node = parent_node; 132 parent_node = node_stack.pop(); 133 task = Task.GetMetadata; 134 }, 135 Task.GetMetadata => { 136 for (current_node.metadata_entries) |*e| { 137 e.* = consumeNum(nums, &num_index); 138 } 139 140 try nodes.append(current_node); 141 parent_node.num_children_found += 1; 142 143 if (parent_node.num_children_found < parent_node.num_children) { 144 task = Task.GetHeader; 145 } else if (parent_node.num_children_found == parent_node.num_children) { 146 task = Task.Ascend; 147 } else { 148 unreachable; 149 } 150 }, 151 else => unreachable, 152 } 153 logDebug("P"); 154 parent_node.print(); 155 logDebug("C"); 156 current_node.print(); 157 } 158 159 return nodes.toSlice(); 160 } 161 162 test "deserialize and sum metadata" { 163 var allocator = debug.global_allocator; 164 var nodes = try deserializeNodes(allocator, test_nums); 165 debug.assert(138 == sumAllMetadataEntries(nodes)); 166 } 167 168 // I was tempted to call this numNum 169 fn consumeNum(nums: []const u32, index: *usize) u32 { 170 var num = nums[index.*]; 171 index.* += 1; 172 return num; 173 } 174 175 const Node = struct { 176 id: usize, 177 num_children: u32, 178 num_metadata: u32, 179 num_children_found: u32, 180 child_ids: []usize, 181 metadata_entries: []u32, 182 183 pub fn init(id: usize, nc: u32, nm: u32, allocator: *mem.Allocator) !Node { 184 var ci_buf = try allocator.alloc(usize, nc); 185 for (ci_buf) |*ci| { 186 ci.* = 0; 187 } 188 var me_buf = try allocator.alloc(u32, nm); 189 for (me_buf) |*e| { 190 e.* = 0; 191 } 192 return Node { 193 .id = id, 194 .num_children = nc, 195 .num_metadata = nm, 196 .num_children_found = 0, 197 .child_ids = ci_buf, 198 .metadata_entries = me_buf, 199 }; 200 } 201 202 pub fn deinit(self: Node, allocator: *mem.Allocator) void { 203 allocator.free(self.child_ids); 204 allocator.free(self.metadata_entries); 205 } 206 207 pub fn print(self: Node) void { 208 logDebug("[{}]: ({}/{}, {}) | ", self.id, self.num_children_found, self.num_children, self.num_metadata); 209 for (self.metadata_entries) |e| { 210 logDebug("{} ", e); 211 } 212 logDebug("| ("); 213 for (self.child_ids) |c| { 214 logDebug("{} ", c); 215 } 216 logDebug(")\n"); 217 } 218 219 pub fn lessThan(l: Node, r: Node) bool { 220 return l.id < r.id; 221 } 222 223 pub fn linearSearch(nodes: []Node, node_id: usize) ?Node { 224 for (nodes) |n, i| { 225 if (n.id == node_id) { 226 return n; 227 } 228 } 229 return null; 230 } 231 232 pub fn sumMetadata(self: Node) u32 { 233 var sum: u32 = 0; 234 for (self.metadata_entries) |e| { 235 sum += e; 236 } 237 return sum; 238 } 239 }; 240 241 fn getNums(allocator: *mem.Allocator, buf: []const u8) ![]u32 { 242 var it = mem.split(buf, []u8 { ' ', '\n', }); 243 var nodes = std.ArrayList(u32).init(allocator); 244 while (it.next()) |token| { 245 var num = try fmt.parseInt(u32, token, 10); 246 try nodes.append(num); 247 } 248 return nodes.toSlice(); 249 } 250 251 test "get nums" { 252 var allocator = debug.global_allocator; 253 const test_buf = "2 3 0 3 10 11 12 1 1 0 1 99 2 1 1 2"; 254 debug.assert(mem.eql(u32, test_nums, try getNums(allocator, test_buf))); 255 } 256 257 fn getFileContents(allocator: *mem.Allocator, file_name: []const u8) ![]u8 { 258 var file = try os.File.openRead(file_name); 259 defer file.close(); 260 261 const file_size = try file.getEndPos(); 262 263 var file_in_stream = io.FileInStream.init(file); 264 var buf_stream = io.BufferedInStream(io.FileInStream.Error).init(&file_in_stream.stream); 265 const st = &buf_stream.stream; 266 return try st.readAllAlloc(allocator, 2 * file_size); 267 } 268 269 fn logDebug(comptime format_str: []const u8, args: ...) void { 270 if (debug_logging) { 271 debug.warn(format_str, args); 272 } 273 }