diff --git a/TLM/Benchmarks/LoopUtilPerfTests.cs b/TLM/Benchmarks/LoopUtilPerfTests.cs index e54bab530..76f0a7744 100644 --- a/TLM/Benchmarks/LoopUtilPerfTests.cs +++ b/TLM/Benchmarks/LoopUtilPerfTests.cs @@ -7,7 +7,7 @@ public class LoopUtilPerfTests { [Benchmark] public void GenerateSpiralGridCoordsClockwise_Once_Radius17() { var radius = 17; - var coords = LoopUtil.GenerateSpiralGridCoordsClockwise().Take(radius * radius); + var coords = LoopUtil.GenerateSpiralGridCoordsCounterclockwise().Take(radius * radius); foreach (var coord in coords) { } } @@ -16,7 +16,7 @@ public void GenerateSpiralGridCoordsClockwise_Once_Radius17() { public void GenerateSpiralGridCoordsClockwise_10Times_Radius17() { var radius = 17; for (int i = 0; i < 10; i++) { - var coords = LoopUtil.GenerateSpiralGridCoordsClockwise().Take(radius * radius); + var coords = LoopUtil.GenerateSpiralGridCoordsCounterclockwise().Take(radius * radius); foreach (var coord in coords) { } } diff --git a/TLM/Benchmarks/SpiralPerfTests.cs b/TLM/Benchmarks/SpiralPerfTests.cs index 9d025743f..27d4ceaba 100644 --- a/TLM/Benchmarks/SpiralPerfTests.cs +++ b/TLM/Benchmarks/SpiralPerfTests.cs @@ -9,7 +9,7 @@ public class SpiralPerfTests { public void SpiralGetCoords_Once_Radius17() { var radius = 17; var spiral = new Spiral(radius); - var coords = spiral.GetCoords(radius); + var coords = spiral.GetCoordsCounterclockwise(radius); for (int i = 0; i < coords.Count; i++) { var coord = coords[i]; } @@ -20,7 +20,7 @@ public void SpiralGetCoords_10Times_Radius17() { var radius = 17; var spiral = new Spiral(radius); for (int i = 0; i < 10; i++) { - var coords = spiral.GetCoords(radius); + var coords = spiral.GetCoordsCounterclockwise(radius); for (int j = 0; j < coords.Count; j++) { var coord = coords[j]; } @@ -30,7 +30,7 @@ public void SpiralGetCoords_10Times_Radius17() { [Benchmark] public void SpiralGetCoords_Once_Radius17_Precached() { var radius = 17; - var coords = _cachedSpiral17.GetCoords(radius); + var coords = _cachedSpiral17.GetCoordsCounterclockwise(radius); for (int i = 0; i < coords.Count; i++) { var coord = coords[i]; } @@ -40,7 +40,7 @@ public void SpiralGetCoords_Once_Radius17_Precached() { public void SpiralGetCoords_10Times_Radius17_Precached() { var radius = 17; for (int i = 0; i < 10; i++) { - var coords = _cachedSpiral17.GetCoords(radius); + var coords = _cachedSpiral17.GetCoordsCounterclockwise(radius); for (int j = 0; j < coords.Count; j++) { var coord = coords[j]; } diff --git a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs index 471ed9154..7df035188 100644 --- a/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs +++ b/TLM/TLM/Manager/Impl/AdvancedParkingManager.cs @@ -22,6 +22,7 @@ public class AdvancedParkingManager IAdvancedParkingManager { private readonly Spiral _spiral; + private Randomizer _randomizer; public static readonly AdvancedParkingManager Instance = new AdvancedParkingManager(SingletonLite.instance); @@ -32,6 +33,7 @@ public static readonly AdvancedParkingManager Instance public AdvancedParkingManager(Spiral spiral) { _spiral = spiral ?? throw new ArgumentNullException(nameof(spiral)); + _randomizer = new Randomizer(); _findParkingSpaceDelegate = GameConnectionManager.Instance.PassengerCarAIConnection.FindParkingSpace; _findParkingSpacePropDelegate = GameConnectionManager.Instance.PassengerCarAIConnection.FindParkingSpaceProp; @@ -1931,7 +1933,7 @@ public bool FindParkingSpaceForCitizen(Vector3 endPos, // found a building with parking space if (Constants.ManagerFactory.ExtPathManager.FindPathPositionWithSpiralLoop( parkPos, - endPos, + knownParkingSpaceLocationId.ToBuilding().m_position, ItemClass.Service.Road, NetInfo.LaneType.Pedestrian, VehicleInfo.VehicleType.None, @@ -1947,10 +1949,10 @@ public bool FindParkingSpaceForCitizen(Vector3 endPos, if (logParkingAi) { Log._Debug( $"Navigating citizen instance {extDriverInstance.instanceId} to parking " + - $"building {knownParkingSpaceLocationId}! segment={endPathPos.m_segment}, " + + $"building {knownParkingSpaceLocationId}, parkPos={parkPos}! segment={endPathPos.m_segment}, " + $"laneIndex={endPathPos.m_lane}, offset={endPathPos.m_offset}. " + $"CurrentPathMode={extDriverInstance.pathMode} " + - $"calculateEndPos={calculateEndPos}"); + $"calculateEndPos={calculateEndPos}, endPos={endPos}"); } return true; @@ -2383,14 +2385,10 @@ bool LoopHandler(int i, int j) { // randomize target position to allow for opposite road-side parking ParkingAI parkingAiConf = GlobalConfig.Instance.ParkingAI; - segCenter.x += - Singleton.instance.m_randomizer.Int32( - parkingAiConf.ParkingSpacePositionRand) - + segCenter.x += rng.Int32(parkingAiConf.ParkingSpacePositionRand) - (parkingAiConf.ParkingSpacePositionRand / 2u); - segCenter.z += - Singleton.instance.m_randomizer.Int32( - parkingAiConf.ParkingSpacePositionRand) - + segCenter.z += rng.Int32(parkingAiConf.ParkingSpacePositionRand) - (parkingAiConf.ParkingSpacePositionRand / 2u); if (netSegment.GetClosestLanePosition( @@ -2460,7 +2458,7 @@ bool LoopHandler(int i, int j) { return true; } - var coords = _spiral.GetCoords(radius); + var coords = _spiral.GetCoordsRandomDirection(radius, ref _randomizer); for (int i = 0; i < radius * radius; i++) { if (!LoopHandler((int)(centerI + coords[i].x), (int)(centerJ + coords[i].y))) { break; @@ -2571,7 +2569,7 @@ bool LoopHandler(int i, int j) { return true; } - var coords = _spiral.GetCoords(radius); + var coords = _spiral.GetCoordsRandomDirection(radius, ref _randomizer); for (int i = 0; i < radius * radius; i++) { if (!LoopHandler((int)(centerI + coords[i].x), (int)(centerJ + coords[i].y))) { break; diff --git a/TLM/TLM/Manager/Impl/ExtPathManager.cs b/TLM/TLM/Manager/Impl/ExtPathManager.cs index 326378080..30f606d77 100644 --- a/TLM/TLM/Manager/Impl/ExtPathManager.cs +++ b/TLM/TLM/Manager/Impl/ExtPathManager.cs @@ -1,7 +1,7 @@ namespace TrafficManager.Manager.Impl { - using ColossalFramework; using System; - using System.Linq; + using ColossalFramework; + using ColossalFramework.Math; using State; using TrafficManager.API.Manager; using TrafficManager.Util; @@ -13,12 +13,14 @@ public class ExtPathManager IExtPathManager { private readonly Spiral _spiral; + private Randomizer _randomizer; public static readonly ExtPathManager Instance = new ExtPathManager(SingletonLite.instance); private ExtPathManager(Spiral spiral) { _spiral = spiral ?? throw new ArgumentNullException(nameof(spiral)); + _randomizer = new Randomizer(); } /// @@ -386,7 +388,7 @@ bool FindHelper(int i, int j) { int spiralDist = Math.Max(Math.Abs(i - centerI), Math.Abs(j - centerJ)); // maximum norm - if (found && spiralDist > lastSpiralDist) { + if (found && lastSpiralDist > 0 && spiralDist > lastSpiralDist) { // last iteration return false; } @@ -454,8 +456,8 @@ bool FindHelper(int i, int j) { out float laneOffsetB)) { float dist = Vector3.SqrMagnitude(position - posA); - if (secondaryPosition != null) { - dist += Vector3.SqrMagnitude((Vector3)secondaryPosition - posA); + if (secondaryPosition.HasValue) { + dist += Vector3.SqrMagnitude(secondaryPosition.Value - posA); } if (dist < minDist) { @@ -471,9 +473,9 @@ bool FindHelper(int i, int j) { myDistanceSqrA = dist; dist = Vector3.SqrMagnitude(position - posB); - if (secondaryPosition != null) { + if (secondaryPosition.HasValue) { dist += Vector3.SqrMagnitude( - (Vector3)secondaryPosition - posB); + secondaryPosition.Value - posB); } if (laneIndexB < 0) { @@ -508,7 +510,7 @@ bool FindHelper(int i, int j) { return true; } - var coords = _spiral.GetCoords(radius); + var coords = _spiral.GetCoordsRandomDirection(radius, ref _randomizer); for (int i = 0; i < radius * radius; i++) { if (!FindHelper((int)(centerI + coords[i].x), (int)(centerJ + coords[i].y))) { break; diff --git a/TLM/TLM/Patch/_CitizenAI/_HumanAI/SimulationStepPatch.cs b/TLM/TLM/Patch/_CitizenAI/_HumanAI/SimulationStepPatch.cs index e8e527360..c85f42242 100644 --- a/TLM/TLM/Patch/_CitizenAI/_HumanAI/SimulationStepPatch.cs +++ b/TLM/TLM/Patch/_CitizenAI/_HumanAI/SimulationStepPatch.cs @@ -270,7 +270,8 @@ ref data.m_citizen.ToCitizen(), data.m_flags &= ~(CitizenInstance.Flags.HangAround | CitizenInstance.Flags.Panicking - | CitizenInstance.Flags.SittingDown); + | CitizenInstance.Flags.SittingDown + | CitizenInstance.Flags.Cheering); ArriveAtDestination(__instance, instanceID, ref data, false); citizenManager.ReleaseCitizenInstance(instanceID); diff --git a/TLM/TLM/Util/LoopUtil.cs b/TLM/TLM/Util/LoopUtil.cs index 260ace54c..b7bcab770 100644 --- a/TLM/TLM/Util/LoopUtil.cs +++ b/TLM/TLM/Util/LoopUtil.cs @@ -11,7 +11,7 @@ public static IEnumerable GenerateSpiralGridCoordsClockwise() { var cursorX = 0; var cursorY = 0; - var directionX = 1; + var directionX = -1; var directionY = 0; var segmentLength = 1; @@ -30,6 +30,44 @@ public static IEnumerable GenerateSpiralGridCoordsClockwise() { segmentsPassed = 0; //start of new segment //rotate clockwise + var buffer = -directionX; + directionX = directionY; + directionY = buffer; + + //increase segmentLength every second time + if (directionY == 0) { + segmentLength++; + } + } + } + + /// + /// Generates a stream of spiral grid coordinates counterclockwise. + /// + /// Stream of spiral grid coordinates until int.MaxValue + 1 elements were returned. + public static IEnumerable GenerateSpiralGridCoordsCounterclockwise() { + var cursorX = 0; + var cursorY = 0; + + var directionX = 1; + var directionY = 0; + + var segmentLength = 1; + var segmentsPassed = 0; + + for (var n = 0; n <= int.MaxValue; n++) { + yield return new Vector2(cursorX, cursorY); + + cursorX += directionX; + cursorY += directionY; + + segmentsPassed++; + if (segmentsPassed != segmentLength) { + continue; //if segment is not full yet + } + segmentsPassed = 0; //start of new segment + + //rotate counterClockwise var buffer = directionX; directionX = -directionY; directionY = buffer; diff --git a/TLM/TLM/Util/Spiral.cs b/TLM/TLM/Util/Spiral.cs index e25aa85d4..dc8138e91 100644 --- a/TLM/TLM/Util/Spiral.cs +++ b/TLM/TLM/Util/Spiral.cs @@ -4,6 +4,8 @@ using UnityEngine; namespace TrafficManager.Util { + using ColossalFramework.Math; + /// /// A spiral coords generator that will cache all previously generated values, /// and only generate new ones if necessary. @@ -12,9 +14,12 @@ public class Spiral { private readonly string radiusOutOfRangeMessage = "Must be in the range [1,int.MaxValue]"; private int _maxRadius; - private IEnumerator _enumerator; - private List _spiralCoords; - private ReadOnlyCollection _spiralCoordsReadOnly; + private IEnumerator _enumeratorClockwise; + private IEnumerator _enumeratorCounterClockwise; + private List _spiralCoordsClockwise; + private List _spiralCoordsCounterclockwise; + private ReadOnlyCollection _spiralCoordsClockwiseReadOnly; + private ReadOnlyCollection _spiralCoordsCounterclockwiseReadOnly; /// /// Initializes a new instance of the class. @@ -38,10 +43,14 @@ public Spiral(int radius) { } _maxRadius = 0; - _enumerator = LoopUtil.GenerateSpiralGridCoordsClockwise() + _enumeratorClockwise = LoopUtil.GenerateSpiralGridCoordsClockwise() + .GetEnumerator(); + _enumeratorCounterClockwise = LoopUtil.GenerateSpiralGridCoordsCounterclockwise() .GetEnumerator(); - _spiralCoords = new List(radius * radius); - _spiralCoordsReadOnly = new ReadOnlyCollection(_spiralCoords); + _spiralCoordsClockwise = new List(radius * radius); + _spiralCoordsCounterclockwise = new List(radius * radius); + _spiralCoordsClockwiseReadOnly = new ReadOnlyCollection(_spiralCoordsCounterclockwise); + _spiralCoordsCounterclockwiseReadOnly = new ReadOnlyCollection(_spiralCoordsCounterclockwise); Ensure(radius); } @@ -52,7 +61,43 @@ public Spiral(int radius) { public int MaxRadius => _maxRadius; /// - /// Gets spiral coords and ensures that the given radius is satisfied. + /// Gets spiral in random direction and ensired that the giver radius is satisfied + /// + /// Needed radius for the spiral coords. + /// Randomizer instance to perform randomization + /// + /// A readonly collection of spiral coords. + /// The result won't generate a new collection for performance reasons, + /// meaning it will return a reference to the same mutable underlying collection. + /// The returned collection will always contain the coords for 'MaxRadius', + /// the largest radius it was ever passed. + /// + public ReadOnlyCollection GetCoordsRandomDirection(int radius, ref Randomizer r) { + return r.Int32(2) == 0 ? GetCoordsCounterclockwise(radius) : GetCoordsClockwise(radius); + } + + /// + /// Gets spiral coords clockwise and ensures that the given radius is satisfied. + /// + /// Needed radius for the spiral coords. + /// + /// A readonly collection of spiral coords. + /// The result won't generate a new collection for performance reasons, + /// meaning it will return a reference to the same mutable underlying collection. + /// The returned collection will always contain the coords for 'MaxRadius', + /// the largest radius it was ever passed. + /// + public ReadOnlyCollection GetCoordsClockwise(int radius) { + if (radius < 1) { + throw new ArgumentOutOfRangeException(nameof(radius), radiusOutOfRangeMessage); + } + + Ensure(radius); + return _spiralCoordsClockwiseReadOnly; + } + + /// + /// Gets spiral coords counterclockwise and ensures that the given radius is satisfied. /// /// Needed radius for the spiral coords. /// @@ -62,13 +107,13 @@ public Spiral(int radius) { /// The returned collection will always contain the coords for 'MaxRadius', /// the largest radius it was ever passed. /// - public ReadOnlyCollection GetCoords(int radius) { + public ReadOnlyCollection GetCoordsCounterclockwise(int radius) { if (radius < 1) { throw new ArgumentOutOfRangeException(nameof(radius), radiusOutOfRangeMessage); } Ensure(radius); - return _spiralCoordsReadOnly; + return _spiralCoordsCounterclockwiseReadOnly; } private void Ensure(int radius) { @@ -77,11 +122,20 @@ private void Ensure(int radius) { } var enumerateCount = radius * radius; - _spiralCoords.Capacity = enumerateCount; - for (int i = _spiralCoords.Count; i < enumerateCount; i++) { - var hasNext = _enumerator.MoveNext(); + + _spiralCoordsClockwise.Capacity = enumerateCount; + for (int i = _spiralCoordsClockwise.Count; i < enumerateCount; i++) { + var hasNext = _enumeratorClockwise.MoveNext(); + if (hasNext) { + _spiralCoordsClockwise.Add(_enumeratorClockwise.Current); + } + } + + _spiralCoordsCounterclockwise.Capacity = enumerateCount; + for (int i = _spiralCoordsCounterclockwise.Count; i < enumerateCount; i++) { + var hasNext = _enumeratorCounterClockwise.MoveNext(); if (hasNext) { - _spiralCoords.Add(_enumerator.Current); + _spiralCoordsCounterclockwise.Add(_enumeratorCounterClockwise.Current); } } diff --git a/TLM/TMPE.UnitTest/Util/LoopUtilTests.cs b/TLM/TMPE.UnitTest/Util/LoopUtilTests.cs index 341e6311b..e0ef19253 100644 --- a/TLM/TMPE.UnitTest/Util/LoopUtilTests.cs +++ b/TLM/TMPE.UnitTest/Util/LoopUtilTests.cs @@ -15,7 +15,7 @@ public void GenerateSpiralGridCoordsClockwise_Radius1() { new Vector2() { x = 0f, y = 0f } }; - var actual = LoopUtil.GenerateSpiralGridCoordsClockwise() + var actual = LoopUtil.GenerateSpiralGridCoordsCounterclockwise() .Take(radius * radius) .ToList(); @@ -24,6 +24,45 @@ public void GenerateSpiralGridCoordsClockwise_Radius1() { [TestMethod] public void GenerateSpiralGridCoordsClockwise_Radius5() { + var radius = 5; + var expected = new List() + { + new Vector2() { x = 0f, y = 0f }, + new Vector2() { x = -1f, y = 0f }, + new Vector2() { x = -1f, y = 1f }, + new Vector2() { x = 0f, y = 1f }, + new Vector2() { x = 1f, y = 1f }, + new Vector2() { x = 1f, y = 0f }, + new Vector2() { x = 1f, y = -1f }, + new Vector2() { x = 0f, y = -1f }, + new Vector2() { x = -1f, y = -1f }, + new Vector2() { x = -2f, y = -1f }, + new Vector2() { x = -2f, y = 0f }, + new Vector2() { x = -2f, y = 1f }, + new Vector2() { x = -2f, y = 2f }, + new Vector2() { x = -1f, y = 2f }, + new Vector2() { x = 0f, y = 2f }, + new Vector2() { x = 1f, y = 2f }, + new Vector2() { x = 2f, y = 2f }, + new Vector2() { x = 2f, y = 1f }, + new Vector2() { x = 2f, y = 0f }, + new Vector2() { x = 2f, y = -1f }, + new Vector2() { x = 2f, y = -2f }, + new Vector2() { x = 1f, y = -2f }, + new Vector2() { x = 0f, y = -2f }, + new Vector2() { x = -1f, y = -2f }, + new Vector2() { x = -2f, y = -2f }, + }; + + var actual = LoopUtil.GenerateSpiralGridCoordsClockwise() + .Take(radius * radius) + .ToList(); + + CollectionAssert.AreEqual(expected, actual); + } + + [TestMethod] + public void GenerateSpiralGridCoordsCounterclockwise_Radius5() { var radius = 5; var expected = new List() { @@ -54,7 +93,7 @@ public void GenerateSpiralGridCoordsClockwise_Radius5() { new Vector2() { x = 2f, y = -2f }, }; - var actual = LoopUtil.GenerateSpiralGridCoordsClockwise() + var actual = LoopUtil.GenerateSpiralGridCoordsCounterclockwise() .Take(radius * radius) .ToList(); diff --git a/TLM/TMPE.UnitTest/Util/SpiralTests.cs b/TLM/TMPE.UnitTest/Util/SpiralTests.cs index ec8c8b441..bf137afa5 100644 --- a/TLM/TMPE.UnitTest/Util/SpiralTests.cs +++ b/TLM/TMPE.UnitTest/Util/SpiralTests.cs @@ -21,14 +21,14 @@ public void Constructor_Radius1_Should_Generate1SpiralCoords() { var cachedSpiral = new Spiral(initialRadius); - Assert.AreEqual(expectedSpiralCoordsCount, cachedSpiral.GetCoords(initialRadius).Count); + Assert.AreEqual(expectedSpiralCoordsCount, cachedSpiral.GetCoordsCounterclockwise(initialRadius).Count); } [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void GetSpiralCoords_RadiusOutOfRange_Should_ThrowException() { var spiral = new Spiral(1); - spiral.GetCoords(-1); + spiral.GetCoordsCounterclockwise(-1); } [TestMethod] @@ -39,7 +39,7 @@ public void GetSpiralCoords_Should_IncreaseIfRadiusLargerInitialRadius() { var cachedSpiral = new Spiral(initialRadius); - Assert.AreEqual(expectedSpiralCoordsCount, cachedSpiral.GetCoords(newRadius).Count); + Assert.AreEqual(expectedSpiralCoordsCount, cachedSpiral.GetCoordsCounterclockwise(newRadius).Count); } [TestMethod] @@ -75,13 +75,13 @@ public void GetSpiralCoords_Should_BeCorrectAfterEachTime() { ); var cachedSpiral = new Spiral(initialRadius); - var initialRadiusActualResult = cachedSpiral.GetCoords(initialRadius); + var initialRadiusActualResult = cachedSpiral.GetCoordsCounterclockwise(initialRadius); CollectionAssert.AreEqual(initialRadiusExpectedResult, initialRadiusActualResult); - var firstRadiusIncreaseActualResult = cachedSpiral.GetCoords(firstRadiusIncrease); + var firstRadiusIncreaseActualResult = cachedSpiral.GetCoordsCounterclockwise(firstRadiusIncrease); CollectionAssert.AreEqual(firstRadiusIncreaseExpectedResult, firstRadiusIncreaseActualResult); - var secondRadiusIncreaseActualResult = cachedSpiral.GetCoords(secondRadiusIncrease); + var secondRadiusIncreaseActualResult = cachedSpiral.GetCoordsCounterclockwise(secondRadiusIncrease); CollectionAssert.AreEqual(secondRadiusIncreaseExpectedResult, secondRadiusIncreaseActualResult); } @@ -105,9 +105,9 @@ public void GetSpiralCoords_Should_ShareTheResult() { ); var cachedSpiral = new Spiral(initialRadius); - var initialRadiusActualResult = cachedSpiral.GetCoords(initialRadius); - var firstRadiusIncreaseActualResult = cachedSpiral.GetCoords(firstRadiusIncrease); - var secondRadiusIncreaseActualResult = cachedSpiral.GetCoords(secondRadiusIncrease); + var initialRadiusActualResult = cachedSpiral.GetCoordsCounterclockwise(initialRadius); + var firstRadiusIncreaseActualResult = cachedSpiral.GetCoordsCounterclockwise(firstRadiusIncrease); + var secondRadiusIncreaseActualResult = cachedSpiral.GetCoordsCounterclockwise(secondRadiusIncrease); CollectionAssert.AreEqual(sharedExpectedResult, initialRadiusActualResult); CollectionAssert.AreEqual(sharedExpectedResult, firstRadiusIncreaseActualResult);