private FluentString GetPropReport(ISolver solver, SolverState commandState) { Report.WriteLine("Solver: {0}", SolverHelper.Describe(solver)); var propsReport = new FluentString(); propsReport.Append(solver.TypeDescriptor); try { var typeDescriptorProps = solver.GetTypeDescriptorProps(commandState); if (typeDescriptorProps != null) { foreach (var(name, text) in typeDescriptorProps) { propsReport.AppendLine($"-> {name,20}: {text}"); Report.WriteLine($"-> {name,20}: {text}"); } } } catch (NotSupportedException) { var msg = $"Solver [{solver.GetType().Name}] does not support {typeof(IExtendedFunctionalityDescriptor).Name}"; Report.WriteLine(msg); propsReport.AppendLine(msg); } catch (NotImplementedException) { var msg = $"Solver [{solver.GetType().Name}] does not support {typeof(IExtendedFunctionalityDescriptor).Name}"; Report.WriteLine(msg); propsReport.AppendLine(msg); } return(propsReport); }
public SolverNode CreateFromPull( SolverNode parent, IBitmap nodeCrateMap, IBitmap walls, VectorInt2 pc, VectorInt2 p, VectorInt2 pp) { if (TryGetPooledInstance(out var fromPool)) { var eCrate = fromPool.CrateMap; var eMove = fromPool.MoveMap; eMove.Fill(false); // Reuse the nodes, resetting the values eCrate.Set(nodeCrateMap); eCrate[pc] = false; eCrate[p] = true; SolverHelper.FloodFillUsingWallAndCratesInline(walls, eCrate, pp, eMove); fromPool.InitialiseInstance(parent, p, pp - p, eCrate, eMove, true); return(fromPool); } var newCrate = CreateBitmap(nodeCrateMap); newCrate[pc] = false; newCrate[p] = true; var newMove = CreateBitmap(nodeCrateMap.Size); SolverHelper.FloodFillUsingWallAndCratesInline(walls, newCrate, pp, newMove); return(CreateInstance(parent, p, pp - p, newCrate, newMove)); }
public virtual SolverState Init(SolverCommand command) { var state = SolverHelper.Init(new SolverBaseState(), command); state.Statistics.Name = GetType().Name; state.Pool = new SolverPoolByBucket(); state.Evaluator = evaluator; state.Queue = new SolverQueue(); state.Root = state.Evaluator.Init(command.Puzzle, state.Queue); Statistics = new[] { state.Statistics, state.Pool.Statistics, state.Queue.Statistics }; return(state); }
public override SolverState Init(SolverCommand command) { var state = SolverHelper.Init(new MultiThreadedSolverState(), command); state.Command.Parent = Worker.Owner; state.Command.CheckAbort = CheckWorkerAbort; state.Statistics.Name = $"{GetType().Name}:{Worker.Name}"; state.Pool = Worker.Pool; state.Evaluator = new ReverseEvaluator(nodeFactory); state.Queue = Worker.Queue; Statistics = new[] { state.Statistics }; return(state); }
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); } }
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); }
public List <SolverResultSummary> Run( SolverRun run, SolverCommand baseCommand, ISolver solver, bool showSummary, BatchSolveComponent.BatchArgs?batchArgs = null) { if (run == null) { throw new ArgumentNullException(nameof(run)); } if (baseCommand == null) { throw new ArgumentNullException(nameof(baseCommand)); } if (solver == null) { throw new ArgumentNullException(nameof(solver), "See: " + nameof(SingleThreadedForwardSolver)); } Report.WriteLine("Puzzle Exit Conditions: {0}", run.PuzzleExit); Report.WriteLine("Batch Exit Conditions : {0}", run.BatchExit); Report.WriteLine("Environment : {0}", DevHelper.RuntimeEnvReport()); Report.WriteLine("Solver Environment : v{0} -- {1}", SolverHelper.VersionUniversal, SolverHelper.VersionUniversalText); Report.WriteLine("Started : {0}", DateTime.Now.ToString("u")); Report.WriteLine(); var res = new List <SolverResultSummary>(); var start = new SolverStatistics { Started = DateTime.Now }; SolverState?state = null; var pp = 0; var consecutiveFails = 0; foreach (var puzzle in run) { if (baseCommand.CheckAbort(baseCommand)) { Progress.WriteLine("EXITING..."); break; } try { pp++; Progress.WriteLine($"(Puzzle {pp}/{run.Count}) Attempting: {puzzle.Ident} \"{puzzle.Name}\", R={StaticAnalysis.CalculateRating(puzzle.Puzzle)}. Stopping on [{baseCommand.ExitConditions}] ..."); Report.WriteLine(" Name: {0}", puzzle); Report.WriteLine(" Ident: {0}", puzzle.Ident); Report.WriteLine(" Rating: {0}", StaticAnalysis.CalculateRating(puzzle.Puzzle)); Report.WriteLine(puzzle.Puzzle.ToString()); // Adds 2x line feeds IReadOnlyCollection <SolutionDTO> existingSolutions = null; if (SkipPuzzlesWithSolutions && Repository != null) { existingSolutions = Repository.GetPuzzleSolutions(puzzle.Ident); if (existingSolutions != null && existingSolutions.Any( x => x.MachineName == Environment.MachineName && x.SolverType == solver.GetType().Name)) { Progress.WriteLine("Skipping... (SkipPuzzlesWithSolutions)"); continue; } } // #### Main Block Start -------------------------------------- var memStart = GC.GetTotalMemory(false); var attemptTimer = new Stopwatch(); attemptTimer.Start(); state = solver.Init(new SolverCommand(baseCommand) { Report = Report, Puzzle = puzzle.Puzzle }); var propsReport = GetPropReport(solver, state); Tracking?.Begin(state); try { solver.Solve(state); } catch (Exception e) { state.Exception = e; state.Exit = ExitConditions.Conditions.Error; state.EarlyExit = true; } var memEnd = GC.GetTotalMemory(false); state.Statistics.MemUsed = memEnd; var memDelta = memEnd - memStart; var bytesPerNode = memDelta / state.Statistics.TotalNodes; var maxNodes = (ulong)0; if (DevHelper.TryGetTotalMemory(out var totalMem)) { maxNodes = totalMem / (ulong)bytesPerNode; } Report.WriteLine($"Memory Used: {Humanise.SizeSuffix(memEnd)}, delta: {Humanise.SizeSuffix(memDelta)} ~ {bytesPerNode:#,##0} bytes/node => max nodes:{maxNodes:#,##0}"); attemptTimer.Stop(); // #### Main Block End ------------------------------------------ state.Summary = new SolverResultSummary( puzzle, state.Solutions, state.Exit, SolverHelper.GenerateSummary(state), attemptTimer.Elapsed, state.Statistics ); res.Add(state.Summary); start.TotalNodes += state.Statistics.TotalNodes; start.TotalDead += state.Statistics.TotalDead; Report.WriteLine("[DONE] {0}", state.Summary.Text); Progress.WriteLine($" -> {state.Summary.Text}"); if (batchArgs != null && batchArgs.Save != null) { Console.WriteLine(" -> Saving..."); var binSer = new BinaryNodeSerializer(); var rootForward = state.GetRootForward(); if (rootForward != null) { var outState = System.IO.Path.Combine(batchArgs.Save, $"{puzzle.Ident}-forward.ssbn"); using (var f = File.Create(outState)) { using (var bw = new BinaryWriter(f)) { binSer.WriteTree(bw, rootForward); } } Report.WriteLine($"\tSaving State: {outState}"); Progress.WriteLine($"\tSaving State: {outState}"); } var rootReverse = state.GetRootReverse(); if (rootReverse != null) { var outState = System.IO.Path.Combine(batchArgs.Save, $"{puzzle.Ident}-reverse.ssbn"); using (var f = File.Create(outState)) { using (var bw = new BinaryWriter(f)) { binSer.WriteTree(bw, rootReverse); } } Report.WriteLine($"\tSaving State: {outState}"); Progress.WriteLine($"\tSaving State: {outState}"); } } // Add Depth Reporting Console.WriteLine(" -> Generating Reports..."); GenerateReports(state, solver); if (Repository != null) { var id = StoreAttempt(solver, puzzle, state, propsReport.ToString()); if (id >= 0) { var solTxt = $"Checking against known solutions. SolutionId={id}"; Report.WriteLine(solTxt); Console.WriteLine(solTxt); } } else { Report.WriteLine($"Solution Repository not available: Skipping."); } if (state?.Summary?.Solutions != null && state.Summary.Solutions.Any()) // May have been removed above { consecutiveFails = 0; } else { consecutiveFails++; if (StopOnConsecutiveFails != 0 && consecutiveFails > StopOnConsecutiveFails) { Progress.WriteLine("ABORTING... StopOnConsecutiveFails"); break; } } Tracking?.End(state); if (state.Exception != null) { Report.WriteLine("[EXCEPTION]"); WriteException(Report, state.Exception); } if (state.Exit == ExitConditions.Conditions.Aborted) { Progress.WriteLine("ABORTING..."); if (showSummary) { WriteSummary(res, start); } return(res); } if (start.DurationInSec > run.BatchExit.Duration.TotalSeconds) { Progress.WriteLine("BATCH TIMEOUT..."); if (showSummary) { WriteSummary(res, start); } return(res); } Progress.WriteLine(); } catch (Exception ex) { if (state != null) { state.Exception = ex; } Progress.WriteLine("ERROR: " + ex.Message); WriteException(Report, ex); } finally { state = null; if (puzzle != run.Last()) { GC.Collect(); } } } if (showSummary) { WriteSummary(res, start); } Report.WriteLine("Completed : {0}", DateTime.Now.ToString("u")); return(res); }