private async Task <IEnumerable <byte[]> > GetUniqueNodeCountsAsync(Shape shape) { const int ThreadCount = 1; var tableau = shape.CreateTableau(); var terminalNodePairs = tableau.TerminalNodeUniqueCombinations() .GroupBy(nodePair => (TerminalNode)nodePair.Node1) .Select(grp => ( startNode: grp.Key, endNodes: grp.Select(node => (TerminalNode)node.Node2).ToList())) .ToList(); var jobSpecs = terminalNodePairs.Select(nodePair => new PathFinderJobSpec { Tableau = tableau, Name = nodePair.startNode.Index.ToString(), StartPoint = nodePair.startNode, EndPoints = nodePair.endNodes.ToList(), ThreadCount = ThreadCount }).ToList(); var combinedNodeCounts = new Dictionary <byte[], int>(new ByteSequenceEqualEqualityComparer()); var tokenSource = new CancellationTokenSource(); var tasks = new List <Task <(IDictionary <byte[], int>, PathFinderState)> >(); foreach (var jobSpec in jobSpecs) { var pathFinderJob = new PathFinderJob(jobSpec); var pathFinderState = new PathFinderState { Name = jobSpec.Name, Steps = jobSpec.StartPoint.Links.Select(link => new Step(link.Value, link.Key)).ToList(), Progress = new Progress() }; tasks.Add(pathFinderJob.ExploreAsync(pathFinderState, tokenSource.Token)); } await Task.WhenAll(tasks); foreach (var task in tasks) { var(nodeCounts, pathFinderState) = task.Result; foreach (var nodeCount in nodeCounts) { if (!combinedNodeCounts.TryAdd(nodeCount.Key, nodeCount.Value)) { combinedNodeCounts[nodeCount.Key] += nodeCount.Value; } } } return(combinedNodeCounts .Where(kv => kv.Value == 1) .Select(kv => kv.Key)); }
//TODO: Make this a Job private void Awake() { CurrentState = PathFinderState.StateIdle; PathNodeOpen = new List <PathNode>(); PathNodeClosed = new List <PathNode>(); PathNodeFinal = new List <PathNode>(); AdjacentNodes = new List <Tile>(); }
public static async Task Main(string[] args) { Trace.Listeners.Add(new TextWriterTraceListener(Console.Out)); if (!Directory.Exists(Configuration.FolderName)) { Directory.CreateDirectory(Configuration.FolderName); } var shape = GetShape(args); var tableau = shape.CreateTableau(); var concurrency = GetConcurrency(args); var persistInterval = GetPersistInterval(args); var terminalNodePairs = tableau.TerminalNodeUniqueCombinations() .GroupBy(nodePair => (TerminalNode)nodePair.Node1) .Select(grp => ( startNode: grp.Key, endNodes: grp.Select(node => (TerminalNode)node.Node2).ToList())) .ToList(); foreach (var(startNode, endNodes) in terminalNodePairs) { Trace.WriteLine($"{startNode.Index}: {string.Join(", ", endNodes.Select(node => node.Index))}"); } var jobs = new List <(PathFinderJob, PathFinderState)>(); foreach (var(startNode, endNodes) in terminalNodePairs) { var jobAdded = false; var jobSpec = new PathFinderJobSpec { Name = startNode.Index.ToString(), Tableau = tableau, StartPoint = startNode, EndPoints = endNodes, ThreadCount = concurrency, MonitorInterval = persistInterval }; if (persistInterval > TimeSpan.Zero) { var saveFilePath = Configuration.Filename(shape, jobSpec.Name); Trace.WriteLine($"Save file path: {saveFilePath}"); if (File.Exists(saveFilePath)) { var savedState = RouteStateReaderWriter.ReadFromFile(saveFilePath); if (savedState.Shape != shape.Name) { throw new Exception($"The shape {savedState.Shape} specfied in file '{saveFilePath}' does not match the expected value {shape.Name}."); } if (savedState.Size != shape.Size) { throw new Exception($"The size {savedState.Size} specfied in file '{saveFilePath}' does not match the expected value {shape.Size}."); } var endPoints = savedState.TerminalNodes .Select(index => tableau.TerminalNodes[index]) .ToList(); var steps = new List <Step>(); var queue = new Queue <(Step step, int id)>(); var items = savedState.Steps.Where(x => x.PreviousId == 0).ToList(); foreach (var item in items) { var step = new Step( node: tableau.Nodes[item.Position], direction: new Direction(item.Direction)); queue.Enqueue((step, item.Id)); } while (queue.Any()) { var(previousStep, id) = queue.Dequeue(); items = savedState.Steps.Where(x => x.PreviousId == id).ToList(); if (items.Count == 0) { steps.Add(previousStep); continue; } foreach (var item in items) { var direction = new Direction(item.Direction); var twist = direction - previousStep.Direction; var step = new Step( node: tableau.Nodes[item.Position], direction: direction, twist: twist, previous: previousStep); queue.Enqueue((step, item.Id)); } } var pathFinderJob = new PathFinderJob(jobSpec); //TODO: paths var pathFinderState = new PathFinderState { Name = jobSpec.Name, Steps = steps, Progress = savedState.Progress }; jobs.Add((pathFinderJob, pathFinderState)); jobAdded = true; } } if (!jobAdded) { var pathFinderJob = new PathFinderJob(jobSpec); var pathFinderState = new PathFinderState { Name = jobSpec.Name, Steps = startNode.Links.Select(link => new Step(link.Value, link.Key)).ToList(), Progress = new Progress() }; jobs.Add((pathFinderJob, pathFinderState)); } } var tokenSource = new CancellationTokenSource(); var tasks = new List <Task <(IDictionary <byte[], int>, PathFinderState)> >(); foreach (var(pathFinderJob, pathFinderState) in jobs) { tasks.Add(pathFinderJob.ExploreAsync(pathFinderState, tokenSource.Token)); } await Task.WhenAll(tasks); var combinedElapsedTime = TimeSpan.Zero; var combinedRouteCount = 0L; var combinedNodeCounts = new Dictionary <byte[], int>(new ByteSequenceEqualEqualityComparer()); foreach (var task in tasks) { var(nodeCounts, pathFinderState) = task.Result; foreach (var nodeCount in nodeCounts) { if (!combinedNodeCounts.ContainsKey(nodeCount.Key)) { combinedNodeCounts.Add(nodeCount.Key, 0); } combinedNodeCounts[nodeCount.Key] += nodeCount.Value; } var uniqueSolutionCount = nodeCounts.Count(item => item.Value == 1); combinedRouteCount += pathFinderState.Progress.RouteCount; if (pathFinderState.Progress.ElapsedTime > combinedElapsedTime) { combinedElapsedTime = pathFinderState.Progress.ElapsedTime; } Trace.WriteLine($"Result for job {pathFinderState.Name}: routes = {pathFinderState.Progress.RouteCount}, distinct solutions = {nodeCounts.Count}, unique solutions = {uniqueSolutionCount}, elapsed time = {pathFinderState.Progress.ElapsedTime}"); } var combinedUniqueSolutionCount = combinedNodeCounts.Count(item => item.Value == 1); Trace.WriteLine($"Overall result: routes = {combinedRouteCount}, distinct solutions = {combinedNodeCounts.Count}, unique solutions = {combinedUniqueSolutionCount}, elapsed time = {combinedElapsedTime}"); }
public async Task <(IDictionary <byte[], int> nodeCounts, PathFinderState state)> ExploreAsync( PathFinderState state, CancellationToken cancellationToken) { var routeCount = state.Progress.RouteCount; var stopwatch = Stopwatch.StartNew(); var routes = new BlockingCollection <Step>(new ConcurrentStack <Step>()); foreach (var step in state.Steps) { routes.Add(step); } var latch = new ManualResetEventSlim(initialState: true); void StartWalkAction() => Interlocked.Add(ref routeCount, StartWalk(routes, latch)); while (routes.Any()) { var tasks = Enumerable.Range(0, _threadCount) .Select(index => Task.Run(StartWalkAction, cancellationToken)) .ToList(); if (_monitorInterval > TimeSpan.Zero) { var persistTask = Task.Delay(_monitorInterval, cancellationToken) .ContinueWith(_ => latch.Reset(), TaskContinuationOptions.ExecuteSynchronously); tasks.Add(persistTask); } await Task.WhenAll(tasks); if (_monitorInterval > TimeSpan.Zero) { //TODO: export paths var routeLog = RouteLogFactory.CreateRouteLog( _tableau, _endPoints, routes.ToArray(), new Progress { RouteCount = routeCount, ElapsedTime = state.Progress.ElapsedTime + stopwatch.Elapsed }); RouteStateReaderWriter.WriteToFile(routeLog, _saveFilePath); } latch.Set(); } stopwatch.Stop(); return( nodeCounts : _nodeCounts, state : state with { Steps = routes.ToList(), Progress = new Progress { RouteCount = routeCount, ElapsedTime = state.Progress.ElapsedTime + stopwatch.Elapsed } });
public List <PathNode> FindPath(Tile startingTile, Tile destinationTile)//TODO: Refactor? { Reset(); var startingIndex = GetTileIndex(startingTile); var destinationIndex = GetTileIndex(destinationTile); if (startingIndex == destinationIndex) { return(null); } CurrentState = PathFinderState.StateSearchingPath; var hScore = GetManhattanDistanceCost(startingTile, destinationTile); var startingNode = startingTile.GetPathNode(); startingNode.SetScore(hScore); AddPathNodeToOpenList(startingNode); while (IsSearchingForPath()) { if (PathNodeOpen.Count == 0) { CurrentState = PathFinderState.StateError; return(null); } var currentNode = PathNodeOpen[0]; MoveNodeToClosedList(currentNode); if (GetTileIndex(currentNode.GetTile()) == destinationIndex) { BuildFinalNodePath(currentNode); CurrentState = PathFinderState.StateFoundPath; return(PathNodeFinal); } FindAjacentTiles(currentNode.GetTile()); for (int i = 0; i < AdjacentNodes.Count; i++) { var tile = AdjacentNodes[i]; if (tile == null) { continue; } if (DoesTileExistInClosedList(tile)) { continue; } if (!DoesTileExistInOpenList(tile)) { hScore = GetManhattanDistanceCost(tile, destinationTile); var node = tile.GetPathNode(); node.SetParent(currentNode); node.SetScore(hScore); AddPathNodeToOpenList(node); } else { var node = GetOpenPathNodeForTile(tile); if (currentNode.GetGScore() + 1 < node.GetGScore()) { node.SetParent(currentNode); SortOpenList(); } } } } return(null); }
public void Reset() { CurrentState = PathFinderState.StateIdle; ClearPathNodes(); }