File size: 10,879 Bytes
5e3846a
 
927fb0b
5e3846a
 
 
927fb0b
5e3846a
 
 
927fb0b
25e0d20
 
 
5e3846a
 
 
927fb0b
 
5e3846a
 
 
 
 
 
927fb0b
5e3846a
 
 
 
 
 
927fb0b
5e3846a
 
 
 
 
 
 
 
 
 
 
 
 
 
927fb0b
 
 
5e3846a
 
 
 
 
 
 
25e0d20
927fb0b
5e3846a
 
 
 
 
25e0d20
5e3846a
 
 
 
 
 
25e0d20
5e3846a
 
 
 
 
 
25e0d20
5e3846a
 
 
927fb0b
5e3846a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
927fb0b
 
5e3846a
 
 
 
 
 
 
 
 
 
 
 
 
927fb0b
 
5e3846a
 
 
 
 
 
 
 
 
 
 
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
import math
from shiny import App, reactive, render, ui

# Crafting requirements for golem parts
ANIMATED_SNOW_PER_HEAD_TORSO = 20
ANIMATED_SNOW_PER_LIMB = 10

# Animated snow given from smashing golem parts
ANIMATED_SNOW_SMASH_HEAD_TORSO = 10
ANIMATED_SNOW_SMASH_PER_LIMB = 5

# Maximum number of each resource that user can input
RESOURCE_CAP = 100000

# Define app UI
app_ui = ui.page_fluid(
    ui.h1("☃️ Snow Golem Crafting Optimizer for MouseHunt (GWH 2023) ☃️"),
    ui.layout_sidebar(
        ui.sidebar(
            ui.p("Adjust the user inputs to view the maximum number of snow golems you can make with your current resources."),
            ui.input_numeric("golem_heads", ui.HTML("<b>Enter the number of snow golem heads you currently have:</b>"), value = 10, min = 0, max = 1000),
            ui.input_numeric("golem_torsos", ui.HTML("<b>Enter the number of snow golem torsos you currently have:</b>"), value = 5, min = 0, max = 1000),
            ui.input_numeric("golem_limbs", ui.HTML("<b>Enter the number of snow golem limbs you currently have:</b>"), value = 30, min = 0, max = 1000),
            ui.input_numeric("ani_snow", ui.HTML("<b>Enter the amount of animated snow you currently have:</b>"), value = 15, min = 0, max = 1000),
            width = 350
        ),
        ui.h3("User-selected inputs"),
        ui.row(
            ui.column(3, ui.output_text_verbatim("user_golem_heads")),
            ui.column(3, ui.output_text_verbatim("user_golem_torsos")),
            ui.column(3, ui.output_text_verbatim("user_golem_limbs")),
            ui.column(3, ui.output_text_verbatim("user_ani_snow"))
        ),
        ui.panel_conditional(
            "!output.user_golem_heads.includes('Invalid') && !output.user_golem_torsos.includes('Invalid') && !output.user_golem_limbs.includes('Invalid') && !output.user_ani_snow.includes('Invalid') && input.golem_heads >= 0 && input.golem_torsos >= 0 && input.golem_limbs >= 0 && input.ani_snow >= 0 && input.golem_heads != null && input.golem_torsos != null && input.golem_limbs != null && input.ani_snow != null",
            ui.h3("Maximum snow golems craftable with current resources"),
            ui.output_text_verbatim("final_result"),
            ui.hr(style = "border: none; border-top: 2px; margin-top: 10px; margin-bottom: 10px;"),
            ui.h3("Detailed breakdown"),
            ui.row(
                ui.column(4, ui.h5("☃️ Golem Requirements"), ui.HTML(ui.output_ui("golem_req"))),
                ui.column(4, ui.h5("💥 Smashing Requirements"), ui.HTML(ui.output_ui("smashing_req"))),
                ui.column(4, ui.h5("🛠️ Crafting Requirements"), ui.HTML(ui.output_ui("crafting_req")))
            ),
            ui.hr(style = "border: none; border-top: 2px; margin-top: 10px; margin-bottom: 10px;"),
            ui.h5("❄️ Animated Snow Usage"), ui.HTML(ui.output_ui("ani_snow_usage"))
        )
    ),
)

