public bool Evaluate( SolverState state, ISolverQueue queue, ISolverPool pool, ISolverPool solutionPool, SolverNode node) { if (node.HasChildren) { throw new InvalidOperationException(); } node.Status = SolverNodeStatus.Evaluting; var toEnqueue = new List <SolverNode>(); var solution = false; foreach (var move in node.MoveMap.TruePositions()) { foreach (var dir in VectorInt2.Directions) { var p = move; var pp = p + dir; var ppp = pp + dir; if (node.CrateMap[pp] && // crate to push state.StaticMaps.FloorMap[ppp] && !node.CrateMap[ppp] && // into free space? !state.StaticMaps.DeadMap[ppp]) // Valid Push { EvaluateValidPush(state, pool, solutionPool, node, pp, ppp, p, dir, toEnqueue, ref solution); } } } if (solution) { node.Status = SolverNodeStatus.SolutionPath; } else if (node.HasChildren) { node.Status = SolverNodeStatus.Evaluted; state.Statistics.TotalDead += node.CheckDead(); // Children may be evaluated as dead already } else { node.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; if (node.Parent != null) { state.Statistics.TotalDead += node.Parent.CheckDead(); } } queue.Enqueue(toEnqueue); pool.Add(toEnqueue); return(solution); }
private void GenerateReports(SolverState state, ISolver solver) { // var r = state.Command.Report; // if (r == null) return; // // var renderer = new MapToReportingRendererText(); // var finalStats = solver.Statistics; // if (finalStats != null) // { // r.WriteLine("### Statistics ###"); // // // MapToReporting.Create<SolverStatistics>() // .AddColumn("Name", x=>x.Name) // .AddColumn("Nodes", x=>x.TotalNodes) // .AddColumn("Avg. Speed", x=>x.NodesPerSec) // .AddColumn("Duration (sec)", x=>x.DurationInSec) // .AddColumn("Duplicates", x=>x.Duplicates < 0 ? null : (int?)x.Duplicates) // .AddColumn("Dead", x=>x.TotalDead < 0 ? null : (int?)x.TotalDead) // .AddColumn("Current Depth", x=>x.DepthCurrent < 0 ? null : (int?)x.DepthCurrent) // .RenderTo(finalStats, renderer, r); // } // // var repDepth = MapToReporting.Create<SolverHelper.DepthLineItem>() // .AddColumn("Depth", x => x.Depth) // .AddColumn("Total", x => x.Total) // .AddColumn("Growth Rate", x => x.GrowthRate) // .AddColumn("UnEval", x => x.UnEval) // .AddColumn("Complete", x => (x.Total - x.UnEval) *100 / x.Total, c=>c.ColumnInfo.AsPercentage()); // // if (state is MultiThreadedSolverState multi) // { // r.WriteLine("### Forward Tree ###"); // repDepth.RenderTo(SolverHelper.ReportDepth(multi.Root), renderer, r); // // r.WriteLine("### Reverse Tree ###"); // repDepth.RenderTo(SolverHelper.ReportDepth(multi.RootReverse), renderer, r); // } // else if (state is SingleThreadedForwardReverseSolver.State sts) // { // r.WriteLine("### Forward Tree ###"); // repDepth.RenderTo(SolverHelper.ReportDepth(sts.Forward?.Root), renderer, r); // // r.WriteLine("### Reverse Tree ###"); // repDepth.RenderTo(SolverHelper.ReportDepth(sts.Reverse?.Root), renderer, r); // } // else if (state is SolverBaseState sb) // { // r.WriteLine("### Forward Tree ###"); // repDepth.RenderTo(SolverHelper.ReportDepth(sb.Root), renderer, r); // } // else // { // // ? // } }
public override bool Evaluate(SolverState state, ISolverQueue queue, ISolverPool myPool, ISolverPool solutionPool, SolverNode node) { if (node.HasChildren) { throw new InvalidOperationException(); } node.Status = SolverNodeStatus.Evaluting; var solution = false; var toEnqueue = new List <SolverNode>(); // TODO: Could be reused var toPool = new List <SolverNode>(); // TODO: Could be reused foreach (var move in node.MoveMap.TruePositions()) { foreach (var dir in VectorInt2.Directions) { var p = move; var pc = p + dir; var pp = p - dir; if (node.CrateMap[pc] && // crate to push state.StaticMaps.FloorMap[pp] && !node.CrateMap[p] && !CheckDeadReverse(state, pp)) { EvaluateValidPull(state, myPool, solutionPool, node, pc, p, pp, toEnqueue, toPool, ref solution); } } } if (solution) { node.Status = SolverNodeStatus.SolutionPath; } else if (node.HasChildren) { node.Status = SolverNodeStatus.Evaluted; state.Statistics.TotalDead += node.CheckDead(); // Children may be evaluated as dead already } else { node.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; if (node.Parent != null) { state.Statistics.TotalDead += node.Parent.CheckDead(); } } queue.Enqueue(toEnqueue); myPool.Add(toEnqueue); return(solution); }
public static void GetSolutions(SolverState state, bool check) { var walls = state.Command.Puzzle.ToMap(state.Command.Puzzle.Definition.Wall); state.Solutions = new List <Path>(); if (state.SolutionsNodes != null) { state.Solutions.AddRange(state.SolutionsNodes.Select(x => ConvertSolutionNodeToPath(x, walls, state.Command.Puzzle))); } if (state.SolutionsNodesReverse != null) { foreach (var tuple in state.SolutionsNodesReverse) { var rev = ConvertReverseNodeToPath(tuple.ReverseNode, walls); var fwd = ConvertForwardNodeToPath(tuple.ForwardNode, walls); var bridge = PathFinder.Find(walls.BitwiseOR(tuple.ForwardNode.CrateMap), tuple.ForwardNode.PlayerAfter, tuple.ReverseNode.PlayerAfter); //Console.WriteLine("Forward Leg: {0}", fwd); //Console.WriteLine("Reverse Leg: {0}", rev); //Console.WriteLine("Bridge {0} => {1}", tuple.ForwardNode.ToStringDebugPositions(), // tuple.ReverseNode.ToStringDebugPositions()); var p = new Path(); p.AddRange(fwd); p.AddRange(bridge); p.AddRange(rev); state.Solutions.Add(p); } } if (check && state.Solutions.Any()) { int cc = 0; foreach (var pathSolution in state.Solutions.ToArray()) { cc++; if (!CheckSolution(state.Command.Puzzle, pathSolution, out var error)) { state.SolutionsInvalid ??= new List <(Path, string)>(); state.SolutionsInvalid.Add((pathSolution, error)); state.Solutions.Remove(pathSolution); if (state.Command.Report != null) { state.Command.Report.WriteLine($"Solution #{cc++} [{(check ? "Valid" : "INVALID!" + error)}] =>\n{pathSolution}"); } } } } }
public void Update(ISolver caller, SolverState state, SolverStatistics global, string txt) { var dt = DateTime.Now - last; if (dt.TotalSeconds < sampleRateInSec) { return; } last = DateTime.Now; UpdateInner(caller, state, global, txt); }
private bool CheckValidSolutions(SolverState state, SolverNode posibleSolution) { var b = state.StaticMaps.WallMap.BitwiseOR(state.StaticMaps.CrateStart); var f = state.Command.Puzzle.Player.Position; var path = posibleSolution.PathToRoot(); path.Reverse(); var start = path[0]; var t = start.PlayerAfter; var first = PathFinder.Find(b, f, t); return(first != null); }
protected override string Render(ISolver caller, SolverState state, SolverStatistics global, string txt) { if (global == null) { return(null); } var totalMemory = System.GC.GetTotalMemory(false); var delta = (prev != null) ? global.TotalNodes - prev.TotalNodes : global.TotalNodes; var sb = new FluentString() .Append(txt) .Sep() .Append($"delta:{delta:#,##0}") .Sep() .Append($"mem({Humanise.SizeSuffix((ulong) totalMemory)} used") .Block(b => { if (DevHelper.TryGetTotalMemory(out var avail)) { b.Sep(); b.Append($"{Humanise.SizeSuffix(avail)} avail"); } }) .Append(")"); var telText = new FluentString(",") .Append(global.DurationInSec.ToString()).Sep() .Append(global.TotalNodes.ToString()).Sep() .Append(global.NodesPerSec.ToString()).Sep() .Append(delta.ToString()).Sep() .Append(totalMemory.ToString()).Sep() ; tele.WriteLine(telText); prev = new SolverStatistics(global); return(sb); }
public static string GenerateSummary(SolverState state) { if (state == null) { throw new ArgumentNullException("state"); } var sb = new StringBuilder(); var nodePerSec = (double)state.Statistics.TotalNodes / state.Statistics.DurationInSec; sb.Append(state.Exit.ToString().PadRight(20)); if (state.Exception != null) { var ex = state.Exception is AggregateException agg ? agg.InnerExceptions.First() : state.Exception; if (ex is NullReferenceException) { sb.Append("[NULL] " + StringUtil.ToLines(ex.StackTrace).First().TrimStart('\t').Trim()); } else { sb.Append(StringUtil.Truncate(StringUtil.StripLineFeeds($"[{ex.GetType().Name}] {ex.Message}"), 180)); } return(sb.ToString()); } sb.Append($" {state.Statistics.TotalNodes,12:#,##0} nodes at {nodePerSec,6:#,##0}/s in {state.Statistics.Elapsed.Humanize()}."); if (state.HasSolution) { var d = state.SolutionsNodes != null ? state.SolutionsNodes.Count : 0; var r = state.SolutionsNodesReverse != null ? state.SolutionsNodesReverse.Count : 0; sb.AppendFormat(" {0} solutions.", d + r); } if (state.SolutionsInvalid != null && state.SolutionsInvalid.Count > 0) { sb.Append(" !INVALID SOLUTIONS!"); } return(sb.ToString()); }
private void GenerateReports(SolverState state, ISolver solver) { var r = state.Command.Report; if (r == null) { return; } var renderer = new MapToReportingRendererText(); var finalStats = solver.Statistics; if (finalStats != null) { r.WriteLine("### Statistics ###"); MapToReporting.Create <SolverStatistics>() .AddColumn("Name", x => x.Name) .AddColumn("Nodes", x => x.TotalNodes) .AddColumn("Avg. Speed", x => x.NodesPerSec) .AddColumn("Duration (sec)", x => x.DurationInSec) .AddColumn("Duplicates", x => x.Duplicates < 0 ? null : (int?)x.Duplicates) .AddColumn("Dead", x => x.TotalDead < 0 ? null : (int?)x.TotalDead) .AddColumn("Current Depth", x => x.DepthCurrent < 0 ? null : (int?)x.DepthCurrent) .RenderTo(finalStats, renderer, Report); } var repDepth = MapToReporting.Create <SolverHelper.DepthLineItem>() .AddColumn("Depth", x => x.Depth) .AddColumn("Total", x => x.Total) .AddColumn("UnEval", x => x.UnEval) .AddColumn("Complete", x => (x.Total - x.UnEval) * 100 / x.Total, c => c.ColumnInfo.AsPercentage()); if (state is MultiThreadedSolverState multi) { r.WriteLine("### Forward Tree ###"); repDepth.RenderTo(SolverHelper.ReportDepth(multi.Root), renderer, r); r.WriteLine("### Reverse Tree ###"); repDepth.RenderTo(SolverHelper.ReportDepth(multi.RootReverse), renderer, r); } }
private void NewSolutionChain(SolverState state, out bool solution, SolverNode newKid, SolverNode match) { solution = true; state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = newKid, ReverseNode = match, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; }
public IEnumerable <(string name, string text)> GetTypeDescriptorProps(SolverState state) =>
public virtual IEnumerable <(string name, string text)> GetTypeDescriptorProps(SolverState state) => ImmutableArray <(string name, string text)> .Empty;
public IEnumerable <(string name, string text)> GetTypeDescriptorProps(SolverState state) => throw new NotSupportedException();
public ExitConditions.Conditions Solve(SolverState state) { var full = (MultiThreadedSolverState)state; var allTasks = full.Workers.Select(x => (Task)x.Task).ToArray(); var cancel = state.Command.CancellationToken; // Start and wait full.IsRunning = true; foreach (var worker in full.Workers) { worker.Task.Start(); } Task statisticsTick = null; if (state.Command.AggProgress != null) { // Setup global/aggregate statistics and updates statisticsTick = Task.Run(() => { while (full.IsRunning) { Thread.Sleep(1000); state.Statistics.TotalNodes = current.PoolForward.Statistics.TotalNodes + current.PoolReverse.Statistics.TotalNodes; var txt = new FluentString() .Append($"==> {state.Statistics.ToString(false, true)}") .Append($" Fwd({current.PoolForward.Statistics.TotalNodes:#,##0}|{current.QueueForward.Statistics.TotalNodes:#,##0})") .Append($" Rev({current.PoolReverse.Statistics.TotalNodes:#,##0}|{current.QueueReverse.Statistics.TotalNodes:#,##0})"); state.Command.AggProgress.Update(this, state, state.Statistics, txt); } if (state.Command.AggProgress is IDisposable dp) { dp.Dispose(); } }); } if (!Task.WaitAll(allTasks, (int)state.Command.ExitConditions.Duration.TotalMilliseconds, cancel)) { // Close down the workers as gracefully as possible state.Command.ExitConditions.ExitRequested = true; // Allow them to process the ExitRequested Thread.Sleep((int)TimeSpan.FromSeconds(1).TotalMilliseconds); // Close down any outlyers foreach (var task in allTasks) { if (task.Status == TaskStatus.Running) { if (!task.Wait((int)TimeSpan.FromSeconds(1).TotalMilliseconds)) { task.Dispose(); } } } state.Exit = ExitConditions.Conditions.TimeOut; } full.IsRunning = false; statisticsTick?.Wait(); state.Statistics.Completed = DateTime.Now; foreach (var stat in current.StatsInner) { stat.Completed = state.Statistics.Completed; } // Get solutions & Exit Conditions & Errors var errors = full.Workers.Select(x => x.WorkerState.Exception).Where(x => x != null).ToList(); if (errors.Any()) { throw new AggregateException(errors); } foreach (var worker in full.Workers) { worker.WorkerState.Statistics.Completed = state.Statistics.Completed; // Bubble up exit to owner state.Command.Report?.WriteLine($"WorkerExit: {worker.Name} -> {worker.WorkerState.Exit}"); if (state.Exit == ExitConditions.Conditions.InProgress && (worker.WorkerState.Exit != ExitConditions.Conditions.InProgress && worker.WorkerState.Exit != ExitConditions.Conditions.Aborted)) { state.Exit = worker.WorkerState.Exit; } if (worker.WorkerState.HasSolution) { if (worker.WorkerState.SolutionsNodes != null) { full.SolutionsNodes ??= new List <SolverNode>(); full.SolutionsNodes.AddRange(worker.WorkerState.SolutionsNodes); state.Exit = ExitConditions.Conditions.Solution; } if (worker.WorkerState.SolutionsNodesReverse != null) { full.SolutionsNodesReverse ??= new List <SolutionChain>(); full.SolutionsNodesReverse.AddRange(worker.WorkerState.SolutionsNodesReverse); state.Exit = ExitConditions.Conditions.Solution; } } } // Update stats state.Statistics.TotalNodes = current.PoolForward.Statistics.TotalNodes + current.PoolReverse.Statistics.TotalNodes; SolverHelper.GetSolutions(state, true); if (state.Exit == ExitConditions.Conditions.Continue) { state.Exit = full.Workers.Select(x => x.WorkerState.Exit) .GroupBy(x => x) .OrderBy(x => x.Count()) .First().Key; } return(state.Exit); }
private int StoreAttempt(ISolver solver, LibraryPuzzle dto, SolverState state, string desc) { var best = state.Solutions?.OrderBy(x => x.Count).FirstOrDefault(); var sol = new SolutionDTO { IsAutomated = true, PuzzleIdent = dto.Ident.ToString(), Path = best?.ToString(), Created = DateTime.Now, Modified = DateTime.Now, MachineName = Environment.MachineName, MachineCPU = DevHelper.DescribeCPU(), SolverType = solver.GetType().Name, SolverVersionMajor = solver.VersionMajor, SolverVersionMinor = solver.VersionMinor, SolverDescription = solver.VersionDescription, TotalNodes = solver.Statistics.First().TotalNodes, TotalSecs = solver.Statistics.First().DurationInSec, Description = desc }; var exists = Repository.GetPuzzleSolutions(dto.Ident); if (exists != null && exists.Any()) { var onePerMachine = exists.FirstOrDefault(x => x.MachineName == sol.MachineName && x.SolverType == sol.SolverType); if (onePerMachine != null) { if (sol.HasSolution) { if (!onePerMachine.HasSolution) { sol.SolutionId = onePerMachine.SolutionId; // replace Repository.Store(sol); return(sol.SolutionId); } else if (sol.TotalNodes < onePerMachine.TotalSecs) { sol.SolutionId = onePerMachine.SolutionId; // replace Repository.Store(sol); return(sol.SolutionId); } else { // drop } } else { if (!onePerMachine.HasSolution && sol.TotalNodes > onePerMachine.TotalNodes) { sol.SolutionId = onePerMachine.SolutionId; // replace Repository.Store(sol); return(sol.SolutionId); } } } else { Repository.Store(sol); return(sol.SolutionId); } } else { Repository.Store(sol); return(sol.SolutionId); } return(-1); }
public abstract bool Evaluate(SolverState state, ISolverQueue queue, ISolverPool pool, ISolverPool solutionPool, SolverNode node);
protected abstract void UpdateInner(ISolver caller, SolverState state, SolverStatistics global, string txt);
private bool EvaluateValidPush( SolverState state, ISolverPool pool, ISolverPool reversePool, SolverNode node, VectorInt2 pp, VectorInt2 ppp, VectorInt2 p, VectorInt2 push, List <SolverNode> toEnqueue, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPush(node, node.CrateMap, state.StaticMaps.WallMap, p, pp, ppp, push); // Cycle Check: Does this node exist already? var dup = pool.FindMatch(newKid); if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); newKid.Duplicate = dup; } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { node.Add(newKid); // If there is a reverse solver, checks its pool for a match, hence a Forward <-> Reverse chain, hence a solution var match = reversePool?.FindMatch(newKid); if (match != null) { // Solution! NewSolutionChain(state, out solution, newKid, match); if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; state.Statistics.TotalDead++; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionForward(state.StaticMaps)) { // Solution solution = true; state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } } } } return(false); }
private bool EvaluateValidPull( SolverState state, ISolverPool myPool, ISolverPool solutionPool, SolverNode node, VectorInt2 pc, VectorInt2 p, VectorInt2 pp, List <SolverNode> toEnqueue, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPull(node, node.CrateMap, state.StaticMaps.WallMap, pc, p, pp); // Cycle Check: Does this node exist already? var dup = myPool.FindMatch(newKid); if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); newKid.Duplicate = dup; } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { var match = solutionPool?.FindMatch(newKid); if (match != null) { // Add to tree / itterator node.Add(newKid); // Solution state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = match, ReverseNode = newKid, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); solution = true; state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { // Add to tree / iterator node.Add(newKid); // Thread-safe: As all kids get created in this method (forward / reverse) if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionReverse(state.StaticMaps)) { // Possible Solution: Did we start in a valid position if (CheckValidSolutions(state, newKid)) { state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); solution = true; foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; } else { // We started in the wrong place; ignore and continue } } } } } return(false); }
private bool CheckDeadReverse(SolverState state, VectorInt2 ppp) { return(false); return(state.StaticMaps.DeadMap[ppp]); }
protected abstract string Render(ISolver caller, SolverState state, SolverStatistics global, string txt);
private bool EvaluateValidPull( SolverState state, ISolverPool pool, ISolverPool solutionPool, SolverNode node, VectorInt2 pc, VectorInt2 p, VectorInt2 pp, List <SolverNode> toEnqueue, List <SolverNode> toPool, ref bool solution) { state.Statistics.TotalNodes++; var newKid = nodeFactory.CreateFromPull(node, node.CrateMap, state.StaticMaps.WallMap, pc, p, pp); if (state.Command.Inspector != null && state.Command.Inspector(newKid)) { state.Command.Report?.WriteLine(newKid.ToString()); } // Cycle Check: Does this node exist already? var dup = pool.FindMatch(newKid); if (SafeMode && dup == null) { dup = ConfirmDupLookup(pool, node, toEnqueue, newKid); // Fix or Throw } if (dup != null) { if (object.ReferenceEquals(dup, newKid)) { throw new InvalidDataException(); } if (dup.SolverNodeId == newKid.SolverNodeId) { throw new InvalidDataException(); } // Duplicate newKid.Status = SolverNodeStatus.Duplicate; state.Statistics.Duplicates++; if (state.Command.DuplicateMode == DuplicateMode.AddAsChild) { node.Add(newKid); if (newKid is ISolverNodeDuplicateLink dupLink) { dupLink.Duplicate = dup; } } else if (state.Command.DuplicateMode == DuplicateMode.ReuseInPool) { nodeFactory.ReturnInstance(newKid); // Add to pool for later re-use? } else // DuplicateMode.Discard { } } else { // These two should always be the same node.Add(newKid); toPool.Add(newKid); // If there is a reverse solver, checks its pool for a match, hence a Forward <-> Reverse chain, hence a solution var match = solutionPool?.FindMatch(newKid); if (match != null) { // Solution state.SolutionsNodesReverse ??= new List <SolutionChain>(); var pair = new SolutionChain { ForwardNode = match, ReverseNode = newKid, FoundUsing = this }; state.SolutionsNodesReverse.Add(pair); solution = true; state.Command.Debug.Raise(this, SolverDebug.Solution, pair); foreach (var n in newKid.PathToRoot().Union(match.PathToRoot())) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; match.Status = SolverNodeStatus.Solution; if (state.Command.ExitConditions.StopOnSolution) { return(true); } } else { if (DeadMapAnalysis.DynamicCheck(state.StaticMaps, node)) { newKid.Status = SolverNodeStatus.Dead; } else { toEnqueue.Add(newKid); if (newKid.IsSolutionReverse(state.StaticMaps)) { // Possible Solution: Did we start in a valid position if (CheckValidSolutions(state, newKid)) { state.SolutionsNodes.Add(newKid); state.Command.Debug.Raise(this, SolverDebug.Solution, newKid); solution = true; foreach (var n in newKid.PathToRoot()) { n.Status = SolverNodeStatus.SolutionPath; } newKid.Status = SolverNodeStatus.Solution; } else { // We started in the wrong place; ignore and continue } } } } } return(false); }