advent-of-code/2021/day-15/common.d

291 lines
7.0 KiB
D

module common;
import std.algorithm.iteration : filter, map;
import std.algorithm.searching : minElement, canFind;
import std.array : split;
import std.container.array : Array;
import std.container.rbtree : redBlackTree;
import std.container.binaryheap : BinaryHeap;
import std.conv : to;
import std.stdio;
import std.string : stripRight;
import std.typecons : tuple, Tuple;
private alias Buffer = Array!(Array!(uint));
struct CaveMap
{
private Buffer positions;
private const ulong tile_width;
private const ulong tile_height;
this(string path)
{
positions = parseFile(path);
}
this(string path, uint tiles)
{
auto buffer = parseFile(path);
tile_height = buffer.length;
tile_width = buffer[0].length;
tileRight(buffer, tiles);
tileDown(buffer, tiles);
positions = buffer;
}
private void tileRight(Buffer buffer, uint tiles)
{
foreach (tile_x; 1 .. tiles)
{
foreach (y; 0 .. tile_height)
{
foreach (x; 0 .. tile_width)
{
auto next_val = (buffer[y][x] + tile_x - 1) % 9;
next_val = next_val == 0 ? 1 : next_val + 1;
buffer[y].insertBack(next_val);
}
}
}
}
private void tileDown(Buffer buffer, uint tiles)
{
foreach (tile_y; 1 .. tiles)
{
const tile_y_begin = tile_y * tile_height;
foreach (y; 0 .. tile_height)
{
buffer.insertBack(Array!uint());
foreach (tile_x; 0 .. tiles)
{
const tile_x_begin = tile_x * tile_width;
foreach (x; 0 .. tile_width)
{
auto next_val = (buffer[y][tile_x_begin + x] + tile_y - 1) % 9;
next_val = next_val == 0 ? 1 : next_val + 1;
buffer[tile_y_begin + y].insertBack(next_val);
}
}
}
}
}
private Array!(Array!uint) parseFile(string path)
{
auto file = File(path, "r");
auto buffer = Buffer();
string line;
while ((line = file.readln()) !is null)
{
auto line_buffer = Array!uint();
foreach (weight; split(stripRight(line), "").map!(to!uint))
{
line_buffer.insertBack(weight);
}
buffer.insertBack(line_buffer);
}
return buffer;
}
void toString(scope void delegate(const(char)[]) sink) const
{
foreach (y; 0 .. positions.length)
{
foreach (x; 0 .. positions[y].length)
sink(to!string(positions[y][x]));
sink("\n");
}
}
}
private alias NodeId = Tuple!(uint, uint);
private struct Node
{
private NodeId id;
private uint weight;
void toString(scope void delegate(const(char)[]) sink) const
{
sink("(");
sink(to!string(this.id[0]));
sink(",");
sink(to!string(this.id[1]));
sink(";");
sink(to!string(this.weight));
sink(")");
}
}
struct Cave
{
private Array!Node[NodeId] nodes;
private NodeId start_node;
private NodeId end_node;
this(CaveMap cave_map)
{
auto node_id = NodeId(0, 0);
start_node = node_id;
uint y = 0;
foreach (row; cave_map.positions)
{
uint x = 0;
foreach (weight; row)
{
node_id = tuple(x, y);
nodes[node_id] = getNeighbors(x, y, cave_map.positions);
x++;
}
y++;
}
end_node = node_id;
}
uint lowestRisk()
{
auto safest_path = dijkstraFaster();
return safest_path[1][end_node];
}
private Tuple!(NodeId[NodeId], uint[NodeId]) dijkstraFaster()
{
uint[NodeId] distance;
NodeId[NodeId] previous;
foreach (node_id; nodes.byKey())
distance[node_id] = uint.max;
distance[start_node] = 0;
auto queue = redBlackTree(tuple(distance[start_node], start_node));
foreach (_, node; queue)
{
if (node == end_node)
break;
foreach (neighbor; nodes[node])
{
auto total_distance = distance[node] + neighbor.weight;
if (total_distance < distance[neighbor.id])
{
queue.removeKey(tuple(distance[neighbor.id], neighbor.id));
distance[neighbor.id] = total_distance;
previous[neighbor.id] = node;
queue.insert(tuple(distance[neighbor.id], neighbor.id));
}
}
}
return tuple(previous, distance);
}
private Tuple!(NodeId[NodeId], uint[NodeId]) dijkstraSlow()
{
bool[NodeId] unvisited;
uint[NodeId] distance;
NodeId[NodeId] previous;
foreach (node_id; nodes.byKey())
{
unvisited[node_id] = true;
distance[node_id] = uint.max;
}
distance[start_node] = 0;
unvisited.rehash;
distance.rehash;
while (unvisited.length != 0)
{
auto cur_node = unvisited.keys.minElement!(x => distance[x]);
if (cur_node == end_node)
{
break;
}
unvisited.remove(cur_node);
foreach (neighbor; nodes[cur_node])
{
if ((neighbor.id in unvisited) is null)
continue;
auto total_distance = distance[cur_node] + neighbor.weight;
if (total_distance < distance[neighbor.id])
{
distance[neighbor.id] = total_distance;
previous[neighbor.id] = cur_node;
}
}
}
return tuple(previous, distance);
}
private immutable int[2][4] DIRECTIONS = [
[-1, 0], [1, 0],
[0, -1], [0, 1],
];
private Array!Node getNeighbors(uint x, uint y, Buffer buf)
{
auto nodes = Array!Node();
foreach (dir; DIRECTIONS)
{
int dx = x + dir[0];
int dy = y + dir[1];
if (dx < 0 || dy < 0)
continue;
if (dx >= buf[0].length() || dy >= buf.length())
continue;
Node node = {id: tuple(dx, dy), buf[dy][dx]};
nodes.insertBack(node);
}
return nodes;
}
void toString(scope void delegate(const(char)[]) sink) const
{
foreach (id; this.nodes.byKey())
{
auto nodes = this.nodes[id];
sink("(");
sink(to!string(id[0]));
sink(",");
sink(to!string(id[1]));
sink(") { ");
foreach (node; nodes)
{
sink(to!string(node));
sink(" ");
}
sink("}\n");
}
}
}