feat(day17): but in javascript :(

Will do this in zig too, but really wanted to know if my strategy worked
This commit is contained in:
2024-08-04 14:27:00 +01:00
parent a6d6fbd31c
commit 241ea7653b
9 changed files with 667 additions and 7 deletions

View File

@ -1,3 +1,4 @@
zig-cache
zig-out
input.txt
node_modules

175
AdventOfCode2023/src/day17/.gitignore vendored Normal file
View File

@ -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

Binary file not shown.

View File

@ -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});
}

View File

@ -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<string, string>();
const distances = new Map<string, number>();
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);

View File

@ -0,0 +1,11 @@
{
"name": "day17",
"module": "index.ts",
"type": "module",
"devDependencies": {
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
}
}

View File

@ -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
]
}
}

View File

@ -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;

View File

@ -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);
}