public List <Room> Generate(BiomeTile[,] tiles, int nThreads, int seed) { List <Room> outRooms = new List <Room>(); //Generate the initial rooms as rectangles, // then modify them from there with cellular automata. //Calculate the size of each initial room. Vector2i roomCellSize = tiles.SizeXY() / NRooms; Vector2i roomSize = roomCellSize - RoomSpacing; float minCircleLerp = Mathf.Lerp(0.5f, 0.0f, CirclePosVariance), maxCircleLerp = Mathf.Lerp(0.5f, 1.0f, CirclePosVariance); //Create the initial rooms. Vector2i max = tiles.SizeXY() + 1; for (int yStart = 0; (yStart + roomCellSize.y) < max.y; yStart += roomCellSize.y) { for (int xStart = 0; (xStart + roomCellSize.x) < max.x; xStart += roomCellSize.x) { Vector2i start = new Vector2i(xStart, yStart); //For the room's biome, get the biome of its center tile. Vector2i center = start + (roomCellSize / 2); PRNG prng = new PRNG(center.x, center.y, seed); Vector2i roomMin = center - (roomSize / 2), roomMax = center + (roomSize / 2); outRooms.Add(new Room(tiles.Get(center), new RectI(roomMin, roomMax))); //Carve circular spaces out of the room. int nCircles = MinCirclesPerRoom + (prng.NextInt() % (MaxCirclesPerRoom - MinCirclesPerRoom + 1)); for (int circI = 0; circI < nCircles; ++circI) { //Choose a random position for the circle centered around the origin. Vector2 posLerp = new Vector2(Mathf.Lerp(minCircleLerp, maxCircleLerp, prng.NextFloat()), Mathf.Lerp(minCircleLerp, maxCircleLerp, prng.NextFloat())); Vector2 circlePos = new Vector2(Mathf.Lerp(xStart, xStart + roomCellSize.x, posLerp.x), Mathf.Lerp(yStart, yStart + roomCellSize.y, posLerp.y)); float circleRadius = Mathf.Lerp(CircleMinRadius * roomSize.x, CircleMaxRadius * roomSize.y, prng.NextFloat()); CarveCircle(outRooms[outRooms.Count - 1], circlePos, circleRadius); } } } return(outRooms); }
public override float Start() { m_started = TimeService.Wall; m_delay = PRNG.NextFloat(0.01f, 0.05f); JobService.Enqueue(m_name, Execute, m_name, m_delay); return(m_delay + 1.0f); }
public override int ChooseRule(PRNG r, int nRules) { //Make a random double with equal distribution between 0 and (nRules - 1). float f = r.NextFloat(); const float minF = -0.5f; float maxF = (float)nRules - 0.5f; f = Mathf.Lerp(minF, maxF, f); //Bias it. f = Mathf.Pow(f, Distribution); return(Mathf.RoundToInt(f)); }
public static string GenerateName(Genders gender, int seed) { //Basic idea: Take a bunch of syllables and smash 'em together. //Every syllable has a beginning and ending letter. //These beginnings and endings can be split into three groups: // 1. Vowels -- can follow any consonant, but should usually not follow a vowel // 2. Ending consonant -- shouldn't follow an ending consonant, MIGHT follow a continuing consonant, can follow any vowel // 3. Continuing consonant -- can follow any continuing consonants or vowels, but not ending consonants. //These syllables are hard-coded and stored in the below "syllables" collection. PRNG rng = new PRNG(seed); const int minSyllables = 2, maxSyllables = 4; const float chance_VowelToVowel = 0.05f, chance_ContinuingConsonantToEndingConsonant = 0.5f; int nSyllables = rng.NextInt(minSyllables, maxSyllables + 1); //Start with a completely random syllable. System.Text.StringBuilder sb = new System.Text.StringBuilder(); Syllable firstSyllable = syllables.ElementAt(rng.NextInt(0, syllables.Count)); sb.Append(firstSyllable.Value); //Add successive syllables making sure they don't conflict with what came before. HashSet <Syllable> acceptableSyllables = new HashSet <Syllable>(); Syllable lastSyllable = firstSyllable; for (int i = 1; i < nSyllables; ++i) { //Get all acceptable syllables. acceptableSyllables.Clear(); foreach (Syllable syllable in syllables) { float chanceOfAccepting; switch (syllable.StartType) { case Syllable.Types.Vowel: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: chanceOfAccepting = chance_VowelToVowel; break; case Syllable.Types.ContinuingConsonant: case Syllable.Types.EndingConsonant: chanceOfAccepting = 1.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; case Syllable.Types.EndingConsonant: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: chanceOfAccepting = 1.0f; break; case Syllable.Types.ContinuingConsonant: chanceOfAccepting = chance_ContinuingConsonantToEndingConsonant; break; case Syllable.Types.EndingConsonant: chanceOfAccepting = 0.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; case Syllable.Types.ContinuingConsonant: switch (lastSyllable.EndType) { case Syllable.Types.Vowel: case Syllable.Types.ContinuingConsonant: chanceOfAccepting = 1.0f; break; case Syllable.Types.EndingConsonant: chanceOfAccepting = 0.0f; break; default: throw new NotImplementedException(lastSyllable.EndType.ToString()); } break; default: throw new NotImplementedException(syllable.StartType.ToString()); } if (chanceOfAccepting <= 0.0f || syllable == lastSyllable) { continue; } if (chanceOfAccepting >= 1.0f || rng.NextFloat() < chanceOfAccepting) { acceptableSyllables.Add(syllable); } } //Pick one randomly. var nextSyllable = acceptableSyllables.ElementAt(rng.NextInt(0, acceptableSyllables.Count)); lastSyllable = nextSyllable; sb.Append(nextSyllable.Value); } sb[0] = char.ToUpper(sb[0]); return(sb.ToString()); }
public void TestPRNG() { const int iterations = 5000000; Console.WriteLine(); int trues = 0; int larger = 0; int smaller = 0; for (int i = 0; i < iterations; i++) { if (PRNG.NextBool()) { trues++; } int val = PRNG.Next(5, 8); // 5, 6 or 7 switch (val) { case 5: smaller++; break; case 6: break; case 7: larger++; break; default: Assert.Fail("Value " + val + " isn't 5, 6 or 7"); break; } // ranged var r1 = (int)PRNG.NextUInt32(); var r2 = (int)PRNG.NextUInt32(); if (r1 < r2) { var rr = PRNG.Next(r1, r2); Assert.IsTrue(rr >= r1 && rr < r2, rr.ToString() + " is not between " + r1 + " and " + r2); } else if (r1 > r2) { var rr = PRNG.Next(r2, r1); Assert.IsTrue(rr >= r2 && rr < r1, rr.ToString() + " is not between " + r2 + " and " + r1); } } var p = (double)trues / (double)iterations; Assert.IsTrue(p > 0.495 && p < 0.505); const double third = 1.0 / 3.0; const double offset = third * 0.05; const double low = third - offset; const double high = third + offset; p = (double)smaller / (double)iterations; Assert.IsTrue(p > low && p < high); p = (double)larger / (double)iterations; Assert.IsTrue(p > low && p < high); // make sure nextdouble() and nextfloat() don't generate numbers outside range var state = RandomSeed.GetUInt64(); for (int r = 0; r < 10000000; r++) { double d = PRNG.NextDouble(ref state); Assert.IsTrue(d >= 0.0 && d < 1.0); float f = PRNG.NextFloat(ref state); Assert.IsTrue(f >= 0.0f && f < 1.0f); } }
/// <summary> /// Runs the generator and returns the spaces in the generated entrance. /// </summary> /// <param name="rooms"> /// The rooms that were generated. Used to choose a place to place an entrance. /// </param> /// <param name="theMap"> /// The map that new units are being generated into. /// </param> /// <param name="unitsToKeep"> /// The units to keep alive in the map, indexed by their ID's. /// Pass "null" if new units should be generated from scratch. /// </param> public List <Vector2i> Generate(Map theMap, EtMGame.WorldSettings genSettings, List <Room> rooms, int nThreads, int seed, UlongSet unitsToKeep = null) { //Choose a room and place the level's "entrance" into the middle of it. List <Vector2i> entranceSpaces = new List <Vector2i>(); { PRNG roomPlacer = new PRNG(unchecked (seed * 8957)); Vector2i entrance = rooms[roomPlacer.NextInt() % rooms.Count].OriginalBounds.Center; entrance = new Vector2i(Mathf.Clamp(entrance.x, 1, genSettings.Size - 2), Mathf.Clamp(entrance.y, 1, genSettings.Size - 2)); //Carve a small circle out of the map for the entrance. const float entranceRadius = 1.75f, entranceRadiusSqr = entranceRadius * entranceRadius; int entranceRadiusCeil = Mathf.CeilToInt(entranceRadius); Vector2i entranceRegionMin = entrance - new Vector2i(entranceRadiusCeil, entranceRadiusCeil), entranceRegionMax = entrance + new Vector2i(entranceRadiusCeil, entranceRadiusCeil); entranceRegionMin = new Vector2i(Mathf.Clamp(entranceRegionMin.x, 0, genSettings.Size - 1), Mathf.Clamp(entranceRegionMin.y, 0, genSettings.Size - 1)); entranceRegionMax = new Vector2i(Mathf.Clamp(entranceRegionMax.x, 0, genSettings.Size - 1), Mathf.Clamp(entranceRegionMax.y, 0, genSettings.Size - 1)); foreach (Vector2i entranceSpace in new Vector2i.Iterator(entranceRegionMin, entranceRegionMax + 1)) { if (entrance.DistanceSqr(entranceSpace) < entranceRadiusSqr) { entranceSpaces.Add(entranceSpace); theMap.Tiles[entranceSpace] = GameLogic.TileTypes.Empty; } } } //If we weren't given any units to place, generate some. if (unitsToKeep == null) { unitsToKeep = new UlongSet(); //Find or create the PlayerGroup. var playerGroup = GameLogic.Groups.PlayerGroup.Get(theMap); //Generate a certain number of player units. PRNG prng = new PRNG(seed); for (int i = 0; i < NStartingChars; ++i) { //PlayerChar's have 3 stats to randomize. //Distribute "points" among them randomly. //Prefer energy, then food, then strength. float totalPoints = StartingStatAbilities; float p = prng.NextFloat() * Math.Min(1.0f, totalPoints); float energy = p; totalPoints -= p; p = prng.NextFloat() * Math.Min(1.0f, totalPoints); float food = p; totalPoints -= p; float strength = totalPoints; energy = Mathf.Lerp(PlayerConsts.MinStart_Energy, PlayerConsts.MaxStart_Energy, energy); food = Mathf.Lerp(PlayerConsts.MinStart_Food, PlayerConsts.MaxStart_Food, food); strength = Mathf.Lerp(PlayerConsts.MinStart_Strength, PlayerConsts.MaxStart_Strength, strength); var gender = (i % 2 == 0) ? GameLogic.Units.Player_Char.Personality.Genders.Male : GameLogic.Units.Player_Char.Personality.Genders.Female; PlayerChar chr = new PlayerChar( theMap, playerGroup.ID, food, energy, strength, 1.0f, GameLogic.Units.Player_Char.Personality.GenerateName(gender, prng.NextInt()), gender); theMap.AddUnit(chr); unitsToKeep.Add(chr.ID); } } //Position the units. UnityEngine.Assertions.Assert.IsTrue(entranceSpaces.Count > 0); int unitsPerSpace = unitsToKeep.Count / entranceSpaces.Count; int unitI = 0, posI = 0; foreach (Unit unit in unitsToKeep.Select(id => theMap.GetUnit(id))) { unit.Pos.Value = entranceSpaces[posI]; unitI += 1; if (unitI >= unitsPerSpace) { unitI = 0; posI = (posI + 1) % entranceSpaces.Count; } } return(entranceSpaces); }