public Battle(BattleParams param, Navmesh navmesh, LuaEnv luaEnv) { this.rid = param.id; this.data = ModelFactory.GetBattleData(Utils.GetIDFromRID(this.rid)); this._luaEnv = luaEnv; this._context = new UpdateContext(); this._entityManager = new EntityManager(this); this._buffManager = new BuffManager(this); this._random = new ConsistentRandom(param.rndSeed); this._pathManager = new NavMeshProxy(); this._timer0 = new TimeScheduler(); this._pathManager.Create(navmesh); if (!string.IsNullOrEmpty(this.data.script)) { this._script = new Script(this, this._luaEnv, this.data.script); this._script.Call(Script.S_ON_BATTLE_INITIALIZED); } this.CreatePlayers(param); foreach (KeyValuePair <string, BattleData.Structure> kv in this.data.structures) { BattleData.Structure def = this.data.structures[kv.Key]; this.CreateBio(def.id, def.pos, def.dir, def.team); } foreach (KeyValuePair <string, BattleData.Neutral> kv in this.data.neutrals) { BattleData.Neutral def = this.data.neutrals[kv.Key]; this.CreateBio(def.id, def.pos, def.dir, def.team); } }
/// <summary> /// Build connections inside maze frame using hunt and kill algorithm. /// </summary> /// <param name="mazeFrame">Maze frame object.</param> /// <param name="huntAfterSegmentLength">Enter hunting mode after segment has grown to this length in world units, /// defaults to unlimited (uses smalles Scale value.</param> /// <param name="huntProbability">Probability of starting hunt when it arrives, defaults to 0.50.</param> protected void BuildMazeFrameUsingHuntAndKill(ref MazeFrame mazeFrame, float huntAfterSegmentLength = float.PositiveInfinity, float huntProbability = 0.50f) { if (mazeRandGen == null) { mazeRandGen = new ConsistentRandom(); } if (randGen == null) { randGen = new ConsistentRandom(); } // Build maze using hunt and kill /* 1) Make the initial node the current node and mark it as visited * 2) While there are unvisited nodes * 1) If the current node has any neighbours which have not been visited * 1) Choose randomly one of the unvisited neighbours * 2) Remove the wall between the current node and the chosen node * 3) Make the chosen node the current node and mark it as visited and add to targets * 2) Find random unvisited node next to visited node * 1) Make it the current node and mark it as visited and add to targets */ // Check start/end node active status if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive) { throw new System.Exception("Start/end nodes are required to be active."); } // Set storage of visit flags and create list for hunt to target int nNodesVisited = 0; Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count); foreach (MazeNode nd in mazeFrame.Nodes) { if (nd.IsActive) { isVisited.Add(nd.Identifier, false); } else { isVisited.Add(nd.Identifier, true); nNodesVisited++; } } List <MazeNode> targets = new List <MazeNode>(Mathf.RoundToInt(mazeFrame.Nodes.Count / 2f)); // half of full maze should be more than enough // Ignore nodes without neighbors foreach (MazeNode nd in mazeFrame.Nodes) { if (nd.AllNeighbors.Count == 0) { isVisited[nd.Identifier] = true; nNodesVisited++; } } // Determine huntAfterNConnections int huntAfterNConnections = Mathf.RoundToInt(huntAfterSegmentLength / Scale.ComponentMin()); //Initialize near startNode MazeNode currentNode = mazeFrame.StartNode; isVisited[currentNode.Identifier] = true; nNodesVisited++; targets.Add(currentNode); int count = 0; int nConnectionsMade = 0; while (nNodesVisited < mazeFrame.Nodes.Count) { // See if there are unvisited neighbors List <MazeNode> unvisitedNeighbors = new List <MazeNode>(); foreach (MazeNode nb in currentNode.AllNeighbors) { if (!isVisited[nb.Identifier]) { unvisitedNeighbors.Add(nb); } } if (nConnectionsMade >= huntAfterNConnections) { if (mazeRandGen.NextDouble() < huntProbability) { nConnectionsMade = 0; } } if (unvisitedNeighbors.Count > 0 && nConnectionsMade < huntAfterNConnections) { // Select random neighbor (2) MazeNode selNode = unvisitedNeighbors[mazeRandGen.Next(unvisitedNeighbors.Count)]; // Add as connected neighbor to current node object(3) and vice versa currentNode.AddConnectionByReference(selNode); // Switch to new node(4) and record visit currentNode = selNode; isVisited[currentNode.Identifier] = true; nNodesVisited++; targets.Add(currentNode); nConnectionsMade++; } else { // Find random unvisited node neighboring a visited node List <int> targetInd = new List <int>(targets.Count); for (int i = 0; i < targets.Count; i++) { targetInd.Insert(i, i); } bool found = false; Stack <MazeNode> targetsToRemove = new Stack <MazeNode>(); int huntCount = 0; while (!found) { // sample a random target node int randInd = targetInd[mazeRandGen.Next(targetInd.Count)]; MazeNode currTarget = targets[randInd]; // See if target has unvisited neighbors unvisitedNeighbors = new List <MazeNode>(); foreach (MazeNode nb in currTarget.AllNeighbors) { if (!isVisited[nb.Identifier]) { unvisitedNeighbors.Add(nb); } } // if it has no unvisited neighors, remove target from list and go again, otherwise, pick random unvisited neighbor if (unvisitedNeighbors.Count == 0) { targetInd.Remove(randInd); targetsToRemove.Push(currTarget); } else { // Select random neighbor and set as currentNode currentNode = currTarget; found = true; } // Reset connections counter nConnectionsMade = 0; // Safety check huntCount++; if (huntCount > targets.Count) { throw new System.Exception("Hunt did not terminate correctly."); } } // Remove nodes from targets that had no unvisisted neigbors while (targetsToRemove.Count > 0) { targets.Remove(targetsToRemove.Pop()); } } // Safety check count++; if (targets.Count == 0 || count > Mathf.Pow(mazeFrame.Nodes.Count, 2)) { throw new System.Exception("Hunt and kill loop did not terminate correctly."); } } // Sanity check foreach (MazeNode nb in mazeFrame.Nodes) { if (!isVisited[nb.Identifier]) { throw new System.Exception("Hunt and kill loop did not terminate correctly."); } } }
/// <summary> /// /// Build connections inside maze frame using recursive backtracking algorithm. /// </summary> /// <param name="mazeFrame">Maze frame object.</param> protected void BuildMazeFrameUsingRecursiveBacktracking(ref MazeFrame mazeFrame) { if (mazeRandGen == null) { mazeRandGen = new ConsistentRandom(); } if (randGen == null) { randGen = new ConsistentRandom(); } // Build maze using recursive backtracking // From wikipedia: /* 1) Make the initial node the current node and mark it as visited * 2) While there are unvisited nodes * 1) If the current node has any neighbours which have not been visited * 1) Choose randomly one of the unvisited neighbours * 2) Push the current node to the stack * 3) Remove the wall between the current node and the chosen node * 4) Make the chosen node the current node and mark it as visited * 2) Else if stack is not empty * 1) Pop a node from the stack * 2) Make it the current node */ // Check start/end node active status if (!mazeFrame.StartNode.IsActive || !mazeFrame.EndNode.IsActive) { throw new System.Exception("Start/end nodes are required to be active."); } // Set storage of visit flags and create stack for visisted nodes int nNodesVisited = 0; Dictionary <string, bool> isVisited = new Dictionary <string, bool>(mazeFrame.Nodes.Count); foreach (MazeNode nd in mazeFrame.Nodes) { if (nd.IsActive) { isVisited.Add(nd.Identifier, false); } else { isVisited.Add(nd.Identifier, true); nNodesVisited++; } } Stack <MazeNode> nodeTrack = new Stack <MazeNode>(mazeFrame.Nodes.Count); // FIXME does preallocating help performance here? // Ignore nodes without neighbors foreach (MazeNode nd in mazeFrame.Nodes) { if (nd.AllNeighbors.Count == 0) { isVisited[nd.Identifier] = true; nNodesVisited++; } } //Initialize near startNode MazeNode currentNode = mazeFrame.StartNode; isVisited[currentNode.Identifier] = true; nNodesVisited++; while (nNodesVisited < mazeFrame.Nodes.Count) { // See if there are unvisited neighbors List <MazeNode> unvisitedNeighbors = new List <MazeNode>(); foreach (MazeNode nb in currentNode.AllNeighbors) { if (!isVisited[nb.Identifier]) { unvisitedNeighbors.Add(nb); } } if (unvisitedNeighbors.Count != 0) { // Add current node to stack (1) nodeTrack.Push(currentNode); // Select random neighbor (2) //MazeNode selNode = RandomlySelectNodeWithBiasTowardsForwards(currentNode, unvisitedNeighbors); //Node selNode = RandomlySelectNodeWithBiasTowardsNode(currentNode, unvisitedNeighbors, endNode.AllNeighbors[0], 2,mazeRandGen); MazeNode selNode = unvisitedNeighbors[mazeRandGen.Next(unvisitedNeighbors.Count)]; // Add as connected neighbor to current node object(3) and vice versa currentNode.AddConnectionByReference(selNode); // Switch to new node(4) and record visit currentNode = selNode; isVisited[currentNode.Identifier] = true; nNodesVisited++; } else if ((unvisitedNeighbors.Count == 0) && (nodeTrack.Count != 0)) { currentNode = nodeTrack.Pop(); } // Safety check if ((nodeTrack.Count == 0) && (nNodesVisited > mazeFrame.Nodes.Count)) { throw new System.Exception("Recursive backtracking loop did not terminate correctly."); } } // Sanity check foreach (MazeNode nb in mazeFrame.Nodes) { if (isVisited[nb.Identifier] == false) { throw new System.Exception("Recursive backtracking loop did not terminate correctly."); } } }
private IEnumerator BuildNewMaze() { float mazeFrameElementsBuilt = 0; mazeFrameMeshesArePresent = false; // Initialize maze creator MazeFrameCreator mazeFrameCreator = null; switch (mazeShape) { case 0: { mazeFrameCreator = new MazeFrameCreatorSquare3D(mazeSize, nQuadrants) { Scale = mazeScale, Jitter = mazeJitter }; break; } case 1: { mazeFrameCreator = new MazeFrameCreatorMeshIcosahedron(nDivisions, nQuadrants) { Scale = mazeScale, Jitter = mazeJitter }; break; } } // Set random seed selector System.Random randGen = new ConsistentRandom(); // Randomize order of difficulties int[] randomQuadrantIndices = Utilities.RandomIndices(nQuadrants); // Generate maze! if (nQuadrants != 0 && nQuadrants != difficulties.Length) { throw new System.ArgumentException("When using quadrants, nQuadrants and nDifficulties should be equal."); } List <MazeFrame> singleMazeFrames = new List <MazeFrame>(difficulties.Length); for (int iDifficulty = 0; iDifficulty < difficulties.Length; iDifficulty++) { List <MazeFrame> singleFrameSections = new List <MazeFrame>(nSections); for (int iSections = 0; iSections < nSections; iSections++) { // Create sections int quadrantInd; //if (nQuadrants != 0) { quadrantInd = iDifficulty; } if (nQuadrants != 0) { quadrantInd = randomQuadrantIndices[iDifficulty]; } else { quadrantInd = 0; } MazeFrame currSection = null; int currDifficulty = difficulties[iDifficulty]; int currSeedInd = randGen.Next(seedData[quadrantInd].seeds[currDifficulty].Length); mazeFrameCreator.RandomSeed = seedData[quadrantInd].seeds[currDifficulty][currSeedInd]; //mazeFrameCreator.RandomSeed = seedData[quadrantInd].seeds[iDifficulty][iSections]; if (nQuadrants == 0) { currSection = mazeFrameCreator.GenerateMaze(); } else { currSection = mazeFrameCreator.GenerateEmptyMazeFrame(); mazeFrameCreator.GenerateMaze(ref currSection, quadrantInd); currSection.SetOnShortestPath(currSection.Quadrants[quadrantInd], 0); // HACKY } //currSection.KeepOnlyShortestPathConnections(); //currSection.AddPathSegments(); // DEBUG if (nQuadrants != 0) { Debug.Log(currSection.GetNIntersectionsOnPath()[0] + " " + currSection.GetDifficulty(quadrantInd)[0] + " - " + seedData[quadrantInd].difficulties[currDifficulty][currSeedInd]); } else { Debug.Log(currSection.GetNIntersectionsOnPath()[0] + " " + currSection.GetDifficulty()[0]); } // DEBUG singleFrameSections.Add(currSection); mazeFrameElementsBuilt++; LevelBuiltProgressPercentage = (mazeFrameElementsBuilt / (difficulties.Length * nSections)) / 2; yield return(null); } MazeFrame currSingleFrame; if (singleFrameSections.Count > 1) { //MazeFrame currSingleFrame = MazeFrame.Concatenate(singleFrameSections, mazeFrameCreator.Scale, mazeDirection); currSingleFrame = MazeFrame.CombineShrink(singleFrameSections, shrinkFactor); } else { currSingleFrame = singleFrameSections[0]; } currSingleFrame.AddOffsetStartNode(Vector3.Scale(mazeDirection, mazeScale) * startOffsetFactor, true); currSingleFrame.AddOffsetEndNode(Vector3.Scale(mazeDirection, mazeScale) * endOffsetFactor, true); for (int iInc = 0; iInc < iDifficulty; iInc++) { currSingleFrame.IncrementShortestPathIndices(); } singleMazeFrames.Add(currSingleFrame); } if (singleMazeFrames.Count > 1) { mazeFrame = MazeFrame.Merge(singleMazeFrames); } else { mazeFrame = singleMazeFrames[0]; } //mazeFrame.ConnectUnconnectedNodes(); mazeFrame.AddPathSegments(); yield return(null); // Populate maze and get return splines and maze objects mazePopulator.PopulateWithSplineFittedBars(mazeFrame, ref mazeFrameSplines, ref mazeObjects, objectScaling); //mazePopulator.PopulateWithSplineFittedCylinders(mazeFrame, ref mazeFrameSplines, ref mazeObjects, objectScaling); // Wait till population is complete while (!mazeFrameMeshesArePresent) { if (mazePopulator.MazeObjectsPlaced == mazeFrameSplines.SplineSegments.Count) { mazeFrameMeshesArePresent = true; } yield return(null); } // Create and Initialize gravity frame gravityFrameController = new GravityFrameController(mazeFrame, mazeFrameSplines, gravJunctionDistFactor, gravPlaneWidth); yield return(null); // Place others PlacePlayerAndCamera(); PlaceEndPortal(); PlaceCubeOfDeath(); PlaceMindWarpTriggers(); }
/// <summary> /// Find random integer in list with integer-specific probability specified by <paramref name="weightsArray"/>. /// </summary> /// <returns>Random integer from list.</returns> /// <param name="intArray">Array of integers to get random integer from.</param> /// <param name="weightsArray">Weights specifying integer-specific probability (can be bigger than 1) </param> public static int GetRandomIntWeightedProbability(int[] intArray, float[] weightsArray, ConsistentRandom randGen) { // Find random integer using integer-specific weights // check inputs if (intArray.Length != weightsArray.Length) { throw new System.ArgumentException("Input arrays should be of equal length."); } // Rescale weights float sum = 0; for (int i = 0; i < weightsArray.Length; i++) { sum = sum + weightsArray[i]; } for (int i = 0; i < weightsArray.Length; i++) { weightsArray[i] = weightsArray[i] / sum; } if (Mathf.Approximately(sum, 0)) { throw new System.ArgumentException("Sum of weights cannot be zero."); } // Find index using above probabilities float p = (float)randGen.NextDouble(); int ind = -1; for (int i = 0; i < weightsArray.Length; i++) { p = p - weightsArray[i]; if (p <= 0) { ind = i; break; } } if (ind == -1) { throw new System.Exception("Weighted probability selection failed."); } return(ind); //// DEBUG //string str = ""; //for (int i = 0; i < weightsArray.Length; i++) { str = str + "-" + weightsArray[i]; } //Debug.Log(str); //// DEBUG }
private IEnumerator CreateMazesFromSeeds() { // Parse difficulty float[,] difficultyBounds = new float[nDifficulties, 2]; float difficultyStartPad = difficultyLimits[0]; float difficultyEndPad = 1 - difficultyLimits[1]; float diffRange = 1 - (difficultyStartPad + difficultyEndPad); float currDiffSize = difficultySize * diffRange; float diffSpacing = (diffRange - (nDifficulties * currDiffSize)) / (nDifficulties - 1); if (diffSpacing < 0) { throw new System.Exception("Overlapping difficulties."); } for (int i = 0; i < (nDifficulties); i++) { difficultyBounds[i, 0] = difficultyStartPad + (currDiffSize + diffSpacing) * i; difficultyBounds[i, 1] = difficultyBounds[i, 0] + currDiffSize; } string boundsDisp = difficultyBounds[0, 0] + ">x<=" + difficultyBounds[0, 1]; for (int i = 1; i < nDifficulties; i++) { boundsDisp = boundsDisp + " | " + difficultyBounds[i, 0] + ">x<=" + difficultyBounds[i, 1]; } Debug.Log(boundsDisp); //seedList[0] = new List<int>(nMazes); // difficulty <=.20 //seedList[0] = new List<int>(nMazes); // difficulty >.20 <=.40 //seedList[0] = new List<int>(nMazes); // difficulty >.40 <=.60 //seedList[0] = new List<int>(nMazes); // difficulty >.60 <=80 //seedList[0] = new List<int>(nMazes); // difficulty >.80 <=1 // Set random seed int mainRandomSeed = System.Environment.TickCount; ConsistentRandom mainRandomGenenerator = new ConsistentRandom(mainRandomSeed); // Setup maze creators MazeFrameCreator mazeFrameCreator = null; switch (mazeShape) { case 0: { mazeFrameCreator = new MazeFrameCreatorSquare3D(mazeSize, nQuadrants); break; } case 1: { mazeFrameCreator = new MazeFrameCreatorMeshIcosahedron(divisions, nQuadrants); break; } } mazeFrameCreator.Scale = mazeScale; // Used in hunt and kill for determining when to hunt // Create level from new seeds int ittMax = 1; if (nQuadrants != 0) { ittMax = nQuadrants; } for (int iQuadrant = 0; iQuadrant < ittMax; iQuadrant++) { // Setup seed/difficulty list List <List <int> > seedList = new List <List <int> >(nDifficulties); for (int i = 0; i < nDifficulties; i++) { seedList.Add(new List <int>(nMazes)); } List <List <float> > difficultyList = new List <List <float> >(nDifficulties); for (int i = 0; i < nDifficulties; i++) { difficultyList.Add(new List <float>(nMazes)); } bool done = false; int count = 0; while (!done) { // set new seed int mazeSeed = mainRandomGenenerator.Next(); mazeFrameCreator.RandomSeed = mazeSeed; // get maze MazeFrame mazeFrame = mazeFrameCreator.GenerateEmptyMazeFrame(); if (nQuadrants != 0) { mazeFrame.SetActiveQuadrant(iQuadrant); } mazeFrameCreator.GenerateMaze(ref mazeFrame); // Save according to difficulty float difficulty; if (nQuadrants == 0) { difficulty = mazeFrame.GetDifficulty()[0]; } else { difficulty = mazeFrame.GetDifficulty(iQuadrant)[0]; } for (int i = 0; i < nDifficulties; i++) { if (difficulty > difficultyBounds[i, 0] && difficulty <= difficultyBounds[i, 1] && seedList[i].Count < nMazes) { seedList[i].Add(mazeSeed); difficultyList[i].Add(difficulty); } } // Check end condition done = true; for (int i = 0; i < nDifficulties; i++) { done = done && seedList[i].Count == nMazes; } // safety check count++; if (count == count * 100) { throw new System.Exception("Something went wrong."); } // Display progress string disp; if (nQuadrants == 0) { disp = count + ": " + seedList[0].Count; } else { disp = "quadrant" + (iQuadrant + 1) + "of" + nQuadrants + " | " + count + ": " + seedList[0].Count; } for (int i = 1; i < nDifficulties; i++) { disp = disp + ", " + seedList[i].Count; } Debug.Log(disp + " | d = " + difficulty.ToString("0.0000")); // Yield WaitForSecondsRealtime wait = null; if (wait == null) { wait = new WaitForSecondsRealtime(0.0001f); } if ((count % 10) == 0) { yield return(wait); } } // Convert to SeedData object if (nDifficulties == 5) { seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(), seedList[3].ToArray(), seedList[4].ToArray(), difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(), difficultyList[3].ToArray(), difficultyList[4].ToArray(), mazeSize, mazeFrameCreator.GetType().FullName); } else if (nDifficulties == 4) {// Convert to SeedData object seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(), seedList[3].ToArray(), difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(), difficultyList[3].ToArray(), mazeSize, mazeFrameCreator.GetType().FullName); } else if (nDifficulties == 3) {// Convert to SeedData object seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), seedList[2].ToArray(), difficultyList[0].ToArray(), difficultyList[1].ToArray(), difficultyList[2].ToArray(), mazeSize, mazeFrameCreator.GetType().FullName); } else if (nDifficulties == 2) {// Convert to SeedData object seedData = new SeedData(seedList[0].ToArray(), seedList[1].ToArray(), difficultyList[0].ToArray(), difficultyList[1].ToArray(), mazeSize, mazeFrameCreator.GetType().FullName); } else { throw new System.Exception("Requested nDifficulty not implemented due to the stupid save part."); } // Save to disk string creatorName = ""; string filePath; switch (mazeShape) { case 0: { creatorName = mazeFrameCreator.GetType().FullName + "-" + mazeSize.ToString() + "-" + mazeScale.ToString(); break; } case 1: { creatorName = mazeFrameCreator.GetType().FullName + "-" + divisions.ToString() + "-" + mazeScale.ToString(); break; } } if (nQuadrants != 0) { filePath = filePathBase + "-" + creatorName + "-" + "quadrant" + (iQuadrant + 1) + "of" + nQuadrants + ".json"; } else { filePath = filePathBase + "-" + creatorName + ".json"; } SeedDataController.SaveSeedData(seedData, filePath); } Debug.Log("Done!"); Debug.Break(); #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #endif }