advent-of-code

Solutions for Advent of Code.
git clone git://git.amin.space/advent-of-code.git
Log | Files | Refs | LICENSE

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 }