Skip to content

Instantly share code, notes, and snippets.

@Jaakkonen
Last active June 9, 2023 23:31
Show Gist options
  • Select an option

  • Save Jaakkonen/de5d42a82dbb3e968732c30c669ccb26 to your computer and use it in GitHub Desktop.

Select an option

Save Jaakkonen/de5d42a82dbb3e968732c30c669ccb26 to your computer and use it in GitHub Desktop.
Idle Skilling cauldron optimizer
"""
Simulates the Cauldron in Idle Skilling.
Optimizes for the highest amount of speed and XP per hour.
Each of the 6 cauldrons have 3 equivalent crystal slots which can give following bonuses:
- Green: 2x XP, 2.5x Speed
- Pink: 3x Speed (also to adjacent)
- Orange: 1.2x all crystals
- "x" is an inactive cauldron
The boosts are additive
(C) 2023 Jaakkonen
License: MIT
"""
import numba
from itertools import combinations_with_replacement, product
@numba.jit(nopython=True)
def get_cauldron_xp_per_hour(config: str):
cauldrons = config.split(" ")
xp_factors: list[float] = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
speed_factors: list[float] = [1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
oranges = config.count("O")
# Orange crystals do not seem to affect themselves and do in fact provide
# a ~17% boost to the other crystals in reality
total_orange_multiplier = 1.0 + (41.0 / 240 * oranges)
for i in range(6):
cauldron = cauldrons[i]
for crystal in cauldron:
if crystal == "x":
xp_factors[i] = 0
speed_factors[i] = 0
elif crystal == "G":
xp_factors[i] += 1 * total_orange_multiplier
speed_factors[i] += 1.5 * total_orange_multiplier
elif crystal == "P":
speed_factors[i] += 2 * total_orange_multiplier
if i > 0:
speed_factors[i - 1] += 2 * total_orange_multiplier
if i < 6 - 1:
speed_factors[i + 1] += 2 * total_orange_multiplier
elif crystal == "O":
pass
else:
raise ValueError(f"Invalid gem {crystal}")
total_speed = sum(speed_factors)
total_xp = 1.0
for speed, xp in zip(speed_factors, xp_factors):
total_xp += speed * xp
return total_speed, total_xp, speed_factors, xp_factors
def main():
VERBOSE = False
cauldron_opts = {"".join(c) for c in combinations_with_replacement("GPO", 3)}
for cauldrons in range(1, 7):
cauldron_combs = product(cauldron_opts, repeat=cauldrons)
cauldron_combs = (
" ".join(cauldron) + " xxx" * (6 - cauldrons) for cauldron in cauldron_combs
)
best_xp = 0
best_speed = 0
best_xp_config = None
best_speed_config = None
for config in cauldron_combs:
res = get_cauldron_xp_per_hour(config)
res = {
"total_speed": res[0],
"total_xp": res[1],
"speed_factors": res[2],
"xp_factors": res[3],
}
print_this = False
if res["total_xp"] > best_xp:
best_xp = res["total_xp"]
best_xp_config = config
print_this = True
if res["total_speed"] > best_speed:
best_speed = res["total_speed"]
best_speed_config = config
print_this = True
if print_this and VERBOSE:
# Limit all floats to 2 decimals when printing including the lists (speed_factors, xp_factors)
print(f"Config: {config}")
print(f"{res['total_xp']:.2f} XP, {res['total_speed']:.2f} Speed")
print(
f"Speed factors: [{', '.join(f'{speed:.2f}' for speed in res['speed_factors'])}]"
)
print(
f"XP factors: [{', '.join(f'{xp:.2f}' for xp in res['xp_factors'])}]"
)
print()
print(f"Best XP: {best_xp:.2f} ({best_xp_config})")
print(f"Best Speed: {best_speed:.2f} ({best_speed_config})")
print()
if __name__ == "__main__":
main()
# Results:
#
# Best XP: 23.00 (GGG xxx xxx xxx xxx xxx)
# Best Speed: 7.00 (PPP xxx xxx xxx xxx xxx)
#
# Best XP: 56.10 (GGG PPO xxx xxx xxx xxx)
# Best Speed: 26.00 (PPP PPP xxx xxx xxx xxx)
#
# Best XP: 114.63 (OOO PPP GGG xxx xxx xxx)
# Best Speed: 48.62 (PPO PPP PPO xxx xxx xxx)
#
# Best XP: 199.03 (GGG PPP GGG OOO xxx xxx)
# Best Speed: 78.17 (POO PPP PPP OOO xxx xxx)
#
# Best XP: 331.16 (GGG PPP GGG OOO OOO xxx)
# Best Speed: 114.35 (OOO PPP PPP PPP OOO xxx)
#
# Best XP: 496.39 (GGG PPP GGG OOO OOO OOO)
# Best Speed: 151.80 (OOO PPP PPP PPP PPP OOO)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment