// Realtime level generation I public static CreVoxNode GenerateMissionGraph(string xmlPath, int Seed) { DungeonLevel.OperateXML.Unserialize.UnserializeFromXml(xmlPath); GraphGrammar graph = new GraphGrammar(); // Rewrite system initialization. RewriteSystem.Initial(Seed); graph = RewriteSystem.TransformFromGraph(); var stopWatch = System.Diagnostics.Stopwatch.StartNew(); // Iterate until finish. while ( ( // Still exist non-terminal nodes. graph.Nodes.Exists(n => n.Terminal == NodeTerminalType.NonTerminal) // Have to exhauste all rules that set minimum. || RewriteSystem.Rules.Sum(r => r.QuantityLimitMin) > 0 ) // Time limit is 3,000 ms. && stopWatch.ElapsedMilliseconds <= 3000 ) { // Rewrite system iteration. RewriteSystem.Iterate(); // Update the current graph. graph = RewriteSystem.TransformFromGraph(); } stopWatch.Stop(); // Setting root node for CreVoxAttach. SetCreVoxNodeRoot(graph.Nodes[0]); Debug.Log(_rootNode); return(_rootNode); }
// Unserialize MissionGraph private static Mission.GraphGrammar UnserializeMissionGraph(XElement element) { Mission.GraphGrammar graph = new Mission.GraphGrammar(); XElement elementMissionGraph = element.Element("MissionGraph"); graph.Nodes = UnserializeNodes(elementMissionGraph); graph.Connections = UnserializeConnections(elementMissionGraph); // Unserialize sticky int connectionIndex = 0; foreach (var elementConnection in elementMissionGraph.Element("Connections").Elements("Connection")) { if (!elementConnection.Element("StartpointStickyOn").IsEmpty) { Guid guid = new Guid(elementConnection.Element("StartpointStickyOn").Value); int nodeIndex = graph.Nodes.FindIndex(x => x.ID == guid); graph.Connections[connectionIndex].StartpointStickyOn = graph.Nodes[nodeIndex]; graph.Nodes[nodeIndex].AddStickiedConnection(graph.Connections[connectionIndex], "start"); } if (!elementConnection.Element("EndpointStickyOn").IsEmpty) { Guid guid = new Guid(elementConnection.Element("EndpointStickyOn").Value); int nodeIndex = graph.Nodes.FindIndex(x => x.ID == guid); graph.Connections[connectionIndex].EndpointStickyOn = graph.Nodes[nodeIndex]; graph.Nodes[nodeIndex].AddStickiedConnection(graph.Connections[connectionIndex], "end"); } connectionIndex++; } return(graph); }
// Transform a graph into tree struct. Then return the table. private static List <Node> TransformGraph(GraphGrammar graph, out int nodeCount) { int edgeIndex = graph.Nodes.Count + 1; // Initialize nodes var nodes = new List <Node>(); for (int i = 0; i < graph.Nodes.Count; i++) { nodes.Add(new Node(graph.Nodes[i], graph.Nodes[i].Ordering)); } // Set parents and children for (int i = 0; i < graph.Nodes.Count; i++) { foreach (var childNode in graph.Nodes[i].Children) { Node edgeNode = new Node(graph.GetConnectionByNode(graph.Nodes[i], childNode), edgeIndex++); nodes.Add(edgeNode); nodes[i].Children.Add(edgeNode); edgeNode.Parents.Add(nodes[i]); int index = graph.Nodes.FindIndex(n => n.ID == childNode.ID); edgeNode.Children.Add(nodes[index]); nodes[index].Parents.Add(edgeNode); } } // Update the node count. nodeCount = graph.Nodes.Count + graph.Connections.Count; // Return the table. return(nodes.OrderBy(n => n.Index).ToList()); }
// No 9. OverflowedAnyNode. private static bool ValidateOverflowedAnyNode(MissionRule rule, GraphGrammar graphGrammar) { // Sort nodes in ordering. GraphGrammarNode[] sourceNodes = rule.SourceRule.Nodes.OrderBy(n => n.Ordering).ToArray(); // if replaceRule have any node that dont match the source ordering. return(!rule.ReplacementRule.Nodes.Exists(n => (Alphabet.IsAnyNode(n.AlphabetID) && (n.Ordering > sourceNodes.Length ? true : !Alphabet.IsAnyNode(sourceNodes[n.Ordering - 1].AlphabetID))))); }
// Serialize MissionGraph private static XElement SerializeMissionGraph(Mission.GraphGrammar graph) { XElement elementMissionGraph = new XElement("MissionGraph"); elementMissionGraph.Add(SerializeNodes(graph.Nodes), SerializeConnections(graph.Connections)); return(elementMissionGraph); }
public static void Initialize() { _scrollView = new Vector2(0, 60); _errorType = ErrorType.None; _graphState = GraphState.Mission; _currentGraph = new Mission.GraphGrammar(); _isInitTabButton = true; _isRuleChanged = true; Seed = 0; }
// No 6. MultipleRelations. private static bool ValidateMultipleRelations(MissionRule rule, GraphGrammar graphGrammar) { if (graphGrammar.Connections.Count > 1) { foreach (GraphGrammarConnection connection in graphGrammar.Connections) { if ((graphGrammar.Connections.Where(e => (e != connection && (e.StartpointStickyOn == connection.StartpointStickyOn && e.EndpointStickyOn == connection.EndpointStickyOn) || (e.StartpointStickyOn == connection.EndpointStickyOn && e.EndpointStickyOn == connection.StartpointStickyOn)))).Any()) { return(false); } } } return(true); }
// Add node and connection to graph grammar by dfs. // "layer" is used to calculate x position private static void RecursionGraphGrammar(Node node, ref GraphGrammar graphGrammar, int layer) { if (CountInLayer.Count <= layer) { CountInLayer.Add(0); } // Mark this node. node.Explored = true; // "index" is used to calculate y position. int index = 0; foreach (Node edgeNode in node.Children) { // Add connection (Now only use Connections[0], will modify). Node childNode = edgeNode.Children[0]; var connection = new GraphGrammarConnection(Alphabet.Connections.Find(c => c.AlphabetID == edgeNode.AlphabetID)); graphGrammar.Connections.Add(connection); // Set starting sticked attribute. _nodeMappingTable[node].AddStickiedConnection(connection, "start"); connection.StartpointStickyOn = _nodeMappingTable[node]; connection.StartPosition = _nodeMappingTable[node].Position; // If mapping table have not contained this Node then add it. if (!_nodeMappingTable.ContainsKey(childNode)) { // Set position. _nodeMappingTable[childNode] = new GraphGrammarNode(_referenceNodeTable[childNode.AlphabetID]) { Position = new Vector2(LEFT_TOP_POSITION.x + layer * PADDING, LEFT_TOP_POSITION.y + (CountInLayer[layer] + index) * PADDING) }; graphGrammar.Nodes.Add(_nodeMappingTable[childNode]); } // Set ending sticked attribute. _nodeMappingTable[childNode].AddStickiedConnection(connection, "end"); connection.EndpointStickyOn = _nodeMappingTable[childNode]; connection.EndPosition = _nodeMappingTable[childNode].Position; // Check the mark exist. if (!childNode.Explored) { // Search deeper, so "layer" must increase. RecursionGraphGrammar(childNode, ref graphGrammar, layer + 1); } index++; } CountInLayer[layer] += index; }
// No 3. IsolatedNode. private static bool ValidateIsolatedNode(MissionRule rule, GraphGrammar graphGrammar) { if (graphGrammar.Nodes.Count > 1) { if (graphGrammar.Connections.Count < graphGrammar.Nodes.Count - 1) { return(false); } foreach (GraphGrammarNode node in graphGrammar.Nodes) { // Connection.StickOn will remain the last node it sticked on, so use position to inforce validation. if (!graphGrammar.Connections.Where(c => (c.StartpointStickyOn == node || c.EndpointStickyOn == node)).Any()) { return(false); } } } return(true); }
// Export the original structure from tree structure to canvas. public static GraphGrammar TransformFromGraph() { var graphGrammar = new GraphGrammar(); // Get reference table (For getting the symbol of Alphabet.) _referenceNodeTable = Alphabet.ReferenceNodeTable; // _referenceConnectionTable = Alphabet.ReferenceConnectionTable; // Initialize mapping table. _nodeMappingTable = new Dictionary <Node, GraphGrammarNode>(); // Add root node. _nodeMappingTable[_root] = new GraphGrammarNode(_referenceNodeTable[_root.AlphabetID]) { Position = LEFT_TOP_POSITION }; graphGrammar.Nodes.Add(_nodeMappingTable[_root]); CountInLayer.Clear(); CountInLayer.Add(0); RecursionGraphGrammar(_root, ref graphGrammar, 1); ClearExplored(_root); ResultGraph = graphGrammar; return(graphGrammar); }
// No 5. ExactlyDuplicated. private static bool ValidateExactlyDuplicated(MissionRule rule, GraphGrammar graphGrammar) { // Check the number of connections & nodes first. if (rule.SourceRule.Nodes.Count != rule.ReplacementRule.Nodes.Count || rule.SourceRule.Connections.Count != rule.ReplacementRule.Connections.Count) { return(true); } // If find any difference in connections, then defined they are not isomorphic. foreach (var connectionA in rule.SourceRule.Connections) { if (!rule.ReplacementRule.Connections.Exists(connectionB => connectionB.AlphabetID == connectionA.AlphabetID && // Check the ordering they sticky on. If null, expresses zero. (connectionB.StartpointStickyOn == null ? 0 : connectionB.StartpointStickyOn.Ordering) == (connectionA.StartpointStickyOn == null ? 0 : connectionA.StartpointStickyOn.Ordering) && (connectionB.EndpointStickyOn == null ? 0 : connectionB.EndpointStickyOn.Ordering) == (connectionA.EndpointStickyOn == null ? 0 : connectionA.EndpointStickyOn.Ordering) )) { return(true); } } // If find any difference in nodes, then defined they are not isomorphic. foreach (var nodeA in rule.SourceRule.Nodes) { if (!rule.ReplacementRule.Nodes.Exists(nodeB => nodeB.AlphabetID == nodeA.AlphabetID && nodeB.Ordering == nodeA.Ordering && nodeB.Children.Count == nodeA.Children.Count && nodeB.Parents.Count == nodeA.Parents.Count )) { return(true); } } // It's illegal and isomorphic. return(false); }
// No 8. OrphanNode. private static bool ValidateOrphanNode(MissionRule rule, GraphGrammar graphGrammar) { // If node has no parent, it is an orphan. // Indegree = 0 quantity cannot > 1. return(graphGrammar.Nodes.FindAll(n => n.Parents.Count == 0).Count == 1); }
// Validate the graph grammar (one of pair of rule). public static KeyValuePair <ValidationLabel, string> Validate(MissionRule rule, GraphGrammar graphGrammar) { // Initial the error to none. _error = ValidationLabel.NoError; // Execute each method. foreach (var method in _validationMethods) { if (!method.Value.Invoke(rule, graphGrammar)) { _error = (method.Key); break; } } // Return Error Message. return(new KeyValuePair <ValidationLabel, string>(_error, SelectErrorType(_error))); }
// Buttons for operating the graph. private void LayoutFunctionButtons() { GUILayout.BeginArea(Container.FunctionButtonsArea); GUILayout.BeginVertical(SampleStyle.Frame(SampleStyle.ColorLightestGrey)); GUILayout.BeginHorizontal(); // Random seed. Seed = SampleStyle.IntFieldLabeled(Languages.GetText("GenerateMission-Seed"), Seed, SampleStyle.IntFieldLabel, SampleStyle.IntField, SampleStyle.IntFieldHeight); if (GUILayout.Button(Languages.GetText("GenerateMission-Random"), SampleStyle.GetButtonStyle(SampleStyle.ButtonType.Regular, SampleStyle.ButtonColor.Blue), SampleStyle.ButtonHeight)) { Seed = Random.Range(1, 1000000); // Unfocus from the field. GUI.FocusControl("FocusToNothing"); } GUILayout.EndHorizontal(); // If error occur, disable apply button. EditorGUI.BeginDisabledGroup(_errorType != ErrorType.None); // Mission and Space Graph button. GUILayout.BeginHorizontal(); if (GUILayout.Button(Languages.GetText("GenerateMission-Initial"), SampleStyle.GetButtonStyle(SampleStyle.ButtonType.Left, SampleStyle.ButtonColor.Blue), SampleStyle.ButtonHeight)) { // Rewrite system initialization. Mission.RewriteSystem.Initial(Seed); _isRuleChanged = false; // Update the current graph. _currentGraph = Mission.RewriteSystem.TransformFromGraph(); // Setting root node for CreVoxAttach. Mission.CreVoxAttach.SetCreVoxNodeRoot(_currentGraph.Nodes[0]); } EditorGUI.BeginDisabledGroup(_isRuleChanged); if (GUILayout.Button(Languages.GetText("GenerateMission-Iterate"), SampleStyle.GetButtonStyle(SampleStyle.ButtonType.Mid, SampleStyle.ButtonColor.Blue), SampleStyle.ButtonHeight)) { // Rewrite system iteration. Mission.RewriteSystem.Iterate(); // Update the current graph. _currentGraph = Mission.RewriteSystem.TransformFromGraph(); // Setting root node for CreVoxAttach. Mission.CreVoxAttach.SetCreVoxNodeRoot(_currentGraph.Nodes[0]); } if (GUILayout.Button(Languages.GetText("GenerateMission-Complete"), SampleStyle.GetButtonStyle(SampleStyle.ButtonType.Right, SampleStyle.ButtonColor.Blue), SampleStyle.ButtonHeight)) { var stopWatch = System.Diagnostics.Stopwatch.StartNew(); // Iterate until finish. while ( ( // Still exist non-terminal nodes. _currentGraph.Nodes.Exists(n => n.Terminal == Mission.NodeTerminalType.NonTerminal) // Have to exhauste all rules that set minimum. || Mission.RewriteSystem.Rules.Sum(r => r.QuantityLimitMin) > 0 ) // Time limit is 3,000 ms. && stopWatch.ElapsedMilliseconds <= 3000 ) { // Rewrite system iteration. Mission.RewriteSystem.Iterate(); // Update the current graph. _currentGraph = Mission.RewriteSystem.TransformFromGraph(); } stopWatch.Stop(); // Setting root node for CreVoxAttach. Mission.CreVoxAttach.SetCreVoxNodeRoot(_currentGraph.Nodes[0]); } EditorGUI.EndDisabledGroup(); GUILayout.EndHorizontal(); EditorGUI.EndDisabledGroup(); GUILayout.EndVertical(); GUILayout.EndArea(); }
// No 1. LeftMoreThanRight. private static bool ValidateLeftMoreThanRight(MissionRule rule, GraphGrammar graphGrammar) { // Are Nodes of sourceRule more than nodes of replacementRule? return(rule.SourceRule.Nodes.Count <= rule.ReplacementRule.Nodes.Count ? true : true); }
// No 2. EmptyLeft. private static bool ValidateEmptyLeft(MissionRule rule, GraphGrammar graphGrammar) { // Is there no node in sourceRule? return(rule.SourceRule.Nodes.Count != 0 ? true : false); }
// No 7. CyclicLink. private static bool ValidateCyclicLink(MissionRule rule, GraphGrammar graphGrammar) { // Store the parents and children to avoid the repeat call method. Dictionary <GraphGrammarNode, List <GraphGrammarNode> > parentsTable = new Dictionary <GraphGrammarNode, List <GraphGrammarNode> >(); Dictionary <GraphGrammarNode, List <GraphGrammarNode> > childrenTable = new Dictionary <GraphGrammarNode, List <GraphGrammarNode> >(); foreach (var node in graphGrammar.Nodes) { parentsTable[node] = node.Parents; childrenTable[node] = node.Children; } // Kahn's Algorithm // Array that record the removed edges. bool[,] _usedEdge = new bool[graphGrammar.Nodes.Count, graphGrammar.Nodes.Count]; // Non-indegree queue. Queue <GraphGrammarNode> nonIndegree = new Queue <GraphGrammarNode>(); // Push non-indegree node to queue. foreach (var node in graphGrammar.Nodes.FindAll(x => parentsTable[x].Count == 0 && // children can not be node itself. (!childrenTable[x].Exists(p => p.Ordering == x.Ordering)))) { nonIndegree.Enqueue(node); } // Bfs. while (nonIndegree.Count > 0) { // Pop. GraphGrammarNode popNode = nonIndegree.Dequeue(); // Remove the edge between this node and children node. foreach (var childNode in childrenTable[popNode]) { // Remove edge. _usedEdge[popNode.Ordering - 1, childNode.Ordering - 1] = true; // Check this child if it is non-indegree or not. bool hasInput = false; foreach (var parentNode in parentsTable[childNode]) { if (!_usedEdge[parentNode.Ordering - 1, childNode.Ordering - 1]) { hasInput = true; break; } } // If it is non-indegree then push it. if (!hasInput) { nonIndegree.Enqueue(childNode); } } } // Return false when any edge exist. It represents that this is a cyclic link. foreach (var node in graphGrammar.Nodes) { foreach (var childNode in childrenTable[node]) { if (!_usedEdge[node.Ordering - 1, childNode.Ordering - 1]) { return(false); } } } // Otherwise, this is not cyclic link. return(true); }
// No 4. IsolatedConnection. private static bool ValidateIsolatedConnection(MissionRule rule, GraphGrammar graphGrammar) { return(!(graphGrammar.Connections.Where(c => c.StartpointStickyOn == null || c.EndpointStickyOn == null).Any())); }