private State FindNextRectangle(int regionNo, double initialTemp)
        {
            var state   = GenerateState();
            var fitness = Evaluate(state);

            ConvergenceStopper.Run(iter =>
            {
                double temp    = initialTemp / iter;
                var newState   = MutateState(state);
                var newFitness = Evaluate(newState);

                if (iter % 10000 == 0)
                {
                    Console.WriteLine($"Region {regionNo}, iteration {iter}:");
                    Console.WriteLine($"    state.Fill = {state.Fill}, fitness = {fitness}");
                    Console.WriteLine($"    state.Region = {state.Region}");
                }

                var proba = GetTransitionProba(fitness, newFitness, temp);
                if (rand.NextDouble() < proba)
                {
                    state   = newState;
                    fitness = newFitness;
                }

                return(fitness);
            }, 0.01, 10000);
            return(state);
        }
        public List <BuildingTask> Decompose()
        {
            Console.WriteLine("FigureDecomposer.Decompose() started");

            double initialTemp = EvaluateDifferenceFrom(curMatrix) / 10.0;
            var    tasks       = new List <BuildingTask>();

            ConvergenceStopper.Run(regionNo =>
            {
                var stopwatch = Stopwatch.StartNew();

                if (xorSums.TotalFulls <= 3)
                {
                    return(null);
                }
                Console.WriteLine($"Points to change: {xorSums.TotalFulls}\n");

                var state = FindNextRectangle(regionNo, initialTemp);
                if ((state.Region.Max - state.Region.Min).Clen <= 3)
                {
                    Console.WriteLine("Too small region, it will be skipped");
                    return(xorSums.TotalFulls);
                }

                Region leg = null;
                if (state.Fill && BreakesGrounding(state) && state.Region.Min.Y > 0)
                {
                    var direction = state.Region.Max - state.Region.Min;
                    if (true /*direction.X < 3 || direction.Z < 3*/)
                    {
                        Console.WriteLine("We don't build regions like vertical lines if they are not grounded");
                        return(xorSums.TotalFulls);
                    }

                    var legX = state.Region.Min.X + 1;
                    var legZ = state.Region.Min.Z + 1;
                    leg      = new Region(
                        new Vector(legX, 0, legZ),
                        new Vector(legX, state.Region.Min.Y - 1, legZ));
                    Console.WriteLine($"Building leg {leg} for region {state.Region}");

                    curMatrix.Fill(leg);
                    UpdateXorMatrix(leg);
                    if (leg.Volume >= 4)
                    {
                        tasks.Add(new BuildingTask(BuildingTaskType.GFill, leg));
                    }
                    else
                    {
                        tasks.AddRange(leg.AllPoints()
                                       .Select(p => new BuildingTask(BuildingTaskType.Fill, new Region(p))));
                    }
                }
                if (!state.Fill && BreakesGrounding(state))
                {
                    Console.WriteLine("Can't fix ungrounded GVoids");
                    return(xorSums.TotalFulls);
                }

                if (state.Fill)
                {
                    curMatrix.Fill(state.Region);
                }
                else
                {
                    curMatrix.Clear(state.Region);
                }
                UpdateXorMatrix(state.Region);
                var type = state.Fill ? BuildingTaskType.GFill : BuildingTaskType.GVoid;
                tasks.Add(new BuildingTask(type, state.Region));

                var minChanged = leg == null ? state.Region.Min : new Vector(0, 0, 0);
                curSums.Update(curMatrix, minChanged);
                xorSums.Update(xorMatrix, minChanged);
                curGroundChecker = new IsGroundedChecker(curMatrix);

                Console.WriteLine($"Elapsed {stopwatch.ElapsedMilliseconds} ms");
                return(xorSums.TotalFulls);
            }, 0.001, 50);

            Console.WriteLine($"Points to change: {xorSums.TotalFulls}");
            tasks.AddRange(CreatePointwiseTasks());

            Console.WriteLine("FigureDecomposer.Decompose() complete");
            return(tasks);
        }