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 0000000..92338aa Binary files /dev/null and b/AdventOfCode2023/src/day17/bun.lockb differ diff --git a/AdventOfCode2023/src/day17/day17.zig b/AdventOfCode2023/src/day17/day17.zig new file mode 100644 index 0000000..f036077 --- /dev/null +++ b/AdventOfCode2023/src/day17/day17.zig @@ -0,0 +1,182 @@ +const graph = @import("../graph/graph.zig"); +const std = @import("std"); + +const print = std.debug.print; + +fn coord(myX: usize, myY: usize) u128 { + return @as(u128, myX) << 64 | @as(u128, myY); +} + +const Coord = struct { x: usize, y: usize }; + +fn from_coord(c: u128) Coord { + const x: usize = @truncate(c >> 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); }