Created
April 30, 2019 06:44
-
-
Save mirasrael/3417f49c227567d4790a8ef477de719f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Strange Loop Games. All rights reserved. | |
// See LICENSE file in the project root for full license information. | |
namespace Eco.Simulation.RouteProbing | |
{ | |
using System.Collections; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using Eco.Shared.Math; | |
using Eco.Shared.Serialization; | |
[Serialized, ThreadSafe] | |
public class RouteCache<T> : IEnumerable<KeyValuePair<WorldPosition3i, T>> | |
{ | |
protected ConcurrentDictionary<WorldPosition3i, T> grid = new ConcurrentDictionary<WorldPosition3i, T>(); | |
public int Count => this.grid.Count; | |
public IEnumerable<WorldPosition3i> Keys => this.grid.Keys; | |
public T this[WorldPosition3i pos] | |
{ | |
set { this.grid[pos] = value; } | |
} | |
public bool IsWalkable(WorldPosition3i pos) | |
{ | |
return this.grid.ContainsKey(pos); | |
} | |
public void Add(WorldPosition3i key, T value) => this.grid.TryAdd(key, value); | |
private static readonly Vector3i[] Links = { Vector3i.Left, Vector3i.Right, Vector3i.Back, Vector3i.Forward }; | |
// Note: Use RouteCacheData.Neighbors instead | |
// Works pretty well but occasionally lets animals path through vertical gaps that are too small | |
public WorldPosition3i[] Neighbors(WorldPosition3i pos) | |
{ | |
var arr = pos.XZNeighbors(); | |
for (int i = 0; i < arr.Length; i++) | |
arr[i] = this.GetClosestWalkable(arr[i]); | |
return arr; | |
} | |
public WorldPosition3i GetClosestWalkable(WorldPosition3i pos, float searchRange = 2) | |
{ | |
if (this.grid.ContainsKey(pos)) | |
return pos; | |
for (int i = 1; i <= searchRange; i++) | |
{ | |
if (this.grid.ContainsKey(pos.AddY(i))) | |
return pos.AddY(i); | |
if (this.grid.ContainsKey(pos.AddY(-i))) | |
return pos.AddY(-i); | |
} | |
return WorldPosition3i.Invalid; | |
} | |
// should cache Y values by column if this gets called often with high search range | |
public List<KeyValuePair<WorldPosition3i, T>> GetAllWalkablesInColumn(WorldPosition3i pos, float searchRange = 2) | |
{ | |
List<KeyValuePair<WorldPosition3i, T>> result = new List<KeyValuePair<WorldPosition3i, T>>(); | |
var p = pos; | |
if (this.grid.TryGetValue(p, out T value)) | |
result.Add(new KeyValuePair<WorldPosition3i, T>(p, value)); | |
for (int i = 1; i <= searchRange; i++) | |
{ | |
p = pos.AddY(i); | |
if (this.grid.TryGetValue(p, out value)) | |
result.Add(new KeyValuePair<WorldPosition3i, T>(p, value)); | |
p = pos.AddY(-i); | |
if (this.grid.TryGetValue(p, out value)) | |
result.Add(new KeyValuePair<WorldPosition3i, T>(p, value)); | |
} | |
return result; | |
} | |
public bool TryRemove(WorldPosition3i pos) | |
{ | |
if (this.grid.ContainsKey(pos)) | |
{ | |
this.grid.TryRemove(pos, out _); | |
return true; | |
} | |
return false; | |
} | |
public bool TryGetWalkable(WorldPosition3i pos, out T value) => this.grid.TryGetValue(pos, out value); | |
public IEnumerator<KeyValuePair<WorldPosition3i, T>> GetEnumerator() => ((IEnumerable<KeyValuePair<WorldPosition3i, T>>)this.grid).GetEnumerator(); | |
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable<KeyValuePair<WorldPosition3i, T>>)this.grid).GetEnumerator(); | |
} | |
} | |
[TestMethod] | |
public void AStarDictionaryProfiling() | |
{ | |
TestUtilities.PrepareThreadForProfiling(); | |
Vector3i worldSize = new Vector3i(1000, 140, 1000); | |
WorldPosition3i.Initialize(worldSize); | |
Dictionary<Vector3i, int> testData; | |
using (var filestream = File.OpenRead(@"..\..\..\TestData\RouteCacheData.eco")) | |
{ | |
var serializer = new SimpleSerializer(); | |
testData = serializer.Deserialize<Dictionary<Vector3i, int>>(filestream); | |
} | |
RouteManager manager = new RouteManager(); | |
RouteCache routeCache = new RouteCache(); | |
manager.Grid = routeCache; | |
foreach (var kvp in testData) | |
routeCache[kvp.Key] = (RouteCacheData)kvp.Value; | |
WorldPosition3i start = new Vector3i(182, 70, 224); | |
WorldPosition3i end = new Vector3i(152, 70, 254); | |
var s = routeCache.GetClosestWalkable(start, 100); | |
while (!s.IsValid) | |
{ | |
start.x++; | |
s = routeCache.GetClosestWalkable(start, 100); | |
} | |
start = s; | |
end = routeCache.GetClosestWalkable(end, 100); | |
double time; | |
AStarSearch regular; | |
/*AStarSearchConcurrent concurrent; | |
AStarSearchThreadSafe threadSafe;*/ | |
var sum = 0.0; | |
for (int j = 0; j < 10; j++) | |
{ | |
time = Core.Plugins.TickTimeUtil.TimeSubprocess(() => | |
{ | |
for (int i = 0; i < 10; i++) | |
regular = new AStarSearch(start, end); | |
}); | |
Trace.WriteLine($"Dictionary {time, 20:n3}"); | |
sum += time; | |
/*time = Core.Plugins.TickTimeUtil.TimeSubprocess(() => | |
{ | |
for (int i = 0; i < 10; i++) | |
concurrent = new AStarSearchConcurrent(start, end); | |
}); | |
Trace.Write($" ConcurrentDictionary {time, 20:n3}"); | |
time = Core.Plugins.TickTimeUtil.TimeSubprocess(() => | |
{ | |
for (int i = 0; i < 10; i++) | |
threadSafe = new AStarSearchThreadSafe(start, end); | |
}); | |
Trace.WriteLine($" ThreadSafeDictionary {time, 20:n3}");*/ | |
} | |
Trace.WriteLine($"Average: {sum / 10}"); | |
}//*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment