Skip to content

Instantly share code, notes, and snippets.

@mirasrael
Created April 30, 2019 06:44
Show Gist options
  • Save mirasrael/3417f49c227567d4790a8ef477de719f to your computer and use it in GitHub Desktop.
Save mirasrael/3417f49c227567d4790a8ef477de719f to your computer and use it in GitHub Desktop.
// 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