Exemple #1
0
        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);
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        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);
        }
Exemple #7
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);
        }