291 lines
7.0 KiB
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");
|
|
}
|
|
}
|
|
}
|