# Define app server
def server(input, output, session):
    @output
    @render.text 
    def user_golem_heads():
        if input.golem_heads() is None:
            return 
        return f"🙂 Head(s): {int(input.golem_heads()) if (input.golem_heads() >= 0 and input.golem_heads() <= RESOURCE_CAP) else 'Invalid'}"

    @output
    @render.text 
    def user_golem_torsos():
        if input.golem_torsos() is None:
            return 
        return f"👕 Torso(s): {int(input.golem_torsos()) if (input.golem_torsos() >= 0 and input.golem_torsos() <= RESOURCE_CAP) else 'Invalid'}"
    
    @output
    @render.text 
    def user_golem_limbs():
        if input.golem_limbs() is None:
            return 
        return f"🦵 Limb(s): {int(input.golem_limbs()) if (input.golem_limbs() >= 0 and input.golem_limbs() <= RESOURCE_CAP) else 'Invalid'}"
    
    @output
    @render.text 
    def user_ani_snow():
        if input.ani_snow() is None:
            return 
        return f"❄️ Anim. Snow: {int(input.ani_snow()) if (input.ani_snow() >= 0 and input.ani_snow() <= RESOURCE_CAP) else 'Invalid'}"

 
    # Perform the calculation for the maximum number of snow golems possible
    @reactive.Calc
    def calculate():
        # Get user inputs
        initial_animated_snow = input.ani_snow()
        heads_in_inventory = input.golem_heads()
        torsos_in_inventory = input.golem_torsos()
        limbs_in_inventory = input.golem_limbs()

        # Perform data validation
        if any(elem is None or elem < 0 for elem in [initial_animated_snow, heads_in_inventory, torsos_in_inventory, limbs_in_inventory]):
            return tuple([0] * 10)
        
        # Change to suitable data type
        initial_animated_snow = int(initial_animated_snow)
        heads_in_inventory = int(heads_in_inventory)
        torsos_in_inventory = int(torsos_in_inventory) 
        limbs_in_inventory = int(limbs_in_inventory)
        
        # Amount of animated snow used per golem
        animated_snow_per_golem = ANIMATED_SNOW_PER_HEAD_TORSO * 2 + ANIMATED_SNOW_PER_LIMB * 4

        # Calculate total animated snow equivalent units
        animated_snow_eq_units = initial_animated_snow + (torsos_in_inventory + heads_in_inventory) * ANIMATED_SNOW_PER_HEAD_TORSO + limbs_in_inventory * ANIMATED_SNOW_PER_LIMB

        # Theoretical maximum number of golems possible to be made based on the equivalent units of animated snow
        res = math.floor(animated_snow_eq_units / animated_snow_per_golem)

        # Scenario where golem parts need to be smashed to maximise number of snow golems built
        if res < heads_in_inventory or res < torsos_in_inventory or res * 4 < limbs_in_inventory:
            # Brute force method to determine how much of the excess part to smash
            for i in range(1, max(heads_in_inventory + 1, torsos_in_inventory + 1, limbs_in_inventory // 4 + 1)):   

                # Start with smashing all but 1 of the largest excess piece then decrease from there
                res = i
            
                # Determine which golem parts that are to be smashed
                smashed_heads = (heads_in_inventory - res) if res < heads_in_inventory else 0
                smashed_torsos = (torsos_in_inventory - res) if res < torsos_in_inventory else 0
                smashed_limbs = (limbs_in_inventory - res * 4) if res * 4 < limbs_in_inventory else 0

                # Get value of animated snow from smashing golem parts
                animated_snow_from_smashing = (smashed_heads + smashed_torsos) * ANIMATED_SNOW_SMASH_HEAD_TORSO + smashed_limbs * ANIMATED_SNOW_SMASH_PER_LIMB

                # Get the revised amount of animated snow that need to be used to craft the snow golems
                crafted_heads = (res - heads_in_inventory) if heads_in_inventory < res else 0
                crafted_torsos = (res - torsos_in_inventory) if torsos_in_inventory < res else 0
                crafted_limbs = (res * 4 - limbs_in_inventory) if limbs_in_inventory < res * 4 else 0

                animated_snow_for_crafting = initial_animated_snow + animated_snow_from_smashing
                
                # Check if available animated snow is insufficient to craft necessary snow golem parts, then go back 1 iteration (That will be the maximum of snow golems that are buildable with current resource constraints)
                if animated_snow_for_crafting < (crafted_heads + crafted_torsos) * ANIMATED_SNOW_PER_HEAD_TORSO + crafted_limbs * ANIMATED_SNOW_PER_LIMB:

                    # Go to previous iteration
                    res -= 1
                    
                    # Determine which golem parts that are needed to be smashed
                    smashed_heads = (heads_in_inventory - res) if res < heads_in_inventory else 0
                    smashed_torsos = (torsos_in_inventory - res) if res < torsos_in_inventory else 0
                    smashed_limbs = (limbs_in_inventory - res * 4) if res * 4 < limbs_in_inventory else 0

                    # Get value of animated snow from smashing golem parts
                    animated_snow_from_smashing = (smashed_heads + smashed_torsos) * ANIMATED_SNOW_SMASH_HEAD_TORSO + smashed_limbs * ANIMATED_SNOW_SMASH_PER_LIMB

                    # Get the revised amount of animated snow that need to be used to craft the snow golems
                    crafted_heads = (res - heads_in_inventory) if heads_in_inventory < res else 0
                    crafted_torsos = (res - torsos_in_inventory) if torsos_in_inventory < res else 0
                    crafted_limbs = (res * 4 - limbs_in_inventory) if limbs_in_inventory < res * 4 else 0

                    animated_snow_for_crafting = initial_animated_snow + animated_snow_from_smashing

                    break

        else: # If no golem parts need to be smashed

            smashed_heads, smashed_torsos, smashed_limbs = 0, 0, 0
            animated_snow_for_crafting = initial_animated_snow
            animated_snow_from_smashing = 0

            crafted_heads = max(res - heads_in_inventory, 0)
            crafted_torsos = max(res - torsos_in_inventory, 0)
            crafted_limbs = max(res * 4 - limbs_in_inventory, 0)

        # Calculate total amount of animated snow used
        used_animated_snow = crafted_heads * ANIMATED_SNOW_PER_HEAD_TORSO + crafted_torsos * ANIMATED_SNOW_PER_HEAD_TORSO + crafted_limbs * ANIMATED_SNOW_PER_LIMB
        leftover_animated_snow = initial_animated_snow + animated_snow_from_smashing - used_animated_snow

        return res, smashed_heads, smashed_torsos, smashed_limbs, crafted_heads, crafted_torsos, crafted_limbs, animated_snow_for_crafting, used_animated_snow, leftover_animated_snow

    @output
    @render.text
    def final_result():
        return f"You can build a maximum of {calculate()[0]} golem(s). {calculate()[0] * '☃️'}"
    
    @output
    @render.text
    def golem_req():
        return f"<li>🙂 Head(s) needed: {calculate()[0]}</li><li>👕 Torso(s) needed: {calculate()[0]}</li><li>🦵 Limbs needed: {calculate()[0] * 4}</li>"
        
    @output
    @render.text
    def smashing_req():
        return f"<li>💥🙂 Smash head(s): {calculate()[1]}</li><li>💥👕 Smash torso(s): {calculate()[2]}</li><li>💥🦵 Smash limb(s): {calculate()[3]}</li>"

    @output
    @render.text
    def crafting_req():
        return f"<li>🛠️🙂 Craft head(s): {calculate()[4]}</li><li>🛠️👕 Craft torso(s): {calculate()[5]}</li><li>🛠️🦵 Craft limb(s): {calculate()[6]}</li>"
    
    @output
    @render.text
    def ani_snow_usage():
        return f"<li>➕❄️ Total Animated Snow available for use after smashing: {calculate()[7]}</li><li>➖❄️ Animated Snow used to craft necessary golem parts: {calculate()[8]}</li><li>🟰❄️ Remaining Animated Snow left over after smashing + crafting: {calculate()[9]}</li>"
    
# Create app
app = App(app_ui, server, debug = True)