예제 #1
0
 public abstract Task <string> Solve(ProjectPage page);
예제 #2
0
        public override async Task <string> Solve(ProjectPage page)
        {
            var processedGoods   = Database.goods.CreateMapping <Constraint>();
            var processedRecipes = Database.recipes.CreateMapping <Variable>();
            var processingStack  = new Queue <Goods>();
            var solver           = DataUtils.CreateSolver("BestFlowSolver");
            var rootConstraint   = solver.MakeConstraint();

            foreach (var root in roots)
            {
                processedGoods[root] = rootConstraint;
            }
            foreach (var goal in goals)
            {
                processedGoods[goal.item] = solver.MakeConstraint(goal.amount, double.PositiveInfinity, goal.item.name);
                processingStack.Enqueue(goal.item);
            }

            await Ui.ExitMainThread();

            var objective = solver.Objective();

            objective.SetMinimization();
            processingStack.Enqueue(null); // depth marker;
            var depth = 0;

            var allRecipes = new List <Recipe>();

            while (processingStack.Count > 1)
            {
                var item = processingStack.Dequeue();
                if (item == null)
                {
                    processingStack.Enqueue(null);
                    depth++;
                    continue;
                }

                var constraint = processedGoods[item];
                foreach (var recipe in item.production)
                {
                    if (!recipe.IsAccessibleWithCurrentMilestones())
                    {
                        continue;
                    }
                    if (processedRecipes[recipe] is Variable var)
                    {
                        constraint.SetCoefficient(var, constraint.GetCoefficient(var) + recipe.GetProduction(item));
                    }
                    else
                    {
                        allRecipes.Add(recipe);
                        var = solver.MakeNumVar(0, double.PositiveInfinity, recipe.name);
                        objective.SetCoefficient(var, recipe.RecipeBaseCost() * (1 + depth * 0.5));
                        processedRecipes[recipe] = var;

                        foreach (var product in recipe.products)
                        {
                            if (processedGoods[product.goods] is Constraint constr && !processingStack.Contains(product.goods))
                            {
                                constr.SetCoefficient(var, constr.GetCoefficient(var) + product.amount);
                            }
                        }

                        foreach (var ingredient in recipe.ingredients)
                        {
                            var proc = processedGoods[ingredient.goods];
                            if (proc == rootConstraint)
                            {
                                continue;
                            }
                            if (processedGoods[ingredient.goods] is Constraint constr)
                            {
                                constr.SetCoefficient(var, constr.GetCoefficient(var) - ingredient.amount);
                            }
                            else
                            {
                                constr = solver.MakeConstraint(0, double.PositiveInfinity, ingredient.goods.name);
                                processedGoods[ingredient.goods] = constr;
                                processingStack.Enqueue(ingredient.goods);
                                constr.SetCoefficient(var, -ingredient.amount);
                            }
                        }
                    }
                }
            }

            var solverResult = solver.Solve();

            Console.WriteLine("Solution completed with result " + solverResult);
            if (solverResult != Solver.ResultStatus.OPTIMAL && solverResult != Solver.ResultStatus.FEASIBLE)
            {
                Console.WriteLine(solver.ExportModelAsLpFormat(false));
                this.tiers = null;
                return("Model have no solution");
            }

            var graph = new Graph <Recipe>();

            allRecipes.RemoveAll(x =>
            {
                if (!(processedRecipes[x] is Variable variable))
                {
                    return(true);
                }
                if (variable.BasisStatus() != Solver.BasisStatus.BASIC || variable.SolutionValue() <= 1e-6d)
                {
                    processedRecipes[x] = null;
                    return(true);
                }
                return(false);
            });

            foreach (var recipe in allRecipes)
            {
                foreach (var ingredient in recipe.ingredients)
                {
                    foreach (var productionRecipe in ingredient.goods.production)
                    {
                        if (processedRecipes[productionRecipe] != null)
                        {
                            // TODO think about heuristics for selecting first recipe. Now chooses first (essentially random)
                            graph.Connect(recipe, productionRecipe);
                            //break;
                        }
                    }
                }
            }

            var subgraph        = graph.MergeStrongConnectedComponents();
            var allDependencies = subgraph.Aggregate(x => new HashSet <(Recipe, Recipe[])>(), (set, item, subset) =>