Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parking AI confused in large parking lots #1626

Merged
merged 12 commits into from
Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions TLM/Benchmarks/LoopUtilPerfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
}
Expand All @@ -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) {
}
}
Expand Down
8 changes: 4 additions & 4 deletions TLM/Benchmarks/SpiralPerfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
Expand All @@ -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];
}
Expand All @@ -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];
}
Expand All @@ -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];
}
Expand Down
20 changes: 9 additions & 11 deletions TLM/TLM/Manager/Impl/AdvancedParkingManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class AdvancedParkingManager
IAdvancedParkingManager
{
private readonly Spiral _spiral;
private Randomizer _randomizer;

public static readonly AdvancedParkingManager Instance
= new AdvancedParkingManager(SingletonLite<Spiral>.instance);
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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<SimulationManager>.instance.m_randomizer.Int32(
parkingAiConf.ParkingSpacePositionRand) -
segCenter.x += rng.Int32(parkingAiConf.ParkingSpacePositionRand) -
(parkingAiConf.ParkingSpacePositionRand / 2u);

segCenter.z +=
Singleton<SimulationManager>.instance.m_randomizer.Int32(
parkingAiConf.ParkingSpacePositionRand) -
segCenter.z += rng.Int32(parkingAiConf.ParkingSpacePositionRand) -
(parkingAiConf.ParkingSpacePositionRand / 2u);

if (netSegment.GetClosestLanePosition(
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 10 additions & 8 deletions TLM/TLM/Manager/Impl/ExtPathManager.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,12 +13,14 @@ public class ExtPathManager
IExtPathManager
{
private readonly Spiral _spiral;
private Randomizer _randomizer;

public static readonly ExtPathManager Instance =
new ExtPathManager(SingletonLite<Spiral>.instance);

private ExtPathManager(Spiral spiral) {
_spiral = spiral ?? throw new ArgumentNullException(nameof(spiral));
_randomizer = new Randomizer();
}

/// <summary>
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion TLM/TLM/Patch/_CitizenAI/_HumanAI/SimulationStepPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
40 changes: 39 additions & 1 deletion TLM/TLM/Util/LoopUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static IEnumerable<Vector2> GenerateSpiralGridCoordsClockwise() {
var cursorX = 0;
var cursorY = 0;

var directionX = 1;
var directionX = -1;
var directionY = 0;

var segmentLength = 1;
Expand All @@ -30,6 +30,44 @@ public static IEnumerable<Vector2> 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++;
}
}
}

/// <summary>
/// Generates a stream of spiral grid coordinates counterclockwise.
/// </summary>
/// <returns>Stream of spiral grid coordinates until int.MaxValue + 1 elements were returned.</returns>
public static IEnumerable<Vector2> 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;
Expand Down
80 changes: 67 additions & 13 deletions TLM/TLM/Util/Spiral.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using UnityEngine;

namespace TrafficManager.Util {
using ColossalFramework.Math;

/// <summary>
/// A spiral coords generator that will cache all previously generated values,
/// and only generate new ones if necessary.
Expand All @@ -12,9 +14,12 @@ public class Spiral {
private readonly string radiusOutOfRangeMessage = "Must be in the range [1,int.MaxValue]";

private int _maxRadius;
private IEnumerator<Vector2> _enumerator;
private List<Vector2> _spiralCoords;
private ReadOnlyCollection<Vector2> _spiralCoordsReadOnly;
private IEnumerator<Vector2> _enumeratorClockwise;
private IEnumerator<Vector2> _enumeratorCounterClockwise;
private List<Vector2> _spiralCoordsClockwise;
private List<Vector2> _spiralCoordsCounterclockwise;
private ReadOnlyCollection<Vector2> _spiralCoordsClockwiseReadOnly;
private ReadOnlyCollection<Vector2> _spiralCoordsCounterclockwiseReadOnly;

/// <summary>
/// Initializes a new instance of the <see cref="Spiral"/> class.
Expand All @@ -38,10 +43,14 @@ public Spiral(int radius) {
}

_maxRadius = 0;
_enumerator = LoopUtil.GenerateSpiralGridCoordsClockwise()
_enumeratorClockwise = LoopUtil.GenerateSpiralGridCoordsClockwise()
.GetEnumerator();
_enumeratorCounterClockwise = LoopUtil.GenerateSpiralGridCoordsCounterclockwise()
.GetEnumerator();
_spiralCoords = new List<Vector2>(radius * radius);
_spiralCoordsReadOnly = new ReadOnlyCollection<Vector2>(_spiralCoords);
_spiralCoordsClockwise = new List<Vector2>(radius * radius);
_spiralCoordsCounterclockwise = new List<Vector2>(radius * radius);
_spiralCoordsClockwiseReadOnly = new ReadOnlyCollection<Vector2>(_spiralCoordsCounterclockwise);
_spiralCoordsCounterclockwiseReadOnly = new ReadOnlyCollection<Vector2>(_spiralCoordsCounterclockwise);

Ensure(radius);
}
Expand All @@ -52,7 +61,43 @@ public Spiral(int radius) {
public int MaxRadius => _maxRadius;

/// <summary>
/// Gets spiral coords and ensures that the given radius is satisfied.
/// Gets spiral in random direction and ensired that the giver radius is satisfied
/// </summary>
/// <param name="radius">Needed radius for the spiral coords.</param>
/// <param name="r">Randomizer instance to perform randomization</param>
/// <returns>
/// 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.
/// </returns>
public ReadOnlyCollection<Vector2> GetCoordsRandomDirection(int radius, ref Randomizer r) {
return r.Int32(2) == 0 ? GetCoordsCounterclockwise(radius) : GetCoordsClockwise(radius);
}

/// <summary>
/// Gets spiral coords clockwise and ensures that the given radius is satisfied.
/// </summary>
/// <param name="radius">Needed radius for the spiral coords.</param>
/// <returns>
/// 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.
/// </returns>
public ReadOnlyCollection<Vector2> GetCoordsClockwise(int radius) {
if (radius < 1) {
throw new ArgumentOutOfRangeException(nameof(radius), radiusOutOfRangeMessage);
}

Ensure(radius);
return _spiralCoordsClockwiseReadOnly;
}

/// <summary>
/// Gets spiral coords counterclockwise and ensures that the given radius is satisfied.
/// </summary>
/// <param name="radius">Needed radius for the spiral coords.</param>
/// <returns>
Expand All @@ -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.
/// </returns>
public ReadOnlyCollection<Vector2> GetCoords(int radius) {
public ReadOnlyCollection<Vector2> GetCoordsCounterclockwise(int radius) {
if (radius < 1) {
throw new ArgumentOutOfRangeException(nameof(radius), radiusOutOfRangeMessage);
}

Ensure(radius);
return _spiralCoordsReadOnly;
return _spiralCoordsCounterclockwiseReadOnly;
}

private void Ensure(int radius) {
Expand All @@ -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);
}
}

Expand Down
Loading