internal void AddNode(FreeSpaceNode node) { base.AddNode(node); foreach (var nodePos in node.Value.FreeSpaces) { PositionToNode.Add(nodePos, node); } }
internal void AddFreeSpaceNodes(GraphSearchData gsData, Level level) { // //All entities need to be made into walls so the only freepsace is space //that won't block the path or be on top of other entities. // HashSet <Point> agentPositions = new HashSet <Point>(); foreach (var inode in Nodes) { if (inode is BoxConflictNode boxNode) { level.Walls[boxNode.Value.Ent.Pos.X, boxNode.Value.Ent.Pos.Y] = true; if (boxNode.Value.EntType == EntityType.AGENT) { agentPositions.Add(boxNode.Value.Ent.Pos); } } } // //Now go through the map and and find all empty spaces. When an empty space is found, bfs is used to //find all connecting spaces which makes up the free space node. // var foundFreeSpace = new Func <(Point pos, int distance), GraphSearcher.GoalFound <Point> >(x => { return(new GraphSearcher.GoalFound <Point>(x.pos, !level.IsWall(x.pos))); }); HashSet <Point> alreadySeenSpaces = new HashSet <Point>(); List <FreeSpaceNode> freeSpaceNodes = new List <FreeSpaceNode>(); for (int y = 0; y < level.Height; y++) { for (int x = 0; x < level.Width; x++) { if (!level.Walls[x, y] && !alreadySeenSpaces.Contains(new Point(x, y))) { //There is an issue here. //The list has a duplicate in it. //It's currently handled by inserting it //into a hashset. var freeSpacesFound = GraphSearcher.GetReachedGoalsBFS(gsData, level, new Point(x, y), foundFreeSpace); //A single free space can't be part of multiple nodes alreadySeenSpaces.UnionWith(freeSpacesFound); //Create node and add it to the graph. //keep a list of the freespace nodes so edges can be added later var newFreeSpaceNode = new FreeSpaceNode(new FreeSpaceNodeInfo(freeSpacesFound)); AddNode(newFreeSpaceNode); freeSpaceNodes.Add(newFreeSpaceNode); } } } // //Now it's time to add edges between the free spaces nodes and the rest of the graph. //To do that the original map has to be restored as the path may block pathways from //a freespace to any other node on the map. Agents and boxes still have to be walls //as there is no direct path through them. This excludes goals as only goals with //boxes on them should be walls and the boxes on top of them will makes them walls. //Go through all free space nodes and use bfs to check which other nodes it can reach, //then add edges to those nodes. // level.ResetWalls(); foreach (var inode in Nodes) { if (inode is BoxConflictNode boxNode && !boxNode.Value.EntType.IsGoal()) { level.Walls[boxNode.Value.Ent.Pos.X, boxNode.Value.Ent.Pos.Y] = true; } } var foundNode = new Func <(Point pos, int distance), GraphSearcher.GoalFound <INode> >(x => { if (PositionToNode.TryGetValue(x.pos, out INode node)) { return(new GraphSearcher.GoalFound <INode>(node, true)); } return(new GraphSearcher.GoalFound <INode>(null, false)); }); foreach (var freeSpaceNode in freeSpaceNodes) { var nodesFound = GraphSearcher.GetReachedGoalsBFS(gsData, level, freeSpaceNode.Value.FreeSpaces.First(), foundNode); foreach (var neighbour in nodesFound.ToHashSet()) { //The search may find itself and such edges are not necessary if (neighbour != freeSpaceNode) { //Bidirectional edges freeSpaceNode.AddEdge(new Edge <DistanceEdgeInfo>(neighbour, new DistanceEdgeInfo())); if (neighbour is BoxConflictNode boxNode) { boxNode.AddEdge(new Edge <DistanceEdgeInfo>(freeSpaceNode, new DistanceEdgeInfo())); } else if (neighbour is FreeSpaceNode freeNode) { freeNode.AddEdge(new Edge <DistanceEdgeInfo>(freeSpaceNode, new DistanceEdgeInfo())); } } } } level.ResetWalls(); foreach (var agentPos in agentPositions) { foreach (var dirDelta in Direction.NONE.DirectionDeltas()) { Point nextToAgent = agentPos + dirDelta; if (PositionHasNode(nextToAgent)) { INode node = GetNodeFromPosition(nextToAgent); if (node is FreeSpaceNode freeSpaceNode) { freeSpaceNode.Value.FreeSpaces.Add(agentPos); continue; } } } } }