public static void LoadFromDatabase(string databaseFile) { SQLiteConnection conn; SQLiteCommand command; SQLiteDataReader reader; conn = new SQLiteConnection(String.Format("Data Source={0};Version=3;", databaseFile)); conn.Open(); // First add a list for each of the 16 floors for (int k = 0; k <= 15; k++) { nodes.Add(new List<Node>()); } // we keep a temporary <id,Node> map for adding connections Dictionary<int, Node> hnodeids = new Dictionary<int, Node>(); // Read the hierarchical nodes command = new SQLiteCommand("SELECT id,x,y,z,width,height FROM HierarchicalNode", conn); reader = command.ExecuteReader(); while (reader.Read()) { int id = reader.GetInt32(0); int x = reader.GetInt32(1); int y = reader.GetInt32(2); int z = reader.GetInt32(3); int width = reader.GetInt32(4); int height = reader.GetInt32(5); Node n = new Node(x, y, z, width, height); // add the node to the appropriate floor nodes[z].Add(n); // id-map hnodeids.Add(id, n); } // Now read the hierarchical connections from the database command = new SQLiteCommand("SELECT nodeid,nodeid2 FROM HierarchicalConnections", conn); reader = command.ExecuteReader(); while (reader.Read()) { int nodeid = reader.GetInt32(0); int nodeid2 = reader.GetInt32(1); // Add the connection to the nodes, using the id-map to extract the nodes hnodeids[nodeid].neighbors.Add(hnodeids[nodeid2]); hnodeids[nodeid2].neighbors.Add(hnodeids[nodeid]); } }
public static double Distance(Node a, Node b) { // Euclidean distance return Math.Sqrt(Math.Pow(a.rect.X - b.rect.X, 2) + Math.Pow(a.rect.Y - b.rect.Y, 2)); }
public static DijkstraNode FindRoute(Node start, Node goal) { // Dijkstra algorithm // We use Dijkstra instead of A* because there are random teleports in Tibia (e.g. boats) // And it is difficult to create a good A* heuristic that takes random teleports into account // and Dijkstra has more consistently good performance than A* with a bad heuristic List<DijkstraNode> openSet = new List<DijkstraNode> { new DijkstraNode(null, start, 0) }; HashSet<Node> closedSet = new HashSet<Node>(); while (openSet.Count > 0) { // Extract path with current minimal cost DijkstraNode current = GetMinimum(openSet); if (current.node == goal) { return current; } // Add node to closed set openSet.Remove(current); closedSet.Add(current.node); // Iterate over all neighboring nodes foreach (Node neighbor in current.node.neighbors) { if (closedSet.Contains(neighbor)) continue; // If node is not in closed set, create a new path, we use conn.settings.cost for 'special' connections (e.g. teleports, stairs) double newCost = current.cost + Distance(current.node, neighbor); DijkstraNode neighborAStar = openSet.Find(o => o.node == neighbor); if (neighborAStar == null) { // The node is not in the open set; add it openSet.Add(new DijkstraNode(current, neighbor, newCost)); } else if (neighborAStar.cost < newCost) { // If the node is already in the open set and with a lower cost than this path, this path cannot be optimal, so skip it continue; } else { // If the node is already in the open set but with a higher cost, remove it and add this node openSet.Remove(neighborAStar); openSet.Add(new DijkstraNode(current, neighbor, newCost)); } } } return null; }
public DijkstraNode(DijkstraNode previous, Node node, double cost) { this.previous = previous; this.node = node; this.cost = cost; }
public static void LoadFromDatabase(string databaseFile) { SQLiteConnection conn; SQLiteCommand command; SQLiteDataReader reader; conn = new SQLiteConnection(String.Format("Data Source={0};Version=3;", databaseFile)); conn.Open(); // First add a list for each of the 16 floors for (int k = 0; k <= 15; k++) { nodes.Add(new List<Node>()); } // we keep a temporary <id,Node> map for adding connections Dictionary<int, Node> hnodeids = new Dictionary<int, Node>(); // Read the hierarchical nodes command = new SQLiteCommand("SELECT id,x,y,z,width,height FROM HierarchicalNode", conn); reader = command.ExecuteReader(); while (reader.Read()) { int id = reader.GetInt32(0); int x = reader.GetInt32(1); int y = reader.GetInt32(2); int z = reader.GetInt32(3); int width = reader.GetInt32(4); int height = reader.GetInt32(5); Node n = new Node(x, y, z, width, height); // add the node to the appropriate floor nodes[z].Add(n); // id-map hnodeids.Add(id, n); } Dictionary<int, SpecialConnection> connectionMap = new Dictionary<int, SpecialConnection>(); command = new SQLiteCommand("SELECT id, x1,y1,z1,x2,y2,z2,name,cost FROM SpecialConnections", conn); reader = command.ExecuteReader(); while (reader.Read()) { int id = reader.GetInt32(0); Coordinate source = new Coordinate(reader.GetInt32(1), reader.GetInt32(2), reader.GetInt32(3)); Coordinate dest = new Coordinate(reader.GetInt32(4), reader.GetInt32(5), reader.GetInt32(6)); string name = reader[7].ToString(); int cost = reader.GetInt32(8); if (!specialConnection.ContainsKey(source.z)) specialConnection.Add(source.z, new Dictionary<Tuple<int, int>, List<SpecialConnection>>()); Tuple<int, int> tpl = new Tuple<int, int>(source.x, source.y); if (!specialConnection[source.z].ContainsKey(tpl)) specialConnection[source.z].Add(tpl, new List<SpecialConnection>()); SpecialConnection connection = new SpecialConnection { source = source, destination = dest, name = name, cost = cost }; specialConnection[source.z][tpl].Add(connection); connectionMap.Add(id, connection); } // Now read the hierarchical connections from the database command = new SQLiteCommand("SELECT nodeid,nodeid2, specialid FROM HierarchicalConnections", conn); reader = command.ExecuteReader(); while (reader.Read()) { int nodeid = reader.GetInt32(0); int nodeid2 = reader.GetInt32(1); int specialConnection = reader.GetInt32(2); SpecialConnection connection = null; if (specialConnection >= 0) { connection = connectionMap[specialConnection]; } // Add the connection to the nodes, using the id-map to extract the nodes hnodeids[nodeid].neighbors.Add(new Connection(hnodeids[nodeid2], connection)); } for (int i = 0; i <= 15; i++) { doors.Add(new Dictionary<Tuple<int, int>, string>()); } command = new SQLiteCommand("SELECT x,y,z,condition FROM Doors", conn); reader = command.ExecuteReader(); while (reader.Read()) { int x = reader.GetInt32(0); int y = reader.GetInt32(1); int z = reader.GetInt32(2); string condition = reader.IsDBNull(3) ? null : reader[3].ToString(); doors[z].Add(new Tuple<int, int>(x, y), condition); } }
public static double Distance(Node node, Point3D goal) { return Math.Abs(node.z - goal.Z) * 100 + node.rect.Distance(new Point(goal.X, goal.Y)); }
public Connection(Node node, SpecialConnection connection = null) { this.node = node; this.connection = connection; }
public static DijkstraNode FindRoute(Node start, Node goal, Point3D exactGoal, DijkstraNode previousPath = null) { // Dijkstra algorithm // We use Dijkstra instead of A* because there are random teleports in Tibia (e.g. boats) // And it is difficult to create a good A* heuristic that takes random teleports into account // and Dijkstra has more consistently good performance than A* with a bad heuristic List<DijkstraNode> openSet = new List<DijkstraNode> { new DijkstraNode(null, new Connection(start), 0) }; HashSet<Node> closedSet = new HashSet<Node>(); double closestDistance = double.MaxValue; DijkstraNode closestNode = null; Dictionary<Node, DijkstraNode> nodes = new Dictionary<Node, DijkstraNode>(); if (previousPath != null) { DijkstraNode it = previousPath; DijkstraNode prevNode = null; while(it != null) { if (it.node == start) { it.previous = null; it.connection = new Connection(it.node); return previousPath; } if (prevNode != null && !nodes.ContainsKey(prevNode.node)) { nodes.Add(prevNode.node, it); } prevNode = it; it = it.previous; } } while (openSet.Count > 0) { // Extract path with current minimal cost DijkstraNode current = GetMinimum(openSet); if (current.node == goal) { return current; } if (nodes.ContainsKey(current.node)) { DijkstraNode node = nodes[current.node]; node.previous = current; return previousPath; } double distance = Distance(current.node, exactGoal); if (distance < closestDistance) { closestNode = current; closestDistance = distance; } // Add node to closed set openSet.Remove(current); closedSet.Add(current.node); // Iterate over all neighboring nodes foreach (Connection neighbor in current.node.neighbors) { if (closedSet.Contains(neighbor.node)) continue; // If node is not in closed set, create a new path, we use conn.settings.cost for 'special' connections (e.g. teleports, stairs) double newCost = current.cost + (neighbor.connection == null ? Distance(current.node, neighbor.node) : 100); DijkstraNode neighborAStar = openSet.Find(o => o.node == neighbor.node); if (neighborAStar == null) { // The node is not in the open set; add it openSet.Add(new DijkstraNode(current, neighbor, newCost)); } else if (neighborAStar.cost < newCost) { // If the node is already in the open set and with a lower cost than this path, this path cannot be optimal, so skip it continue; } else { // If the node is already in the open set but with a higher cost, remove it and add this node openSet.Remove(neighborAStar); openSet.Add(new DijkstraNode(current, neighbor, newCost)); } } } return closestNode; }
public static double Distance(Node node, Point3D goal) { return Math.Abs(node.z - goal.Z) * 100 + Distance(node.x, node.y, goal.X, goal.Y); }