// Builds a graph from an adjacency matrix. // Only the lower left half is checked. SearchGraph SearchGraphFromData(bool[,] adjacencyMatrix) { int n = adjacencyMatrix.GetUpperBound(0) + 1; // Don't screw this up. Assert.IsTrue(n == adjacencyMatrix.GetUpperBound(1) + 1); List<SkillNode> nodes = new List<SkillNode>(); for (int i = 0; i < n; i++) { SkillNode node = new SkillNode(); node.Id = (ushort)i; nodes.Add(node); } for (int i = 0; i < n; i++) { nodes[i].Neighbor = new List<SkillNode>(); for (int j = 0; j < i; j++) { if (adjacencyMatrix[i, j]) { nodes[i].Neighbor.Add(nodes[j]); // No directed edges atm. nodes[j].Neighbor.Add(nodes[i]); } } } SearchGraph graph = new SearchGraph(); foreach (SkillNode node in nodes) { graph.AddNode(node); } return graph; }
/// <summary> /// Preprocesses the SkillTree graph into a simplified graph that omits /// any isolated node groups (single pass) and contracts all skilled nodes /// into a single node. /// </summary> /// <param name="targets">A set of node IDs representing the target nodes.</param> /// <returns>A SearchGraph representing the simplified SkillTree</returns> private void buildSearchGraph(HashSet<ushort> targets) { searchGraph = new SearchGraph(); // Add the start nodes to the graph. startNodes = searchGraph.SetStartNodes(tree.SkilledNodes); targetNodes = new HashSet<GraphNode>(); // Add the target nodes to the graph. foreach (ushort nodeId in targets) { // Don't add nodes that are already skilled. if (searchGraph.nodeDict.ContainsKey(SkillTree.Skillnodes[nodeId])) continue; // Add target node to the graph. GraphNode node = searchGraph.AddNodeId(nodeId); targetNodes.Add(node); } foreach (SkillNodeGroup ng in SkillTree.NodeGroups) { bool mustInclude = false; SkillNode firstNeighbor = null; // Find out if this node group can be omitted. foreach (SkillNode node in ng.Nodes) { /// If the group contains a skilled node or a target node, /// it can't be omitted. if (searchGraph.nodeDict.ContainsKey(node)) { mustInclude = true; break; } /// If the group is adjacent to more than one node, it must /// also be fully included (since it's not isolated and could /// be part of a path to other nodes). foreach (SkillNode neighbor in node.Neighbor.Where(neighbor => neighbor.SkillNodeGroup != ng)) { if (firstNeighbor == null) firstNeighbor = neighbor; // Does the group have more than one neighbor? if (neighbor != firstNeighbor) { mustInclude = true; break; } } if (mustInclude) break; } if (mustInclude) { // Add the group's nodes individually foreach (SkillNode node in ng.Nodes) { // Can't path through class starts. if (SkillTree.rootNodeList.Contains(node.Id)) continue; /// Don't add nodes that are already in the graph (as /// target or start nodes). if (searchGraph.nodeDict.ContainsKey(node)) continue; searchGraph.AddNode(node); } } } }
Dictionary<int, GraphNode> GetGraphNodesIdIndex(SearchGraph graph) { Dictionary<int, GraphNode> retval = new Dictionary<int, GraphNode>(); foreach (GraphNode node in graph.nodeDict.Values) { retval.Add(node.Id, node); } return retval; }
/// <summary> /// Preprocesses the SkillTree graph into a simplified graph that omits /// any isolated node groups (single pass) and contracts all skilled nodes /// into a single node. /// </summary> /// <param name="targets">A set of node IDs representing the target nodes.</param> /// <returns>A SearchGraph representing the simplified SkillTree</returns> private void buildSearchGraph(HashSet <ushort> targets) { searchGraph = new SearchGraph(); // Add the start nodes to the graph. startNodes = searchGraph.SetStartNodes(tree.SkilledNodes); targetNodes = new HashSet <GraphNode>(); // Add the target nodes to the graph. foreach (ushort nodeId in targets) { // Don't add nodes that are already skilled. if (searchGraph.nodeDict.ContainsKey(SkillTree.Skillnodes[nodeId])) { continue; } // Add target node to the graph. GraphNode node = searchGraph.AddNodeId(nodeId); targetNodes.Add(node); } foreach (SkillNodeGroup ng in SkillTree.NodeGroups) { bool mustInclude = false; SkillNode firstNeighbor = null; // Find out if this node group can be omitted. foreach (SkillNode node in ng.Nodes) { /// If the group contains a skilled node or a target node, /// it can't be omitted. if (searchGraph.nodeDict.ContainsKey(node)) { mustInclude = true; break; } /// If the group is adjacent to more than one node, it must /// also be fully included (since it's not isolated and could /// be part of a path to other nodes). foreach (SkillNode neighbor in node.Neighbor.Where(neighbor => neighbor.SkillNodeGroup != ng)) { if (firstNeighbor == null) { firstNeighbor = neighbor; } // Does the group have more than one neighbor? if (neighbor != firstNeighbor) { mustInclude = true; break; } } if (mustInclude) { break; } } if (mustInclude) { // Add the group's nodes individually foreach (SkillNode node in ng.Nodes) { // Can't path through class starts. if (SkillTree.rootNodeList.Contains(node.Id)) { continue; } /// Don't add nodes that are already in the graph (as /// target or start nodes). if (searchGraph.nodeDict.ContainsKey(node)) { continue; } searchGraph.AddNode(node); } } } }