mh_snow_golem / app.py
tsu2000's picture
Update app.py
25e0d20
raw history blame
No virus
10.9 kB
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)