File size: 4,438 Bytes
d7d1dc9
 
 
 
 
 
4d85b4b
d7d1dc9
81cf27a
 
 
 
 
d7d1dc9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81cf27a
d7d1dc9
 
 
 
4d85b4b
d7d1dc9
 
 
ed2014d
d7d1dc9
 
 
4d85b4b
 
 
 
1fb0d85
 
 
d7d1dc9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81cf27a
d7d1dc9
 
 
 
 
4d85b4b
 
 
 
 
 
 
d7d1dc9
 
 
d606caf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from datetime import datetime

import numpy as np
from numpy.typing import NDArray
from pyscipopt import Model, quicksum

from ui.display import format_results

from data_loader import (
    DISHES_DF,
    LABELS,
    PLANTS_DF,
)


def optimize(budget, strategy, stocks, sold_prices):
    """
    Calculate the optimal solution of item sales based on the given budget
    and inventory constraints.

    Args:
        - budget (int): The total budget available for purchasing items.
        - strategy (str): The strategy to use for optimization, either "MinimizeStock" or "MaximizeStock".
        - stocks (NDArray[int]): An array representing the available stock of each item.
        - sold_prices (NDArray[int]): An array representing the selling price of each item.

    Returns:
        - dict: A dictionary containing the solution, total price, total count,
    """
    # Initialize the master problem
    model = Model("Knapsack")

    # Decision variables in master problem
    x = [
        model.addVar(
            vtype="I", name=f"x_{i}", lb=0, ub=int(stocks[i]) if stocks[i] else 0
        )
        for i in range(len(stocks))
    ]

    obj1 = quicksum(sold_prices[i] * x[i] for i in range(len(stocks)))
    obj2 = quicksum(x[i] for i in range(len(stocks)))

    # Objective: maximize total value of sold plants
    model.setObjective(obj1, "maximize")

    model.addCons(obj1 <= budget)

    # first optimize
    model.hideOutput()
    model.optimize()

    if model.getStatus() == "optimal":
        optimal_total_value = model.getObjVal()
        model.freeTransform()

        model.setObjective(
            obj2, "maximize" if strategy == "MinimizeStock" else "minimize"
        )
        model.addCons(obj1 == optimal_total_value)
        model.optimize()

        # Final solution processing
        solution = [0] * len(x)
        total_value = 0
        total_count = 0

        if model.getStatus() == "optimal":
            for i, var in enumerate(x):
                if (n := round(model.getVal(var))) > 0 and sold_prices[i] > 0:
                    solution[i] = n
                    total_count += n
                    total_value += n * sold_prices[i]

            return {
                "solution": solution,
                "total_price": int(total_value),
                "total_count": int(total_count),
                "remaining": int(budget - total_value),
            }
    raise ValueError(
        f"Optimization failed with status: {model.getStatus()} at {datetime.now()}"
    )


def get_results(
    language,
    currency,
    budget,
    plants_prices_extra_rate,
    dishes_prices_extra_rate,
    talent_price_bonus,
    strategy,
    *inventory,
):
    talent_price_bonus = talent_price_bonus or 0
    prices: NDArray[np.int16] = np.concat(
        [
            PLANTS_DF[currency] * (1 + plants_prices_extra_rate),
            np.floor(
                DISHES_DF[currency]
                * (1 + dishes_prices_extra_rate)
                * (1 + talent_price_bonus / 100)
            ).astype(
                np.int16
            ),  # Licet(@discord)'s data shows that all values are rounded down: https://docs.google.com/spreadsheets/d/1CWv0VmgfKKWWlqUty9hqGwWt86_G94x5K89DP4b4eRI
        ],
        dtype=np.int16,
    )
    outputs = optimize(
        budget,
        strategy,
        np.array([n if n else 0 for n in inventory], dtype=np.int16),
        prices,
    )
    plants_solution = outputs["solution"][: len(PLANTS_DF)]
    dishes_solution = outputs["solution"][
        len(PLANTS_DF) : len(PLANTS_DF) + len(DISHES_DF)
    ]
    results = {}
    results["solution"] = {
        f"{LABELS[language]['plants'][PLANTS_DF.iloc[i]['name']]} ({LABELS[language]['tiers'][PLANTS_DF.iloc[i]['tier']]}, {int(prices[i])} {currency})": plants_solution[
            i
        ]
        for i in range(len(PLANTS_DF))
        if plants_solution[i] > 0
    }
    results["solution"].update({
        f"{LABELS[language]['dishes'][DISHES_DF.iloc[i]['name']]} ({LABELS[language]['tiers'][DISHES_DF.iloc[i]['tier']]}, {int(prices[len(PLANTS_DF) + i])} {currency})": dishes_solution[
            i
        ]
        for i in range(len(DISHES_DF))
        if dishes_solution[i] > 0
    })
    results["total_price"] = outputs["total_price"]
    results["total_count"] = outputs["total_count"]
    results["remaining"] = outputs["remaining"]
    return format_results(results, language)