From 5b8558cac35c81ffc672dea06a8e8cfbb9e09aa9 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Sat, 11 Dec 2021 10:13:37 -0500 Subject: [PATCH] Day 10 --- day10/build.zig | 27 +++ day10/input | 94 ++++++++++ day10/src/main.zig | 424 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 545 insertions(+) create mode 100644 day10/build.zig create mode 100644 day10/input create mode 100644 day10/src/main.zig diff --git a/day10/build.zig b/day10/build.zig new file mode 100644 index 0000000..e32f1ea --- /dev/null +++ b/day10/build.zig @@ -0,0 +1,27 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + const exe = b.addExecutable("day10", "src/main.zig"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); +} diff --git a/day10/input b/day10/input new file mode 100644 index 0000000..214cf11 --- /dev/null +++ b/day10/input @@ -0,0 +1,94 @@ +{{<{{{{([{[([[()<>]{<>{}}]<([]())(()<>)>)((({}())[()[]])<<[][]>[{}[]]>)]{{(<{}<>>{<><>}]([<>[]]< +[(<{{[{(<({{<<[]()><<>{}>>([<>[]]{<><>})}})>)}]}}>[{(<{({[{[[({}())((){})]({{}[]})]<<[<>{}]([][])>({<>()} +(({<{[{({(([[([]())({}())]]({[[]{}]([][]))<((){})<{}<>>>))[(([<>[]]<[]>)(([]{}){{}{}}))])})[({<[{ +([{{[([<({<<<([]())[()[]]>{<()[]>[[]()]}>[{<[]{}><[]>>{<<>()>{[]()}}]>[[[[[]{}]([]<>)]<{<>{}} +[[((<({<(<{<<{{}()}{[][]}>[((){})]>}>{((<({}<>)<{}()>>[[<>()]])<<<[][]><<>[]>>{<{}[]>(<>())}>)<{[[{ +[{<{{{{<([{[(<[]<>>(<>[])){({}<>)([]<>)}]{{([][])[<>{}]}{<[]<>>(<>{})}}}])<<{[<[<>{}]<(){}>>{<{}<>><<> +(({({([(<[[([[{}{}]([]<>}][(<>()){(){}}])]](({{{{}<>}<{}[]>}([{}[]][(){}])}[(<[]{}>({}()))<<<><>>>])<[{ +[{<((<{(<{<<{[()()](()<>)}<({}[])([]<>)>>{(<<><>>[[]{}])[[[]()](<>[])]}>{[[({}<>)([]<>)][[{}[] +{<{<{{(<[[{[[({}[])[()[]]]([()<>][{}[]])]<<({}())[{}{}]><[<>[]](<><>)>>}(([([]{})((){})]((<>)([]{}))))]<{<<< +<{(<<(<[<{<[{[<>[]][{}{}]}{{()<>}{<><>}>]>[<(([]{})[[]])>]}<[<([{}{}][<><>]){([]{})}>]({<{<>()}>{ +{[[<{{{<<<[<((()<>)({}{}))<{{}()}>>]<((([]{}){{}<>})[{{}<>}([]{})])({{()[]}[[]()]}[{{}()}({ +<[[[{{([{[({[<{}[]>[{}()]]{<()>}}({<()()>}<<<>()>{[]}>))]((({({}[])>{[(){}]{<><>}})(({()[]}[()<>] +[([([([<[{{<[[{}[]]{<>{}}][<{}>(()[])]>[({<>()}(()[])){<[][]>{()[]}}]}}<{(({()}([]()))(([]())<()[]>))<((<>{} +<{[{(({([([[{((){})[[]]>{{{}[]}[<>()]}]])]{([([<[]{}>((){})]<[()<>]<()()>>)][<<{<>{}}{<><> +[[{{(([{(({[[{{}[]}]][<<<>()>({}{})>[[{}<>]{{}<>}]]}[(({{}}{[][]}){[{}{}][<>[]]})(<<<>{}><<><>>>)])<[ +[[{([<<([[<(<{<>()}<[][]>>{[<>[]]{()[]}}){[<[][]><{}()>][([]())<<><>>]}>]((<<[(){}][(){}]><([])>>)) +[{[<{(([(([([(<>[]}(<>{})][<{}[]>[()[]]])<{[{}<>]<[]<>>}{{[]<>}{{}{}}}>]<<{[<>{}](<>())}{{()[]}{<>{}}}>[ +{({<[(((<{<<[[()<>]]<({}<>)>>>[<({[]<>}{()<>})[[()<>][<><>]]>({[{}[]]({}<>)}[<(){}>(<>())])]}><[{<[[<> +<(((({{(<({[<<[]<>>>([()()]{<>()})][<[{}{}]{<>()}>{{{}{}}[[][]]}]}{(<<<><>>(()<>)>(<<>()>[()[]])) +[<{{[{[[[<(({<()()>[(){}]}[([]())[(){}]]))>{[<[[[]]<[][]>]{[[][]](()())}>[(<<>()>{[]<>})(({}())<{}[]>) +(<[[((<<({[(([()[]]([]<>)){{(){}}})]{<(<<><>><[]{}>)<<{}<>]([]<>)>>}}){(<{<{[]{}}[{}<>]>}>{({(<><>)<()[]>}{(( +{[{((<{<<<<([<[]{}>[[][]]]<[[]()][<>[]]>)[[<{}[]>{{}()}](<()<>>[<>[]])]>>[{([<()[]>{{}[]}](({}{} +(<([<((<{[<<(((){})<<><>>)<[{}()]<[]<>>}>[([[]{}](()[]))]>[<{({}()){{}()}}>[(([]<>)({}[])){ +{((((<(({[(<{<()]([][])}>){[<[[][]]>]<{{{}<>}[[][]]}>}]}[<{([{[]{}}]{[(){}]<<>()>})[(<()<>>{()<> +<<{([{<[[[<{([[]<>]){[<>()][(){}]}}[{(()[])[()[]]}]>][{{[{{}[]}<()<>>]>[[{(){}}[<>[]]][[<>[]]({}() +<((<[<(([<[{({<><>}(()[]))}]<[<[{}()]<()[]>>(<()()>)][[(<>())<()())]<{<>}<{}>>]>>({{<<<>[]><<>( +[({{{{{{(([[<<()()><<>[]>>[{[]<>}]]{(<{}<>>(()[]))<<[][]>(()())>}]([<({}()><[][]>>]<{[[][]]<[]<>>}( +(<(<{{<[{{{[<[<>[]]<[]{}>>[{<>{}}{()()}]]{<<{}()>(<>())>[[{}[]]]}}}}][{([{[([]{})<()[]>][[<>{}]{()[]}]}(([() +{[<<(<<<{[<<<<[]<>><()[]>>[({}[])[[]<>]]><((<><>)[{}<>])[{{}{}}[[]()]]>>{{{[<><>]<<>()>}[<[]{}>([][])] +<(<{<(<({[[{((()[])[{}<>])([<>()]>}{[({}{})[(){}]]<[()<>]({}[])>}]{[<{[]{}}><{[]{}}[[][]]>][[<{}<>><()[]>]<[ +{<({[<((<<<{{([]<>}{<><>}}<{<>{}}[[]<>]>}>{<[{{}}(()())]>}>(<<{[(){}]<()[]>}({(){}}[()<>])>[<<<>[]>{[] +{(({<{{{{{((<{[]<>>(<>{})>[[<>()]])<<(()())[<>]><[(){}][{}{}]>>)}({([[[]]]{<{}{}><()[]>})<{[(){}][[]()]}> +<<[<<[((({[[(<{}[]>({}())){[()()]}][{{<>{}}<<>()>}[<{}[]>[<>[]]]]]{<(([]())([]()))>{([()()]<<>[]>)[< +{([<<<({((<{{[<>[]][<>{}]}<<<>[]>[[][]]>}[{[()[]]<<>[]>}<[{}{}]<<><>>>]>({[<[]<>>]<<()()>([][])>}(((< +(<{<((<{[[<[(<[]{}>{<>{}})][<([][])[{}<>]>[<{}<>>]]><<[(<>[])[{}<>]]{<{}{}><{}[]>}>(<([]<>){()[]}>)>]]}{({[<( +[{{(({<<<<{{<({}()){{}{}}>[(()[])]}[([(){}]{<>[]})[(<>())[{}]]]}>[<<<{[]{}}{{}{}}>]{<[{}<>]>{(()())}}>[{(<[]< +{[<<{<[[({{<<{()[]}<<><>>>([[]<>>[{}[]])>}[[[([]<>)[<>{}]]]<<[<>()](<>{})>>]})(((({({}[])<<><>>}([<>])))((< +[{(({({<({(<{{[][]}({}<>)}[{()()}{[][]}]>(((<><>)(<>()))([{}()][{}()])))([{[[]()]<[]{}>}{( +{<{{<[<<[(<([({}())[{}()]]{{{}<>}<[]>}){[((){})<[][]>]<([]())({}[])>}>{<(((){})[()<>])(<()[]>[(){}] +{({({<{{(<([<[<><>]((){})>{{()[]}<<>{}>}]{[(<>())<{}[]>][{()<>}[[]()]>})>[[<{([])[()[]]}><<[(){}]({}())>>]{ +(<{{(<<<{(<(({<><>}{()[]})<[{}[]]([]())>)([(()<>)[[]<>]]({[]()}{{}{}}))))[(<{{()[]}<()[]>}((<><>))>[[ +<<[<([{<<[[({<[]<>><<>()>}{([]<>)[[]<>]}){[<{}{}><()[]>]<{<>()}{<>()}>}]{{<({}{})>})]>[<({< +(<<[[({[({(<{{[]{}}<[]{}}}>[<<()<>><{}<>>>(<()()>{{}<>})])}{<({({}[])<()>}<([]())<{}<>>>)<<[( +[<(<{(([(<([[<<>{}>(<>{})]](((()[])[[]()])[[{}<>]({}{})]))><<{<([]<>){[][]}>[{<>[]}<()<>>]}>(<((<>[]){()<>} +[<{[[{(([[{[{<[]<>>(<>[])}<<<>()>{{}{}}>][({[][]}<<>>){(<>[])[<><>]}]}(([([][])(<>[])]{(()())}))](<<{ +((<{<<([<[(([{()()}((){})][<()[]><()[]>])<{({}<>){{}<>}}>)[{(<<><>>)<{{}[]}>}({(<>()){()}}{([ +[({{{{((([({{(<>[])[[]<>]}([{}])}(({<><>}{<>()})[[{}[]]]))<{[[<>{}](()[])](<()<>>{[]()})}{{<()() +[<[<({[[({[[[<<>[]>]({[]}[<><>])]{[[()<>]([]())]<({}{})[[][]]>}]}<<(([()()]<()<>>)<{()()}[()[]]> +{<([[<[<<(([<(()[])(()<>)>{[<><>](<>[])]]{({{}})}){<((<><>)<{}<>>)((()[])[[]()])>})<<(<(())( +<[<((<{(<[[({{{}()}({}{})}}<[[()<>]]<[()()][<>]>>]<[([()()]({}<>))[<{}()>([]<>)]]>][{[[(()())[()]][[<>[] +[{(<(<[((<{(({(){}}[()[]])<{[][]}(<>{})>)[(<<><>>[()[]]]{[{}[]]{()<>}}]}><[<[[{}()](()[])][(<>()){<>()}]>[{ +([({<({{{[[<[{{}<>}([][])]{[(){}]{()<>}}>][{[{[]<>}][<{}()>]}]]}{((<[{{}[]}(()()))[{<>[]}]>({(<>{})<[]<>>} +{[({[<[([(<{((<>{})<{}{}>)}(<[[]()]<{}{}>>({{}[]}{[]<>}))><[((()[])(())){([]<>)[()[]]}]<({() +[(<<<([{[[[<<<<>>{{}<>}>[{[]()}]>[([[]{}]{[]<>}){[<>()]}]]]]}]<<((<([{[][]}<{}<>>]<[[]{}][[]<>])){{[<>[] +{[{{[[{{[<[<<{{}<>}<[]{}>><<()[]>[<>[]]>>[([<>{}](()()))]]>{({([{}[]](<><>))<<(){}>(<>{})>}({([]<>)[{}[])}(<< +<{([[{<[<{{([(<>{})[<>()]]){{[()[]]([])}}}[{<((){})([]())>}(<{[][]}<<>{}>>[[[][]]<()()>])]}>[ +{(<[({{(<<<{[(()())]<[<>[]][{}{}]>}[[<()[]>{[][]}]]>(<((<>())[()[]]){{()[]}{[][]}}>({{[]<>}<[][]>}(<[]{} +<{{[{((((<[{({[]{}}<(){}>)}[[{[][]}<<>{}>]<<[][]>{[]}>]]>{{([((){})<<>{}>]{{()()}})<[<<>()>](<<>>[() +<([({(<<<{[<{(()<>)(<>[])}([<>]{<>{}})>]<(<<<><>><{}()>>[([][]){[]<>}])>}{[([[[]<>]{(){}}]<[{}<>]{{}<>} +{<<<[{[(<[([([[]()]<()[]>)(([]())({}[]))]<[[()<>]]<[(){}]<[][]>>>)(<{<[][]>)<(()<>)<{}[]>> +{[[<[[<(<[[{(<()<>>)<{{}<>}[[][]]>}<<(()()){[]{}}>{<[]()>>>]]>)({<<<<[{}{}]{<>()}>{<<><>>([]{})}>>[([<()()>( +[[[{({{({[{[(({}())(<>[]))<<<>[]>[[]<>]>]<<<()()><<>>>[({}())(<>[])]>}[({<(){}>({}{})}[[{}()]]){[[[]<> +<(((<<[{[[<({({}<>)[{}()]}[{[]{}}([]())]){([[]<>]<[]<>>)[(()[])[{}[]]]}>(<<<{}[]><()[]>)>[[<{}( +[<<{[{{<<[[{<<()[]>(())><<{}()][<>()]>}({[{}{}][<>()]}<([]())([])>)]({{{[][]}[<>{}]}<[()]{{}()}>}(({ +{([(<[[<(<<<(<<><>><{}<>>)({{}()}[<>()])>{([(){}][[][]]){[<><>]{<><>}}}>>(({(<<><>><()<>>){{()<>}{(){}}}} +[(<([[[([(({[[<>[]]]}))([{{[{}()]([]<>)}{{<>[]}[[][]]}}(<([]())>[([]{}){()<>}])][<{<()[]><()()>}>{<([ +{([[<<[[<[[[<[{}[]]{[]<>}>[<{}()>{<>[]}]]]<[<{[][]}(<>[])>[[{}[]]<[][]>]][((()[])[<>{}]){{{}()}[(){}]}]>]> +{[[[<<{{[<<<([{}[]]<<>[]>)([()]<{}{}>)>({<<>{}>(()[])})>[(((<><>>{<>{}})<<<>[]>[[]()]>)({(()( +[[[{<<(({{[((<{}<>>[{}])({<>{}}<{}{}>))[(<{}()>(<>{}))(<<><>>{{}{}})]]({{<()>}}(<{(){}](<><>) +(({{[[[<[{<{[<{}()><<>{}>]}({[[]()]<{}[]>])>}[<{{(<><>)<{}()>}{{{}<>}{{}[]}}}(([<>[]]([][])) +<[<<{({[(<(<{({}<>)<[][]>}{(()[])({}[])}>{(<{}{}>>})>([<<<<>{}>((){})>[(()())[[]()]]><{[{}<>]<<> +[{<{[(<[[[{({({}<>)(()<>)}{<[][]>})}{[<[()[]]{[]<>]>{<{}()>(()())}]{[<()()>{<>[]}]<[<><>]>}}] +({{<{[[[([({{<<>()>(()<>)}(<{}()><()[]>)}<[[{}()]<{}[]>]{((){}){{}[]}}>)[([{<><>}<()<>>][(<><>)(()())] +(({<{{{[([{<<<{}{}>{[]{}}>([<>[]]{[]{}})>}{<<{[]{}}[[]{}]>{<<>{}>([]())}><(<(){}>({}[]))[(<>[])(()<>)]> +<[<(<{[[<([({[<>()][<>{}]}{{()()}[<><>]})]<[[<[]()>]<[{}<>]([])>]{<[<>[]]<<>()>]((<>())[<>{}])}>)([{<[[][]] +<<{{((({<<[[<{<>()}(()())>[(()[])[()<>]]]{({{}[]}[<><>])}]<{<<<><>>{{}[]}>}<[<[][]>[{}[]]](<()()>{[]{}}) +[<<{<{([{(({[{()[]}{()}][({}{})(()[])]}{({<>()})({[]()}({}[]))}))}][[[([[{<>[]}[{}[]]][[<><>]<()[]>]]([ +[([[{{<(<[{(<<()<>>><[[]()]{()()}>)<(({}()){[]<>})>}]>)>{<[<{{((()())({}<>)>{([]{}){{}<>}}}}<({ +((<<{(<[(<[[<({})>[{[][]}[<>[]]]]]>)]>)}([(<[[([(([][]))[{{}()}([][])]][((<><>){[]()})])]]<<( +<<<<(<{[{[({<{(){}}[<>]>[<[]<>>{<>()}]}[<[<>()]>{[[]<>][{}[]}}])(<{<(){}>[{}{}]}<(()<>)<()[]>>>)][<{{<[]< +(([[<{[({{{[<<<><>>(()())>({[]<>}(<>()))]{<[<>()][{}{}]>([[]()]((){}))}}((<({}{})[<>]>([<>()]{(){}})))}[<{([< +{[([<<[([[<[(<[]()>{()[]})<{<>()}{(){}}>]><<{<[][]><{}()>}(<[]<>>([]<>])>>]{[[<<{}<>>({}[] +{{[<<(<({[<<{<[]<>>}{(<>())}>[{[<>]{(){}}}]>]}{{{<([()<>]{<>{}})(<()[]>[[]()])><[<<>[]>({}{})]((()())[{ +[{{[<{<<{{<[<<()()>[{}]>([{}{}]({}{}))]{(<[][]>([][]))(([]{})<<>[]>)}>}({{<{<>[]}[<>()]>(({ +[<{[(<<[<<(([[<>[]]][([]())({}<>}])){(({[]()}{()<>})[<[]{}>(<><>)])[<[{}[]][{}{}]><{(){}}<{}{}>>]}>({<<[[]() +[({[<<<({<({({{}<>}[[]{}])}({({}<>)<<>[]>}))([<{(){}}>])>}{<{[[{{}{}}(<>{})]]<[[()[]]<<><>>]{[<><>]<{}()> +[<{<<<<(([([[{{}()}[<>]][((){})[[]{}]]](({[]()}[[]()])[{[]()}(<>[])])){[{<<><>>{{}<>}}<{()[]}[[] +[{({<(((<({{({[]()}))[((()())<[][]>)<{<>{}}(<><>)>]}{[<[[][]](<><>)>][[{<>{}}[<>{}]](<<>[]>{[][]})]})>)))>} +[[{[[{({[([(<<<>[]>[<><>]>([{}()][<>()]))<<[<>{}]{{}<>}>[<<>{}>([]{}]]>])<(<(<[]()>)[(()[]) +<([(<<[<<({{{([]{})}[{()<>}<[]()>]}}{(<{[]{}}([][])>)})[[[<[[][]]<<><>>><{{}{}}(<><>)>]<<(()())[(){}]>{ +[(<{[{({[({<{<{}[]><<>()>}{<{}()>[[][]]}><<{{}()}{(){}}>>}[<{{<>{}}{()())}>]){[<[{()()}<[]()>]><[[{ +<{<[{([([<<[<[<>()]{(){}}>][({{}[]}((){}))[<[][]><()<>>]]>>{{{<{<><>}{(){}}>({<><>}<()))}({<()><{}[]>}(<( +({((([[{([<[(([]{})){[()<>][<><>]}]>{[<<{}{}><()[]>>(<<>>)]({<<><>>[<>{}]}{<[]{}><<>{}>})}])[(<(([<><>][{}{ +<[<(([(({([[<(()[])[[]{}]>{<<>{}>(<>{})}]]([([<>{}]{[]{}})][{<()>{()[]}}(({}[])(<>[]))]))})({{[(({(){}}[<> diff --git a/day10/src/main.zig b/day10/src/main.zig new file mode 100644 index 0000000..c6b4003 --- /dev/null +++ b/day10/src/main.zig @@ -0,0 +1,424 @@ +const std = @import("std"); + +pub fn main() anyerror!void { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + + // Read our input + var f = try std.fs.cwd().openFile("input", .{}); + defer f.close(); + var contents = try f.readToEndAlloc(alloc, std.math.maxInt(u32)); + defer alloc.free(contents); + + var lit = std.mem.tokenize(contents, "\n"); + var corrupt_score: u32 = 0; + var completion_scores = std.ArrayList(u64).init(alloc); + defer completion_scores.deinit(); + var line_id: usize = 0; + while (lit.next()) |line| { + //std.log.debug("{any}", .{nodes}); + var tree = try line_to_tree(alloc, line, line_id); + corrupt_score += tree.corrupt; + if (tree.corrupt == 0) { + // Try completion + var score = try tree.autocomplete(); + std.log.debug("Line {} autocompletion score {}", .{line_id, score}); + try completion_scores.append(score); // score + } + line_id += 1; + } + std.log.info("[Part 1] Sum of corrupt line scores: {}", .{corrupt_score}); + std.sort.sort(u64, completion_scores.items, {}, comptime std.sort.asc(u64)); + + std.log.info( + "[Part 2] Completion scores: {}", + .{completion_scores.items[completion_scores.items.len/2]} + ); +} + +fn line_to_tree(alloc: *std.mem.Allocator, line: []const u8, id: usize) !Tree { + var tkzr = Tokenizer.init(line); + var tkns = std.ArrayList(Token).init(alloc); + var corrupt_score: u32 = 0; + while (true) { + var tkn = tkzr.next(); + if (tkn.tag != .eos) { + try tkns.append(tkn); + } + else { + break; + } + } + //std.log.debug("{} tokens", .{tkns.items.len}); + var nodes = std.ArrayList(Node).init(alloc); + try nodes.append(.{ + .index = 0, + .parent = 0, + .tag = .root, + .start_index = 0, + }); + var current_node: usize = 0; + for (tkns.items) |tkn, index| { + switch(tkn.tag) { + .r_paren, + .r_brace, + .r_bracket, + .r_angle_bracket, + => { + if (current_node == 0) { + std.log.debug("Closing token before opening token: {}", + .{tkn}); + break; + } + else { + //std.log.warn("Current node: {}", .{current_node}); + nodes.items[current_node].end_index = index; + if (nodes.items[current_node].tag != token_tag_to_node_tag(tkns.items[index].tag)) { + std.log.warn( + "Line {}: mismatched tags: {} != {}", + .{id, tkns.items[nodes.items[current_node].start_index].tag, + tkns.items[index].tag} + ); + corrupt_score += switch(tkns.items[index].tag) { + .r_paren => @as(u32, 3), + .r_bracket => @as(u32, 57), + .r_brace => @as(u32, 1197), + .r_angle_bracket => @as(u32, 25137), + else => @as(u32, 0), + }; + break; + } + current_node = nodes.items[current_node].parent; + } + }, + .eos, + .invalid, + => unreachable, + else => { + // Opens a new node + var node = Node { + .index = nodes.items.len + 1, + .parent = current_node, + .start_index = index, + .tag = switch(tkn.tag) { + .l_paren => .paren, + .l_brace => .brace, + .l_bracket => .bracket, + .l_angle_bracket => .angle_bracket, + else => unreachable, + } + }; + try nodes.append(node); + try nodes.items[current_node].children.append(alloc, nodes.items.len - 1); + current_node = nodes.items.len - 1; + //std.log.warn("Start new node from token index {} at {}: {any}", + // .{index, current_node, node}); + }, + } + } + return Tree { + .nodes = nodes, + .tokens = tkns, + .corrupt = corrupt_score, + }; +} + +pub const Tree = struct { + nodes: std.ArrayList(Node), + tokens: std.ArrayList(Token), + corrupt: u32 = 0, + + pub fn autocomplete(self: *Tree) anyerror!u64 { + // I guess we want to traverse the tree, adding in tokens for end_index + // where necessary + var node = self.nodes.items[0]; + var score: u64 = 0; + for (node.children.items) |n| { + score += try self.complete(&self.nodes.items[n], score); + } + return score; + } + + fn complete(self: *Tree, node: *Node, score: u64) anyerror!u64 { + var s = score; + for (node.*.children.items) |n| { + s += try self.complete(&self.nodes.items[n], score); + } + if (node.*.end_index) |ei| { + return s; + } + else { + try self.tokens.append(.{ + .tag = switch(node.*.tag) { + .paren => .r_paren, + .brace => .r_brace, + .bracket => .r_bracket, + .angle_bracket => .r_angle_bracket, + else => .invalid, + }, + .loc = .{.start = 0, .end = 0}, // fake + }); + node.*.end_index = self.tokens.items.len - 1; + s *= 5; + s += switch(node.*.tag) { + .paren => @as(u64, 1), + .brace => @as(u64, 3), + .bracket => @as(u64, 2), + .angle_bracket => @as(u64, 4), + else => @as(u64, 0), + }; + } + return s; + } +}; + +pub const Node = struct { + index: usize, + parent: usize, + children: std.ArrayListUnmanaged(usize) = .{}, + tag: Tag, + start_index: usize, + end_index: ?usize = null, + pub const Tag = enum { + root, + paren, + brace, + bracket, + angle_bracket, + invalid, + }; +}; + +fn token_tag_to_node_tag(t: Token.Tag) Node.Tag { + switch(t) { + .l_paren, + .r_paren, + => return .paren, + .l_brace, + .r_brace, + => return .brace, + .l_bracket, + .r_bracket, + => return .bracket, + .l_angle_bracket, + .r_angle_bracket, + => return .angle_bracket, + else => return .invalid + } +} + +pub const Token = struct { + tag: Tag, + loc: Loc, + + pub const Loc = struct { + start: usize, + end: usize, + }; + pub const Tag = enum { + l_paren, + r_paren, + l_brace, + r_brace, + l_bracket, + r_bracket, + l_angle_bracket, + r_angle_bracket, + eos, + invalid, + }; + + pub fn lexeme(tag: Tag) ?[]const u8 { + return switch (tag) { + .l_paren => "(", + .r_paren => ")", + .l_brace => "{", + .r_brace => "}", + .l_bracket => "[", + .r_bracket => "]", + .l_angle_bracket => "<", + .r_angle_bracket => ">", + else => "", + }; + } + +}; + +pub const Tokenizer = struct { + index: usize, + buffer: []const u8, + + pub fn init(buffer: []const u8) Tokenizer { + return Tokenizer{ + .buffer = buffer, + .index = 0, + }; + } + + pub fn next(self: *Tokenizer) Token { + const start_index = self.index; + var result = Token { + .tag = .eos, + .loc = .{ + .start = start_index, + .end = undefined, + }, + }; + while (self.index < self.buffer.len) : (self.index += 1) { + const c = self.buffer[self.index]; + switch (c) { + '(' => { + result.tag = .l_paren; + self.index += 1; + break; + }, + ')' => { + result.tag = .r_paren; + self.index += 1; + break; + }, + '{' => { + result.tag = .l_brace; + self.index += 1; + break; + }, + '}' => { + result.tag = .r_brace; + self.index += 1; + break; + }, + '[' => { + result.tag = .l_bracket; + self.index += 1; + break; + }, + ']' => { + result.tag = .r_bracket; + self.index += 1; + break; + }, + '<' => { + result.tag = .l_angle_bracket; + self.index += 1; + break; + }, + '>' => { + result.tag = .r_angle_bracket; + self.index += 1; + break; + }, + else => { + result.tag = .invalid; + self.index += 1; + break; + }, + } + } + if (result.tag == .eos) { + result.loc.start = self.index; + } + result.loc.end = self.index; + return result; + } +}; + +test "tokenizer - all" { + try testTokenize("()[]{}<>", &.{ + .l_paren, .r_paren, .l_bracket, .r_bracket, .l_brace, .r_brace, + .l_angle_bracket, .r_angle_bracket + }); +} + +test "tokenizer - early end" { + try testTokenize("({", &.{ + .l_paren, .l_brace, + }); +} + +test "tokenizer - invalid" { + try testTokenize("({}){}[([])<>]]", 1); + try std.testing.expectEqual(@as(u32, 0), tree.corrupt); +} + +test "a" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "{([(<{}[<>[]}>{[]{[(<()>", 1); + try std.testing.expectEqual(@as(u32, 1197), tree.corrupt); +} + +test "b" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "[[<[([]))<([[{}[[()]]]", 1); + try std.testing.expectEqual(@as(u32, 3), tree.corrupt); +} + +test "c" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "[{[{({}]{}}([{[{{{}}([]", 1); + try std.testing.expectEqual(@as(u32, 57), tree.corrupt); +} + +test "d" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "[<(<(<(<{}))><([]([]()", 1); + try std.testing.expectEqual(@as(u32, 3), tree.corrupt); +} + +test "e" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "<{([([[(<>()){}]>(<<{{", 1); + try std.testing.expectEqual(@as(u32, 25137), tree.corrupt); +} + +test "autocomplete simple" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "<", 1); + try std.testing.expectEqual(@as(u32, 0), tree.corrupt); + var score = try tree.autocomplete(); + try std.testing.expectEqual(@as(u64, 4), score); +} + +test "autocomplete" { + var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); + defer arena.deinit(); + const alloc = &arena.allocator; + var tree = try line_to_tree(alloc, "<{([{{}}[<[[[<>{}]]]>[]]", 1); + try std.testing.expectEqual(@as(u32, 0), tree.corrupt); + var score = try tree.autocomplete(); + try std.testing.expectEqual(@as(u64, 294), score); +}