Skip to content

Instantly share code, notes, and snippets.

@drusepth
Created January 24, 2019 08:18
Show Gist options
  • Save drusepth/8534a3f37764786f924a4b3317b6a320 to your computer and use it in GitHub Desktop.
Save drusepth/8534a3f37764786f924a4b3317b6a320 to your computer and use it in GitHub Desktop.
/*
* A* Pathfinding Algorithm
* Modified from my solution to the "After the Dance Battle" challenge
* in the 2011 Facebook Hacker Cup
* by Andrew Brown
* Last modified: 1/14/11
*/
using System;
using System.IO;
using System.Collections.Generic;
namespace Astar
{
class Pathfinding
{
public const int UNKNOWN = 99999999;
// Returns a list of coordinates corresponding to the minimum number of moves
// required to move from start to end in map.
public static List<Coordinate> AstarPath(Map map, Coordinate start, Coordinate end)
{
List<Coordinate> moves = new List<Coordinate>();
List<Node> neighbors;
int moves_needed = AstarPrep(map, start, end);
if (moves_needed == Pathfinding.UNKNOWN)
{
throw new Exception("Seems to be an impossible destination!");
}
// Work back from the end, follow the path of lower numbers
Coordinate curloc = end;
while (curloc != start)
{
moves.Add(curloc);
neighbors = map.GetSurroundingNodes(curloc);
foreach (Node n in neighbors)
{
if (n.distance_from_start < map.GetNode(curloc).distance_from_start)
{
curloc = n;
}
}
}
moves.Reverse();
return moves;
}
// A* meat
// Modifies map; returns the number of moves needed to get from start
// to end in map.
private static int AstarPrep(Map map, Coordinate start, Coordinate end)
{
List<Node> queue = new List<Node>();
List<Node> history = new List<Node>();
int moves_needed = UNKNOWN;
// Start position
map.GetNode(start.X, start.Y).distance_from_start = 0;
queue.Add(map.GetNode(start.X, start.Y));
while (queue.Count > 0)
{
// Pop queue
Node curloc = queue[queue.Count - 1];
queue.RemoveAt(queue.Count - 1);
// Add to history
history.Add(curloc);
// Neighbors onto queue
List<Node> neighbors = map.GetSurroundingNodes(curloc);
foreach (Node n in neighbors)
{
// Neighbor = wall?
if (n.value == Map.S_WALL)
{
history.Add(n);
continue;
}
// Neighbor = end?
if (n.value == Map.S_END)
{
if (curloc.distance_from_start + 1 < moves_needed)
{
moves_needed = curloc.distance_from_start + 1;
n.distance_from_start = curloc.distance_from_start + 1;
}
history.Add(curloc);
continue;
}
// Update neighbors with min(new, old) distance
if (curloc.distance_from_start + 1 < n.distance_from_start)
{
n.distance_from_start = curloc.distance_from_start + 1;
}
// Queue if we haven't been there and we aren't queued already
if (!history.Contains(n) && !queue.Contains(n))
{
queue.Add(n);
}
}
// Next!
}
if (moves_needed == UNKNOWN)
{
throw new Exception("Impossible path");
}
return moves_needed;
}
}
// Map class modeled for Facebook Hacker Cup "After the Dance Battle" puzzle
class Map
{
public const char S_WALL = 'W';
public const char S_START = 'S';
public const char S_END = 'E';
public const char S_EMPTY = '.';
private int height, width;
private char[,] data;
private List<Node> nodes;
private Coordinate start, end;
private List<Coordinate> walls = new List<Coordinate>();
public Map(string raw)
{
string[] split = raw.Split(' ');
try
{
height = Convert.ToInt32(split[0]);
width = Convert.ToInt32(split[1]);
data = new char[height, width];
}
catch
{
throw new Exception("Something is horrendously wrong with the input file");
}
for (int y = 0; y < height; y++)
{
string row = split[y + 2];
for (int x = 0; x < width; x++)
{
data[y, x] = row[x];
switch (row[x])
{
case S_START:
start = new Coordinate(x, y);
break;
case S_END:
end = new Coordinate(x, y);
break;
case S_WALL:
walls.Add(new Coordinate(x, y));
break;
}
}
}
InitNodes();
}
public static List<Map> FromFile(string filename)
{
StreamReader fin = new StreamReader(filename);
List<Map> maps = new List<Map>();
int cases = Convert.ToInt32(fin.ReadLine());
for (int c = 0; c < cases; c++)
{
fin.ReadLine(); // Clear the \n between each map
string[] line = fin.ReadLine().Split(' ');
int height = Convert.ToInt32(line[0]);
int width = Convert.ToInt32(line[1]);
string raw = String.Format("{0} {1}", height, width);
for (int i = 0; i < height; i++)
{
raw = String.Format("{0} {1}", raw, fin.ReadLine());
}
maps.Add(new Map(raw));
}
fin.Close();
return maps;
}
void InitNodes()
{
nodes = new List<Node>();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
nodes.Add(new Node(this, x, y));
}
}
}
public void PrintDebugDistances()
{
for (int i = 0; i < nodes.Count; i++)
{
string symbol = nodes[i].distance_from_start.ToString();
// Empty (0) nodes
if (nodes[i].distance_from_start == Pathfinding.UNKNOWN)
{
symbol = S_EMPTY.ToString();
}
// Special nodes
switch (nodes[i].value)
{
case S_WALL:
case S_END:
symbol = nodes[i].value.ToString();
break;
}
Console.Write("{0,2} {1}",
symbol, ((i + 1) % width == 0 ? "\n" : ""));
}
}
public List<Node> GetSurroundingNodes(Coordinate loc)
{
List<Node> surrounding = new List<Node>();
int base_index = loc.Y * width + loc.X;
// Up
if (loc.Y > 0)
{
surrounding.Add(GetNode(loc.X, loc.Y - 1));
}
// Right
if (loc.X + 1 < width) {
surrounding.Add(GetNode(loc.X + 1, loc.Y));
}
// Left
if (loc.X > 0)
{
surrounding.Add(GetNode(loc.X - 1, loc.Y));
}
// Down
if (loc.Y + 1 < height)
{
surrounding.Add(GetNode(loc.X, loc.Y + 1));
}
return surrounding;
}
public Node GetNode(int x, int y)
{
return nodes[y * width + x];
}
public Node GetNode(Coordinate c)
{
return GetNode(c.X, c.Y);
}
#region Accessors/Mutators
public Coordinate Start
{
get { return start; }
set { start = value; }
}
public Coordinate End
{
get { return end; }
set { end = value; }
}
public List<Coordinate> Walls
{
get { return walls; }
}
public List<Node> Nodes
{
get { return nodes; }
}
public char[,] Data
{
get { return data; }
}
#endregion
}
class Coordinate
{
private int x, y;
public Coordinate(int x, int y)
{
this.x = x;
this.y = y;
}
public int X { get { return x; } set { x = value; } }
public int Y { get { return y; } set { y = value; } }
public override string ToString()
{
return String.Format("({0}, {1})", x, y);
}
public static bool operator ==(Coordinate a, Coordinate b)
{
return (a.X == b.X && a.Y == b.Y);
}
public static bool operator !=(Coordinate a, Coordinate b)
{
return !(a == b);
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
class Node : Coordinate
{
public char value;
public int distance_from_start;
public Node(Map m, int x, int y)
: base(x, y)
{
distance_from_start = Pathfinding.UNKNOWN;
value = m.Data[y, x];
}
public Node(Map m, Coordinate c) : this(m, c.X, c.Y) { }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment