From 241ea7653bf0933c8b51afa1016522802f3b55c4 Mon Sep 17 00:00:00 2001 From: John Costa Date: Sun, 4 Aug 2024 14:27:00 +0100 Subject: [PATCH] feat(day17): but in javascript :( Will do this in zig too, but really wanted to know if my strategy worked --- AdventOfCode2023/.gitignore | 1 + AdventOfCode2023/src/day17/.gitignore | 175 +++++++++++++++ AdventOfCode2023/src/day17/bun.lockb | Bin 0 -> 2765 bytes AdventOfCode2023/src/day17/day17.zig | 182 ++++++++++++++++ AdventOfCode2023/src/day17/index.ts | 266 +++++++++++++++++++++++ AdventOfCode2023/src/day17/package.json | 11 + AdventOfCode2023/src/day17/tsconfig.json | 22 ++ AdventOfCode2023/src/graph/graph.zig | 10 +- AdventOfCode2023/src/main.zig | 7 +- 9 files changed, 667 insertions(+), 7 deletions(-) create mode 100644 AdventOfCode2023/src/day17/.gitignore create mode 100755 AdventOfCode2023/src/day17/bun.lockb create mode 100644 AdventOfCode2023/src/day17/day17.zig create mode 100644 AdventOfCode2023/src/day17/index.ts create mode 100644 AdventOfCode2023/src/day17/package.json create mode 100644 AdventOfCode2023/src/day17/tsconfig.json diff --git a/AdventOfCode2023/.gitignore b/AdventOfCode2023/.gitignore index ddc746a..92c7193 100644 --- a/AdventOfCode2023/.gitignore +++ b/AdventOfCode2023/.gitignore @@ -1,3 +1,4 @@ zig-cache zig-out input.txt +node_modules diff --git a/AdventOfCode2023/src/day17/.gitignore b/AdventOfCode2023/src/day17/.gitignore new file mode 100644 index 0000000..468f82a --- /dev/null +++ b/AdventOfCode2023/src/day17/.gitignore @@ -0,0 +1,175 @@ +# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore + +# Logs + +logs +_.log +npm-debug.log_ +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Caches + +.cache + +# Diagnostic reports (https://nodejs.org/api/report.html) + +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# Runtime data + +pids +_.pid +_.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover + +lib-cov + +# Coverage directory used by tools like istanbul + +coverage +*.lcov + +# nyc test coverage + +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) + +.grunt + +# Bower dependency directory (https://bower.io/) + +bower_components + +# node-waf configuration + +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) + +build/Release + +# Dependency directories + +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) + +web_modules/ + +# TypeScript cache + +*.tsbuildinfo + +# Optional npm cache directory + +.npm + +# Optional eslint cache + +.eslintcache + +# Optional stylelint cache + +.stylelintcache + +# Microbundle cache + +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history + +.node_repl_history + +# Output of 'npm pack' + +*.tgz + +# Yarn Integrity file + +.yarn-integrity + +# dotenv environment variable files + +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) + +.parcel-cache + +# Next.js build output + +.next +out + +# Nuxt.js build / generate output + +.nuxt +dist + +# Gatsby files + +# Comment in the public line in if your project uses Gatsby and not Next.js + +# https://nextjs.org/blog/next-9-1#public-directory-support + +# public + +# vuepress build output + +.vuepress/dist + +# vuepress v2.x temp and cache directory + +.temp + +# Docusaurus cache and generated files + +.docusaurus + +# Serverless directories + +.serverless/ + +# FuseBox cache + +.fusebox/ + +# DynamoDB Local files + +.dynamodb/ + +# TernJS port file + +.tern-port + +# Stores VSCode versions used for testing VSCode extensions + +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/AdventOfCode2023/src/day17/bun.lockb b/AdventOfCode2023/src/day17/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..92338aa9579c7e9471661a7e436871bd631d13e1 GIT binary patch literal 2765 zcmY#Z)GsYA(of3F(@)JSQ%EY!;{sycoc!eMw9K4T-L(9o+{6;yG6OCq1_p*5W>f10 z7F@dJ`c7$skVM?#*^QFS&k7u~%8z{0J~4M~p)D6s5fHFJC56*z16;?pxK=Vx8YoUhuTqurXC#0b(z0RZ_L1cZUu8fZVrJP;NIVqySHe-co! z6;QtjkOumTfsYu?Ks7M^D}ZcApng^$Ed|6Nniv4n{}QO!1gIYrBOtvXHxL70`Za-` zwt?CY($7MSW~dsF888ge2MRx!`$05_4^I$4HcTyuCanG+|38ocR0fO;5Oa}@W?5{X zbz|Y8ooq3k@fs{@^1e2lhKa1A4X&4%H(9&qg!^rB`sC3OEE6o}&*xWj(W%U?FKX5u zlm_P4M3pwDF3)c~@#iVvoqI zJ0(jFnO!KZw_9D3p%`dzlyTzWV!QqsnI_D3(^$(3qqJuM4T71=3^No+v#161SU+D_ z;>5KouyFDs`LG6&ungn3N3GA;Km1x&vf6ybM#X0VQ~l1W=FzBuWx%QlCX=OT=>m28$|v!yWh3ogC4+u>aQgcG+XMSTyu z%h(>9PWUg30|r^cHAK7EX#IIh2ORF;)|rG+`P`- z{u5*f9RMsWU^F>aBBv{I^bbuv8=%?z2$bfsDK1LZ%gie-NzBR7gH<|uAw{XFb_zxY z3dNaKsrhL-3MLAPIhpBs`Dx(#<==k@0EGc4{d|C$#=;H>6)u}nV7yq~}^ul3J9Pm=j!5l$n=qr(lTi zrUjBWONx`hW*+!0vb1Q*1QAd}10=?OZ?Vlg*}`2V#l@*bK=WcRD>MeBdH;|FW(-ih z-(q2UhOvV0-vy`}Ou;b^3>_O#m>S{=QihVsg4E*VqRfI4P%{S1%P1)+D7MnqFG@|% zEG{Xk)XOW#%_`Q*FG|;ktI^j*uyqaf4E2l*^-9vK9KZ(Xmls1BdHE@+c#K0*1vC(( zSQiovh9)4>O7l`OlQRiA3RxM@7-X(4IPOeAh7oo~c`;CTd9f}yjTnLSKms2U;CS>S V!VqWxf&;YNR1fSCaEuN@0sw> 64); + const y: usize = @truncate(c); + + return Coord{ .x = x, .y = y }; +} + +const MAX_STRAIGHT_LINE_MOVE = 3; + +fn create_nodes(allocator: std.mem.Allocator, input: [][]const u8, label: []const u8) !std.AutoHashMap(u128, *graph.Node) { + var node_map = std.AutoHashMap(u128, *graph.Node).init(allocator); + + for (0..input.len) |y| { + for (0..input[0].len) |x| { + const c = coord(x, y); + + const node = try allocator.create(graph.Node); + node.label = label; + node.work_distance = 0; + node.out_edges = undefined; + + try node_map.put(c, node); + } + } + + return node_map; +} + +pub fn solve(input: [][]const u8) !void { + const allocator = std.heap.page_allocator; + + var h_map = try create_nodes(allocator, input, "h-node"); + var v_map = try create_nodes(allocator, input, "v-node"); + + var clean_h_map = std.AutoHashMap(u128, *graph.Node).init(allocator); + var clean_v_map = std.AutoHashMap(u128, *graph.Node).init(allocator); + + var h_map_iter = h_map.iterator(); + while (h_map_iter.next()) |entry| { + const map_c = entry.key_ptr.*; + const h_node = entry.value_ptr.*; + + const c = from_coord(map_c); + + var y = c.y; + var counter: usize = MAX_STRAIGHT_LINE_MOVE; + + var edge_array = std.ArrayList(graph.Edge).init(allocator); + + var accumulated_distance: usize = 0; + + while (counter > 0) : (counter -= 1) { + if (y == 0) { + break; + } + + const edge_out_coord = coord(c.x, y); + const out_node = v_map.get(edge_out_coord).?; + + accumulated_distance += input[y][c.x] - '0'; + + const edge = graph.Edge{ .in_node = h_node, .out_node = out_node, .distance = accumulated_distance }; + try edge_array.append(edge); + + y -= 1; + } + + y = c.y; + counter = MAX_STRAIGHT_LINE_MOVE; + accumulated_distance = 0; + + while (counter > 0) : (counter -= 1) { + if (y == input.len - 1) { + break; + } + + const edge_out_coord = coord(c.x, y); + const out_node = v_map.get(edge_out_coord).?; + + accumulated_distance += input[y][c.x] - '0'; + + const edge = graph.Edge{ .in_node = h_node, .out_node = out_node, .distance = accumulated_distance }; + try edge_array.append(edge); + + y += 1; + } + + h_node.out_edges = try edge_array.toOwnedSlice(); + try clean_h_map.put(map_c, h_node); + } + + h_map.deinit(); + + var v_map_iter = v_map.iterator(); + while (v_map_iter.next()) |entry| { + const map_c = entry.key_ptr.*; + const v_node = entry.value_ptr.*; + + const c = from_coord(map_c); + + var x = c.x; + var counter: usize = MAX_STRAIGHT_LINE_MOVE; + + var edge_array = std.ArrayList(graph.Edge).init(allocator); + var accumulated_distance: usize = 0; + + while (counter > 0) : (counter -= 1) { + if (x == 0) { + break; + } + + const edge_out_coord = coord(x, c.y); + const out_node = clean_h_map.get(edge_out_coord).?; + + accumulated_distance += input[c.y][x] - '0'; + + const edge = graph.Edge{ .in_node = v_node, .out_node = out_node, .distance = accumulated_distance }; + try edge_array.append(edge); + + x -= 1; + } + + x = c.x; + counter = MAX_STRAIGHT_LINE_MOVE; + accumulated_distance = 0; + + while (counter > 0) : (counter -= 1) { + if (x == input[0].len - 1) { + break; + } + + const edge_out_coord = coord(x, c.y); + const out_node = clean_h_map.get(edge_out_coord).?; + + accumulated_distance += input[c.y][x] - '0'; + + const edge = graph.Edge{ .in_node = v_node, .out_node = out_node, .distance = accumulated_distance }; + try edge_array.append(edge); + + x += 1; + } + + v_node.out_edges = try edge_array.toOwnedSlice(); + try clean_v_map.put(map_c, v_node); + } + + v_map.deinit(); + + var all_nodes = try allocator.alloc(*graph.Node, input.len * input[0].len * 2); + var index: usize = 0; + + var node_iter = clean_h_map.valueIterator(); + while (node_iter.next()) |n| { + all_nodes[index] = n.*; + index += 1; + } + + node_iter = clean_v_map.valueIterator(); + while (node_iter.next()) |n| { + all_nodes[index] = n.*; + index += 1; + } + + const g = graph.Graph{ .nodes = all_nodes }; + + const start = clean_v_map.get(coord(0, 0)).?; + const end = clean_v_map.get(coord(input[0].len - 1, input.len - 1)).?; + + const part1 = try graph.dijkstra(allocator, g, start, end); + print("Part 1: {}\n", .{part1}); +} diff --git a/AdventOfCode2023/src/day17/index.ts b/AdventOfCode2023/src/day17/index.ts new file mode 100644 index 0000000..4d08d08 --- /dev/null +++ b/AdventOfCode2023/src/day17/index.ts @@ -0,0 +1,266 @@ +const input = await Bun.file("./input.txt").text(); +const processed_input = input + .split("\n") + .slice(0, -1) + .map((s) => s.split("").map(Number)); + +class Node { + public id: string; + public x: number; + public y: number; + public weight: number; + + constructor(id: string, x: number, y: number, weight: number) { + this.id = `${id}-${x}-${y}`; + this.x = x; + this.y = y; + this.weight = weight; + } +} + +class NodeWithWork extends Node { + public work_distance: number; + + constructor(node: Node, work_distance: number) { + super(node.id.split("-")[0], node.x, node.y, node.weight); + this.work_distance = work_distance; + } +} + +function get_from_id(n: NodeWithWork[], id: string): NodeWithWork | undefined { + return n.find((n) => n.id === id); +} + +class Edge { + public from: string; + public to: string; + public weight: number; + + constructor(from: string, to: string, weight: number) { + this.from = from; + this.to = to; + this.weight = weight; + } +} + +class Graph { + public nodes: Node[]; + public edges: Edge[]; + + constructor(nodes: Node[], edges: Edge[]) { + this.nodes = nodes; + this.edges = edges; + } + + public get_node_from_id(id: string): Node { + const node = this.nodes.find((n) => n.id === id); + if (node == null) { + throw new Error("Could not find node"); + } + + return node; + } + + public get_node_or_throw(x: number, y: number): Node { + const node = this.nodes.find((n) => n.x === x && n.y === y); + if (node == null) { + throw new Error("Could not find node"); + } + + return node; + } + + public get_node_out_edges(node: Node): Edge[] { + return this.edges.filter((e) => e.from === node.id); + } + + public add_edge(edge: Edge): void { + this.edges.push(edge); + } + + public add_node(node: Node): void { + this.nodes.push(node); + } + + public print(): void { + for (const n of this.nodes) { + console.log(`Node: ${n.id}`); + for (const e of h_nodes.get_node_out_edges(n)) { + console.log(` Edge: ${e.from} --- ${e.to} | Weight: ${e.weight}`); + } + } + } + + public dijkstras(source: Node, destination: Node): number { + const work_map = new Map(); + const distances = new Map(); + + const work_queue = this.nodes.map((n) => new NodeWithWork(n, 9999999999)); + + work_queue.find((n) => n.id === source.id)!.work_distance = 0; + + while (work_queue.length > 0) { + work_queue.sort((a, b) => a.work_distance - b.work_distance); + const closent_node = work_queue[0]; + + distances.set(closent_node.id, closent_node.work_distance); + + work_queue.splice(0, 1); + + for (const e of this.get_node_out_edges(closent_node)) { + const neighbor = get_from_id(work_queue, e.to); + if (neighbor == null) { + continue; + } + + const alternative_path = closent_node.work_distance + e.weight; + + if (alternative_path < neighbor.work_distance) { + neighbor.work_distance = alternative_path; + work_map.set(neighbor.id, closent_node.id); + } + } + } + + return distances.get(destination.id)!; + } + + public static from_graphs(...graphs: Graph[]) { + return new Graph( + graphs.flatMap((g) => g.nodes), + graphs.flatMap((g) => g.edges), + ); + } +} + +function create_graph(id: string, input: number[][]): Graph { + const nodes: Node[] = []; + + for (const [y, row] of input.entries()) { + for (const [x, n] of row.entries()) { + nodes.push(new Node(id, x, y, n)); + } + } + + return new Graph(nodes, []); +} + +function create_interlaced_edges( + first: Graph, + second: Graph, + offset: number, + distance: number, +) { + for (const n of first.nodes) { + let accumulated_weight = 0; + for (let i = 1; i < offset && n.x + i < processed_input[0].length; i++) { + accumulated_weight += processed_input[n.y][n.x + i]; + } + + // Right + for ( + let i = offset; + i <= distance && n.x + i < processed_input[0].length; + i++ + ) { + accumulated_weight += processed_input[n.y][n.x + i]; + const out_node = second.get_node_or_throw(n.x + i, n.y); + + first.add_edge(new Edge(n.id, out_node.id, accumulated_weight)); + } + + accumulated_weight = 0; + for (let i = -1; i > -offset && n.x + i >= 0; i--) { + accumulated_weight += processed_input[n.y][n.x + i]; + } + + // Left + for (let i = -offset; -i <= distance && n.x + i >= 0; i--) { + accumulated_weight += processed_input[n.y][n.x + i]; + const out_node = second.get_node_or_throw(n.x + i, n.y); + + first.add_edge(new Edge(n.id, out_node.id, accumulated_weight)); + } + } + + for (const n of second.nodes) { + let accumulated_weight = 0; + for (let i = 1; i < offset && n.y + i < processed_input.length; i++) { + accumulated_weight += processed_input[n.y + i][n.x]; + } + // Right + for ( + let i = offset; + i <= distance && n.y + i < processed_input.length; + i++ + ) { + accumulated_weight += processed_input[n.y + i][n.x]; + const out_node = first.get_node_or_throw(n.x, n.y + i); + + first.add_edge(new Edge(n.id, out_node.id, accumulated_weight)); + } + + accumulated_weight = 0; + for (let i = -1; i > -offset && n.y + i >= 0; i--) { + accumulated_weight += processed_input[n.y + i][n.x]; + } + + // Left + for (let i = -offset; -i <= distance && n.y + i >= 0; i--) { + accumulated_weight += processed_input[n.y + i][n.x]; + const out_node = first.get_node_or_throw(n.x, n.y + i); + + first.add_edge(new Edge(n.id, out_node.id, accumulated_weight)); + } + } +} + +const v_nodes = create_graph("v", processed_input); +const h_nodes = create_graph("h", processed_input); + +// create_interlaced_edges(h_nodes, v_nodes, 1, 3); +create_interlaced_edges(h_nodes, v_nodes, 4, 10); + +const full_graph = Graph.from_graphs(h_nodes, v_nodes); + +// full_graph.print(); + +const INITIAL = "initial-0-0"; +const FINAL = `final-${processed_input[0].length - 1}-${ + processed_input.length - 1 +}`; + +full_graph.add_node(new Node("initial", 0, 0, 0)); +full_graph.add_node( + new Node( + "final", + processed_input[0].length - 1, + processed_input.length - 1, + 0, + ), +); + +full_graph.add_edge(new Edge(INITIAL, "v-0-0", 0)); +full_graph.add_edge(new Edge(INITIAL, "h-0-0", 0)); + +full_graph.add_edge( + new Edge( + `v-${processed_input[0].length - 1}-${processed_input.length - 1}`, + FINAL, + 0, + ), +); +full_graph.add_edge( + new Edge( + `h-${processed_input[0].length - 1}-${processed_input.length - 1}`, + FINAL, + 0, + ), +); + +const shortest_path = full_graph.dijkstras( + full_graph.get_node_from_id(INITIAL), + full_graph.get_node_from_id(FINAL), +); + +console.log(shortest_path); diff --git a/AdventOfCode2023/src/day17/package.json b/AdventOfCode2023/src/day17/package.json new file mode 100644 index 0000000..52c3c54 --- /dev/null +++ b/AdventOfCode2023/src/day17/package.json @@ -0,0 +1,11 @@ +{ + "name": "day17", + "module": "index.ts", + "type": "module", + "devDependencies": { + "bun-types": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } +} \ No newline at end of file diff --git a/AdventOfCode2023/src/day17/tsconfig.json b/AdventOfCode2023/src/day17/tsconfig.json new file mode 100644 index 0000000..7556e1d --- /dev/null +++ b/AdventOfCode2023/src/day17/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + "allowImportingTsExtensions": true, + "noEmit": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" // add Bun global + ] + } +} diff --git a/AdventOfCode2023/src/graph/graph.zig b/AdventOfCode2023/src/graph/graph.zig index d472b56..fe644f0 100644 --- a/AdventOfCode2023/src/graph/graph.zig +++ b/AdventOfCode2023/src/graph/graph.zig @@ -5,21 +5,21 @@ const print = std.debug.print; const log = std.log; const mem = std.mem; -const Edge = struct { +pub const Edge = struct { in_node: *Node, out_node: *Node, distance: usize, }; -const Node = struct { +pub const Node = struct { out_edges: []const Edge, label: []const u8, work_distance: usize, }; -const Graph = struct { +pub const Graph = struct { nodes: []*Node, }; @@ -55,7 +55,7 @@ fn find_shortest_index(list: *std.ArrayList(*const Node)) usize { return shortestIndex; } -fn dijkstra(allocator: std.mem.Allocator, graph: Graph, source: *Node, destination: *Node) !usize { +pub fn dijkstra(allocator: std.mem.Allocator, graph: Graph, source: *Node, destination: *Node) !usize { var work_map = std.AutoHashMap(*const Node, *const Node).init(allocator); // this is for sure the wrong data structure @@ -73,6 +73,8 @@ fn dijkstra(allocator: std.mem.Allocator, graph: Graph, source: *Node, destinati const closest_node_index = find_shortest_index(&work_queue); const closest_node = work_queue.orderedRemove(closest_node_index); + print("Current node: {}\n", .{closest_node.work_distance}); + for (closest_node.out_edges) |edge| { const neighbor = edge.out_node; const alternative_path = closest_node.work_distance + edge.distance; diff --git a/AdventOfCode2023/src/main.zig b/AdventOfCode2023/src/main.zig index 6cf27d0..e595190 100644 --- a/AdventOfCode2023/src/main.zig +++ b/AdventOfCode2023/src/main.zig @@ -13,7 +13,8 @@ const std = @import("std"); // const day14 = @import("./day14/day14.zig"); // const day13 = @import("./day13/day13.zig"); // const day15 = @import("./day15/day15.zig"); -const day16 = @import("./day16/day16.zig"); +// const day16 = @import("./day16/day16.zig"); +const day17 = @import("./day17/day17.zig"); // const day18 = @import("./day18/day18.zig"); // const day19 = @import("./day19/day19.zig"); // const day21 = @import("./day21/day21.zig"); @@ -23,8 +24,8 @@ const utils = @import("utils.zig"); pub fn main() !void { const allocator = std.heap.page_allocator; - const input = try utils.getInput("./src/day16/input.txt", allocator); + const input = try utils.getInput("./src/day17/input.txt", allocator); defer allocator.free(input); - try day16.solve(input); + try day17.solve(input); }