private static void BuildGraph(List <string> maze, Dictionary <char, int> pos, Dictionary <char, Dictionary <char, StepsToPoint> > graph) { foreach (char source in pos.Keys) { if (source >= 'a' || source == '@') { foreach (char destination in pos.Keys) { if (destination != source && (destination >= 'a' || destination == '@') && (!graph.ContainsKey(source) || !graph[source].ContainsKey(destination))) { StepsToPoint best = ShortestPath(pos[source], pos[destination], maze); if (best.steps != 0) { if (!graph.ContainsKey(source)) { graph[source] = new Dictionary <char, StepsToPoint>(); } if (!graph.ContainsKey(destination)) { graph[destination] = new Dictionary <char, StepsToPoint>(); } graph[source][destination] = best; graph[destination][source] = best; } } } } } //build graph }
static int ShortestPath(int start, int destination, List <string> maze, Dictionary <int, int> warps, bool ignoreZ = true) { //search maze for best path Queue <StepsToPoint> paths = new Queue <StepsToPoint>(); StepsToPoint point = new StepsToPoint { x = start / 1000, y = start % 1000 }; paths.Enqueue(point); HashSet <int> found = new HashSet <int> { start }; //z*1000000 + x*1000 + y while (paths.Count > 0) { var path = paths.Dequeue(); for (int i = 0; i < 4; i++) { int nextX = path.x; int nextY = path.y; int nextZ = path.z; switch (i) { case 0: //up nextY--; break; case 1: //down nextY++; break; case 2: //left nextX--; break; case 3: //right nextX++; break; } //if next is a warp, replace it with its warp endpoint int nextIntXY = nextX * 1000 + nextY; if (warps.ContainsKey(nextIntXY) && (ignoreZ || nextZ != 0 || IsInnerWarp(nextX, nextY, maze))) //for part 2 (ignoreZ==false), only use warp if it is an inner warp point or z != 0 { if (!ignoreZ) { nextZ += IsInnerWarp(nextX, nextY, maze) ? 1 : -1; } nextIntXY = warps[nextIntXY]; nextX = nextIntXY / 1000; nextY = nextIntXY % 1000; } bool canMove = (nextY >= 0 && nextY < maze.Count && nextX >= 0 && nextX < maze[nextY].Length); if (canMove && maze[nextY][nextX] == '.' && !found.Contains(nextZ * 1000000 + nextX * 1000 + nextY)) { StepsToPoint next = new StepsToPoint { steps = path.steps + 1, x = nextX, y = nextY, z = nextZ }; if ((next.x * 1000 + next.y) == destination && next.z == 0) { return(next.steps); } found.Add(nextZ * 1000000 + nextX * 1000 + nextY); paths.Enqueue(next); } } } //while return(0); }
static StepsToPoint ShortestPath(int start, int destination, List <string> maze) { //search maze for best path Queue <StepsToPoint> paths = new Queue <StepsToPoint>(); StepsToPoint point = new StepsToPoint { x = start / 100, y = start % 100 }; if (maze[point.y][point.x] != '#') { paths.Enqueue(point); } else { //if the starting point is a wall, enqueue the the 4 '@' in part 2: paths.Enqueue(new StepsToPoint() { x = point.x - 1, y = point.y - 1 }); paths.Enqueue(new StepsToPoint() { x = point.x + 1, y = point.y - 1 }); paths.Enqueue(new StepsToPoint() { x = point.x - 1, y = point.y + 1 }); paths.Enqueue(new StepsToPoint() { x = point.x + 1, y = point.y + 1 }); } HashSet <int> found = new HashSet <int> { start }; while (paths.Count > 0) { var path = paths.Dequeue(); for (int i = 0; i < 4; i++) { int nextX = path.x; int nextY = path.y; switch (i) { case 0: //up nextY--; break; case 1: //down nextY++; break; case 2: //left nextX--; break; case 3: //right nextX++; break; } bool canMove = (nextY >= 0 && nextY < maze.Count && nextX >= 0 && nextX < maze[nextY].Length); if (canMove && maze[nextY][nextX] != '#' && (!found.Contains(nextX * 100 + nextY) || (maze[path.y][path.x] - 'a' >= 0))) { StepsToPoint next = new StepsToPoint(); next.steps = path.steps + 1; next.x = nextX; next.y = nextY; next.doors = path.doors; if ((next.x * 100 + next.y) == destination) { return(next); } found.Add(nextX * 100 + nextY); if (maze[nextY][nextX] >= 'A' && maze[nextY][nextX] <= 'Z') { next.doors += 1 << (maze[nextY][nextX] - 'A'); } paths.Enqueue(next); } } } //while return(new StepsToPoint()); }