private IDictionary <string, GameObject[]> BreadthFirstSpaceGeneration(GraphType graph, NodeType startNode) { IDictionary <string, GameObject[]> generatedSpace = new Dictionary <string, GameObject[]>(graph.Nodes.Node.Length); Queue <string> nodeIdQueue = new Queue <string>(); GameObject[] startNodeSpaceObjects = BuildSpaceForMissionSymbol(startNode.symbol); generatedSpace[startNode.id] = startNodeSpaceObjects; nodeIdQueue.Enqueue(startNode.id); while (nodeIdQueue.Count != 0) { string currentNodeId = nodeIdQueue.Dequeue(); IList <NodeType> adjacentNodes = graph.AdjacencyList[currentNodeId]; foreach (NodeType adjacentNode in adjacentNodes) { if (generatedSpace.ContainsKey(adjacentNode.id)) { continue; } GameObject[] currentNodeSpaceObjects = generatedSpace[currentNodeId]; GameObject[] adjacentNodeSpaceObjects = BuildSpaceForMissionSymbol( adjacentNode.symbol, currentNodeSpaceObjects); generatedSpace[adjacentNode.id] = adjacentNodeSpaceObjects; nodeIdQueue.Enqueue(adjacentNode.id); } } return(generatedSpace); }
private void ConnectFinalKeyToFinalLock(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { IList <NodeType> lockFinalNodes = missionGraph.NodeSymbolMap[lockFinalSymbol]; if (lockFinalNodes.Count != 1) { throw new InvalidOperationException("Cannot connect final lock to final key due to the wrong " + "number of lock nodes in mission graph corresponding to the lock " + $"final symbol '{lockFinalSymbol}'. Expected 1 but found " + $"{lockFinalNodes.Count}."); } IList <NodeType> keyFinalNodes = missionGraph.NodeSymbolMap[keyFinalSymbol]; if (keyFinalNodes.Count != 1) { throw new InvalidOperationException("Cannot connect final lock to final key due to the wrong " + "number of key nodes in mission graph corresponding to the key " + $"final symbol '{keyFinalSymbol}'. Expected 1 but found " + $"{keyFinalNodes.Count}."); } ConnectLockAndKey(generatedSpace, lockFinalNodes[0].id, keyFinalNodes[0].id); }
public GraphType GenerateGraph() { IDictionary <string, GraphType> graphs = new Dictionary <string, GraphType>(Graphs.Graph.Length); foreach (GraphType graph in Graphs.Graph) { graphs[graph.id] = graph; } string startGraphRef = Grammar.StartGraph.@ref; GraphType startGraph = graphs[startGraphRef]; int ruleNumber = 0; while (true) { RuleType[] applicableRules = GetApplicableRules(graphs, startGraph); if (applicableRules.Length == 0) { return(startGraph); } RuleType ruleToApply = applicableRules[ applicableRules.Length == 1 ? 0 : Random.Range(0, applicableRules.Length)]; Debug.Log($"[Applying Rule {++ruleNumber}] source: {ruleToApply.source} | target: {ruleToApply.target}"); GraphType ruleSource = graphs[ruleToApply.source]; GraphType ruleTarget = graphs[ruleToApply.target]; startGraph.FindAndReplace(ruleSource, ruleTarget); } }
private void PostProcess(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { foreach (PostProcessor postProcessor in GetComponents <PostProcessor>()) { postProcessor.Process(missionGraph, generatedSpace); } }
/** * Copy the edges from targetGraph into this graph, determining the source and target nodes from markedNodes */ private void InsertEdgesBetweenMarkedNodes(GraphType otherGraph, IDictionary <string, NodeType> markedNodes) { if (otherGraph.Edges?.Edge == null || otherGraph.Edges?.Edge?.Length == 0) { return; } IList <EdgeType> edges = Edges?.Edge != null ? new List <EdgeType>(Edges.Edge) : new List <EdgeType>(otherGraph.Edges.Edge.Length); foreach (EdgeType targetGraphEdge in otherGraph.Edges.Edge) { string source = targetGraphEdge.source; string target = targetGraphEdge.target; edges.Add(new EdgeType { source = markedNodes[source].id, target = markedNodes[target].id }); } if (Edges == null) { Edges = new EdgesType(); } Edges.Edge = edges.ToArray(); }
private void ConnectMultiKeysToMultiLock(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { IList <NodeType> lockMultiNodes = missionGraph.NodeSymbolMap[lockMultiSymbol]; if (lockMultiNodes.Count != 1) { throw new InvalidOperationException("Cannot connect multi keys to multi lock due to the wrong " + "number of lock nodes in mission graph corresponding to the lock " + $"final symbol '{lockMultiSymbol}'. Expected 1 but found " + $"{lockMultiNodes.Count}."); } string lockId = lockMultiNodes[0].id; GameObject[] locks = GetLocks(generatedSpace, lockId); if (locks.Length != 1) { throw new InvalidOperationException($"Cannot connect multi keys to multi lock (ID: {lockId}) " + "due to the wrong number of lock GameObjects corresponding to " + $"the lock's ID. Expected 1 but found {locks.Length}."); } Item[] keys = missionGraph.NodeSymbolMap[keyMultiSymbol] .SelectMany(node => generatedSpace[node.id]) .SelectMany(spaceObject => spaceObject.transform.Cast <Transform>()) .Where(child => child.CompareTag("Key")) .Select(keyTransform => keyTransform.GetComponent <Item>()) .ToArray(); locks[0].GetComponent <UnlockDoorAction>().AddRequiredKeys(keys); }
private bool SubgraphSearch(GraphType otherGraph, IDictionary <string, IList <NodeType> > markedNodes = null) { foreach (NodeType startNode in otherGraph.StartNodes) { IList <NodeType> sourceNodes = new List <NodeType> { startNode }; IList <NodeType> nodeCandidates = NodeSymbolMap[startNode.symbol]; bool markNodesForAllNodeCandidates = markedNodes != null && nodeCandidates.Count > 1; IList <IDictionary <string, IList <NodeType> > > markedNodesList = markNodesForAllNodeCandidates ? new List <IDictionary <string, IList <NodeType> > >() : null; bool successfulCandidateFound = false; foreach (NodeType nodeCandidate in nodeCandidates) { IList <NodeType> thisNodes = new List <NodeType> { nodeCandidate }; IDictionary <string, IList <NodeType> > candidateMarkedNodes = markNodesForAllNodeCandidates ? new Dictionary <string, IList <NodeType> >() : markedNodes; bool isSuccessfulCandidate = SubgraphSearch(otherGraph, thisNodes, sourceNodes, candidateMarkedNodes); if (isSuccessfulCandidate) { successfulCandidateFound = true; if (!markNodesForAllNodeCandidates) { break; // not marking nodes for all candidates means we don't need to search every node, so break } markedNodesList.Add(candidateMarkedNodes); } } if (!successfulCandidateFound) { return(false); } if (markNodesForAllNodeCandidates) { // pick a random collection of marked nodes to use and add contents to original markedNodes Dictionary IDictionary <string, IList <NodeType> > markedNodesToUse = markedNodesList[Random.Range(0, markedNodesList.Count)]; foreach (KeyValuePair <string, IList <NodeType> > keyValuePair in markedNodesToUse) { markedNodes[keyValuePair.Key] = keyValuePair.Value; } } } return(true); }
public void FindAndReplace(GraphType sourceGraph, GraphType targetGraph) { IDictionary <string, NodeType> markedNodes = FindSubgraphAndMarkNodes(sourceGraph); RemoveEdgesBetweenMarkedNodes(sourceGraph, markedNodes); FindAndReplaceNodes(sourceGraph, targetGraph, markedNodes); InsertEdgesBetweenMarkedNodes(targetGraph, markedNodes); RecalculateFields(); }
private RuleType[] GetApplicableRules(IDictionary <string, GraphType> graphs, GraphType startGraph) { return(Grammar.Rules.Rule .Where(rule => { GraphType ruleSourceGraph = graphs[rule.source]; return startGraph.IsSupergraphOf(ruleSourceGraph); }) .ToArray()); }
private bool HasAllSymbolsIn(GraphType otherGraph) { return(otherGraph.NodeSymbolMap.All(pair => { string symbol = pair.Key; IList <NodeType> otherGraphNodes = pair.Value; return NodeSymbolMap.ContainsKey(symbol) && NodeSymbolMap[symbol].Count >= otherGraphNodes.Count; })); }
/** * Carry out a BFS through missionGraph. For each node, retrieve the correct SpaceObjectByMissionSymbol based * on the node's symbol and place the SpaceObject on the plane of the scene, connecting it to an existing * SpaceObject where possible (e.g. not the first SpaceObject placed, and only connected where specified by the * SpaceObject - such as a doorway). */ private IDictionary <string, GameObject[]> GenerateSpace(GraphType missionGraph) { int numStartNodes = missionGraph.StartNodes.Length; if (numStartNodes > 1) { throw new InvalidOperationException("Mission graph cannot have more than 1 start node. " + $"It currently has {numStartNodes}."); } return(BreadthFirstSpaceGeneration(missionGraph, missionGraph.StartNodes[0])); }
public override void Process(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { playerController = GameObject.FindWithTag("Player").GetComponent <PlayerController>(); if (missionGraph.NodeSymbolMap.ContainsKey(questItemSymbol)) { CreateQuestsForQuestItems(missionGraph, generatedSpace); } if (missionGraph.NodeSymbolMap.ContainsKey(levelBossSymbol)) { CreateQuestsForLevelBoss(missionGraph, generatedSpace); } }
/** * Transform this graph by transforming marked nodes into their corresponding nodes * in targetGraph, adding a node for each node in targetGraph that has no match in * this graph, and removing any nodes that have no corresponding node in targetGraph. */ private void FindAndReplaceNodes(GraphType sourceGraph, GraphType targetGraph, IDictionary <string, NodeType> markedNodes) { List <NodeType> thisGraphNodes = new List <NodeType>(Nodes.Node); foreach (NodeType targetGraphNode in targetGraph.Nodes.Node) { string nodeId = targetGraphNode.id; string newSymbol = targetGraphNode.symbol; if (markedNodes.ContainsKey(nodeId)) { markedNodes[nodeId].symbol = newSymbol; } else { List <int> numericIds = new List <int>(thisGraphNodes.Count); foreach (NodeType node in thisGraphNodes) { if (int.TryParse(node.id, out int idAsInt)) { numericIds.Add(idAsInt); } } int newNodeId = 0; if (numericIds.Count > 0) { numericIds.Sort(); newNodeId = numericIds.Last() + 1; } NodeType newNode = new NodeType { id = newNodeId.ToString(), symbol = newSymbol }; thisGraphNodes.Add(newNode); markedNodes[nodeId] = newNode; } } IEnumerable <string> nodesToRemove = sourceGraph.Nodes.Node .Where(sourceNode => targetGraph.Nodes.Node.All(targetNode => targetNode.id != sourceNode.id)) .Select(sourceNode => markedNodes[sourceNode.id].id); thisGraphNodes.RemoveAll(node => nodesToRemove.Contains(node.id)); Nodes.Node = thisGraphNodes.ToArray(); }
private bool SubgraphSearch(GraphType otherGraph, IList <NodeType> thisNodes, IList <NodeType> otherNodes, IDictionary <string, IList <NodeType> > markedNodes = null, IList <string> visitedOtherNodes = null) { visitedOtherNodes = visitedOtherNodes ?? new List <string>(otherGraph.Nodes.Node.Length); foreach (NodeType otherNode in otherNodes) { if (visitedOtherNodes.Contains(otherNode.id)) { continue; } visitedOtherNodes.Add(otherNode.id); IList <string> visitedOtherNodesThusFar = new List <string>(visitedOtherNodes); bool matchingNodeFound = false; foreach (NodeType thisNode in thisNodes) { if (thisNode.symbol != otherNode.symbol) { continue; } IList <NodeType> thisAdjacentNodes = AdjacencyList[thisNode.id]; IList <NodeType> otherAdjacentNodes = otherGraph.AdjacencyList[otherNode.id]; matchingNodeFound = SubgraphSearch(otherGraph, thisAdjacentNodes, otherAdjacentNodes, markedNodes, visitedOtherNodes); if (!matchingNodeFound) { visitedOtherNodes = new List <string>(visitedOtherNodesThusFar); } else if (markedNodes != null) { if (!markedNodes.ContainsKey(otherNode.id)) { markedNodes[otherNode.id] = new List <NodeType>(); } markedNodes[otherNode.id].Add(thisNode); } } if (!matchingNodeFound) { return(false); } } return(true); }
private void RemoveEdgesBetweenMarkedNodes(GraphType otherGraph, IDictionary <string, NodeType> markedNodes) { if (Edges?.Edge == null || otherGraph.Edges?.Edge == null) { return; } List <EdgeType> edges = new List <EdgeType>(Edges.Edge); foreach (EdgeType sourceGraphEdge in otherGraph.Edges.Edge) { string sourceNodeId = markedNodes[sourceGraphEdge.source].id; string targetNodeId = markedNodes[sourceGraphEdge.target].id; edges.RemoveAll(edge => edge.source == sourceNodeId && edge.target == targetNodeId); } Edges.Edge = edges.ToArray(); }
public override void Process(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { if (missionGraph.HasNodesForSymbols(lockSymbol, keySymbol)) { ConnectKeysToLocks(missionGraph, generatedSpace); } if (missionGraph.HasNodesForSymbols(lockMultiSymbol, keyMultiSymbol)) { ConnectMultiKeysToMultiLock(missionGraph, generatedSpace); } if (missionGraph.HasNodesForSymbols(lockFinalSymbol, keyFinalSymbol)) { ConnectFinalKeyToFinalLock(missionGraph, generatedSpace); } }
// TODO: remove this method when done prototyping private void DebugLogGraph(GraphType graph) { foreach (NodeType node in graph.Nodes.Node) { Debug.Log($"[Graph {graph.id} | Node: {node.id}] symbol: {node.symbol}"); } if (graph.Edges?.Edge == null) { return; } for (int j = 0; j < graph.Edges.Edge.Length; j++) { EdgeType edge = graph.Edges.Edge[j]; Debug.Log($"[Graph {graph.id} | Edge: {j + 1}] source: {edge.source} | target: {edge.target}"); } }
private void Start() { System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); long timeBeforeXmlDeserialization = stopwatch.ElapsedMilliseconds; GenGraType genGra = DeserializeGenGraXML(); long timeAfterXmlDeserialization = stopwatch.ElapsedMilliseconds; long xmlDeserializationTime = timeAfterXmlDeserialization - timeBeforeXmlDeserialization; Debug.Log($"XML deserialization completed in: {xmlDeserializationTime}ms"); long timeBeforeGraphTransformation = stopwatch.ElapsedMilliseconds; GraphType missionGraph = genGra.GenerateGraph(); long timeAfterGraphTransformation = stopwatch.ElapsedMilliseconds; long graphTransformationTime = timeAfterGraphTransformation - timeBeforeGraphTransformation; DebugLogGraph(missionGraph); Debug.Log($"Mission graph generation completed in: {graphTransformationTime}ms"); long timeBeforeSpaceGeneration = stopwatch.ElapsedMilliseconds; IDictionary <string, GameObject[]> generatedSpace = GenerateSpace(missionGraph); long timeAfterSpaceGeneration = stopwatch.ElapsedMilliseconds; long spaceGenerationTime = timeAfterSpaceGeneration - timeBeforeSpaceGeneration; Debug.Log($"Space generation completed in: {spaceGenerationTime}ms"); long timeBeforePostProcessing = stopwatch.ElapsedMilliseconds; PostProcess(missionGraph, generatedSpace); long timeAfterPostProcessing = stopwatch.ElapsedMilliseconds; long postProcessingTime = timeAfterPostProcessing - timeBeforePostProcessing; Debug.Log($"Post processing completed in: {postProcessingTime}ms"); stopwatch.Stop(); Debug.Log($"Total execution completed in: {stopwatch.ElapsedMilliseconds}ms"); }
private void ConnectKeysToLocks(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { IList <NodeType> usedLockNodes = new List <NodeType>(); IList <NodeType> suspendedKeyNodes = new List <NodeType>(); IList <NodeType> keyNodes = missionGraph.NodeSymbolMap[keySymbol]; foreach (NodeType keyNode in keyNodes) { bool isLockNodeAdjacent = false; IList <NodeType> adjacentNodes = missionGraph.AdjacencyList[keyNode.id]; foreach (NodeType adjacentNode in adjacentNodes) { if (adjacentNode.symbol == lockSymbol) { ConnectLockAndKey(generatedSpace, adjacentNode.id, keyNode.id); usedLockNodes.Add(adjacentNode); isLockNodeAdjacent = true; break; } } if (!isLockNodeAdjacent) { suspendedKeyNodes.Add(keyNode); } } if (suspendedKeyNodes.Count == 0) { return; } List <NodeType> lockNodes = missionGraph.NodeSymbolMap[lockSymbol].ToList(); lockNodes.RemoveAll(usedLockNodes.Contains); foreach (NodeType keyNode in suspendedKeyNodes) { NodeType randomLockNode = lockNodes[lockNodes.Count == 1 ? 0 : Random.Range(0, lockNodes.Count)]; ConnectLockAndKey(generatedSpace, randomLockNode.id, keyNode.id); lockNodes.Remove(randomLockNode); } }
public override void Process(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { foreach (GameObject[] spaceObjectList in generatedSpace.Values) { foreach (GameObject spaceObject in spaceObjectList) { foreach (Transform child in spaceObject.transform) { if (child.CompareTag("Navigatable")) { NavMeshSurface navMeshSurface = child.GetComponent <NavMeshSurface>(); if (navMeshSurface != null) { navMeshSurface.BuildNavMesh(); } } } } } }
private void CreateQuestsForQuestItems(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { IList <NodeType> questItemNodes = missionGraph.NodeSymbolMap[questItemSymbol]; foreach (NodeType questItemNode in questItemNodes) { GameObject[] spaceObjects = generatedSpace[questItemNode.id]; foreach (GameObject spaceObject in spaceObjects) { foreach (Transform child in spaceObject.transform) { if (child.CompareTag("Item")) { Item item = child.GetComponent <Item>(); Quest quest = new Quest($"Find {item.ItemName}"); item.Quest = quest; playerController.AddQuest(quest); } } } } }
private void CreateQuestsForLevelBoss(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { IList <NodeType> levelBossNodes = missionGraph.NodeSymbolMap[levelBossSymbol]; foreach (NodeType levelBossNode in levelBossNodes) { GameObject[] spaceObjects = generatedSpace[levelBossNode.id]; foreach (GameObject spaceObject in spaceObjects) { foreach (Transform child in spaceObject.transform) { if (child.CompareTag("Enemy")) { Enemy enemy = child.GetComponent <Enemy>(); Quest quest = new Quest($"Defeat {enemy.EnemyName}"); enemy.Quest = quest; playerController.AddQuest(quest); } } } } }
private IDictionary <string, NodeType> FindSubgraphAndMarkNodes(GraphType otherGraph) { IDictionary <string, IList <NodeType> > candidateMarkedNodes = new Dictionary <string, IList <NodeType> >(); bool isSupergraphOfSourceGraph = SubgraphSearch(otherGraph, candidateMarkedNodes); if (!isSupergraphOfSourceGraph) { throw new InvalidOperationException($"No subgraph found in graph {id} matching source graph" + $" {otherGraph.id}, so cannot carry out find and replace operation"); } IDictionary <string, NodeType> markedNodes = new Dictionary <string, NodeType>(candidateMarkedNodes.Count); foreach (KeyValuePair <string, IList <NodeType> > keyValuePair in candidateMarkedNodes) { IList <NodeType> nodes = keyValuePair.Value; NodeType nodeToUse = nodes[nodes.Count == 1 ? 0 : Random.Range(0, nodes.Count)]; markedNodes[keyValuePair.Key] = nodeToUse; } return(markedNodes); }
public abstract void Process(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace);
public override void Process(GraphType missionGraph, IDictionary <string, GameObject[]> generatedSpace) { DestroyAttachedAttachmentPoints(); AttachWallsToUnattachedAttachmentPoints(generatedSpace); }
public bool IsSupergraphOf(GraphType otherGraph) { return(HasAllSymbolsIn(otherGraph) && SubgraphSearch(otherGraph)); }