public int SolverRun(BatchArgs batchArgs, SolverRun run)
        {
            var args =
                new FluentString(" ")
                .Append(batchArgs.Puzzle).Sep()
                .Append($"--solver {batchArgs.Solver}").Sep()
                .Append($"--pool {batchArgs.Pool}").Sep()
                .If(batchArgs.Min > 0, $"--min {batchArgs.Min}").Sep()
                .If(batchArgs.Sec > 0, $"--sec {batchArgs.Sec}").Sep()
                .If(batchArgs.MinR > 0, $"--min-rating {batchArgs.MinR}").Sep()
                .If(batchArgs.MaxR < 2000, $"--min-rating {batchArgs.MaxR}");

            batchArgs.Console.WriteLine($"Arguments: {args}");

            var           exitRequested = false;
            SolverCommand?executing     = null;

            // Setup: Report and cancellation
            var benchId   = DateTime.Now.ToString("s").Replace(':', '-');
            var outFile   = $"./benchmark--{benchId}.txt";
            var outTele   = $"./telemetry--{benchId}.csv";
            var outFolder = "./results/";

            if (!Directory.Exists(outFolder))
            {
                Directory.CreateDirectory(outFolder);
            }
            var info = new FileInfo(System.IO.Path.Combine(outFolder, outFile));
            var tele = new FileInfo(System.IO.Path.Combine(outFolder, outTele));


            using var report  = File.CreateText(info.FullName);
            using var repTele = File.CreateText(tele.FullName);

            System.Console.CancelKeyPress += (o, e) =>
            {
                report.Flush();
                batchArgs.Console.WriteLine("Ctrl+C detected; cancel requested");

                if (executing != null)
                {
                    executing.ExitConditions.ExitRequested = true;
                }
                exitRequested = true;
            };

            ISokobanSolutionRepository?solutionRepo = File.Exists("./solutions.json") && !DevHelper.IsDebug()
                ? new JsonSokobanSolutionRepository("./solutions.json")
                : null;
            ISolverRunTracking?runTracking = null;

            var results    = new List <(Strategy, List <SolverResultSummary>)>();
            var perm       = GetPermutations(batchArgs.Solver, batchArgs.Pool).ToList();
            var countStrat = 0;

            foreach (var strat in perm)
            {
                countStrat++;
                batchArgs.Console.WriteLine($"(Strategy {countStrat}/{perm.Count}) {strat}");

                var ioc = new SolverContainerByType(new Dictionary <Type, Func <Type, object> >()
                {
                    { typeof(ISolverPool), _ => PoolFactory(strat.Pool) },
                    { typeof(ISolverQueue), _ => new SolverQueueConcurrent() },
                    { typeof(ISolverRunTracking), _ => runTracking },
                    { typeof(ISokobanSolutionRepository), _ => solutionRepo },
                });
                var solverCommand = new SolverCommand
                {
                    ServiceProvider = ioc,
                    ExitConditions  = new ExitConditions()
                    {
                        Duration       = TimeSpan.FromMinutes(batchArgs.Min).Add(TimeSpan.FromSeconds(batchArgs.Sec)),
                        MemAvail       = DevHelper.GiB_1 / 2, // Stops the machine hanging / swapping to death
                        StopOnSolution = true,
                    },
                    AggProgress = new ConsoleProgressNotifier(repTele),
                    CheckAbort  = x => exitRequested,
                };


                var runner = new SingleSolverBatchSolveComponent(
                    new TextWriterAdapter(report),
                    batchArgs.Console,
                    solutionRepo,
                    runTracking,
                    5,
                    false);

                var solverInstance = SolverFactory(strat.Solver, ioc);
                var summary        = runner.Run(run, solverCommand, solverInstance, false, batchArgs);
                results.Add((strat, summary));
            }

            // Header
            var extras = new Dictionary <string, string>()
            {
                { "Args", args },
                { "Report", info.FullName }
            };

            DevHelper.WriteFullDevelopmentContext(report, extras);
            DevHelper.WriteFullDevelopmentContext(System.Console.Out, extras);

            // Body
            var reportRow = GenerateReport(results).ToList();

            MapToReporting.Create <SummaryLine>()
            .AddColumn("Solver", x => x.Strategy.Solver)
            .AddColumn("Pool", x => x.Strategy.Pool)
            .AddColumn("Puzzle", x => x.Result.Puzzle.Ident)
            .AddColumn("State", x => x.Result.Exited)
            .AddColumn("Solutions", x => (x.Result.Solutions?.Count ?? 0) == 0 ? null : (int?)x.Result.Solutions.Count)
            .AddColumn("Statistics", x =>
                       x.Result.Exited == ExitConditions.Conditions.Error
                                  ? x.Result.Exited.ToString()
                                  : x.Result.Statistics?.ToString(false, true)
                       )
            .RenderTo(reportRow, new MapToReportingRendererText(), report)
            .RenderTo(reportRow, new MapToReportingRendererText(), System.Console.Out);

            return(results.Any(x => x.Item2.Any(y => y.Exited == ExitConditions.Conditions.Error)) ? -1 : 0); // All exceptions
        }
Exemple #2
0
        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);
        }