Skip to content

Instantly share code, notes, and snippets.

@tatianass
Last active March 7, 2026 13:33
Show Gist options
  • Select an option

  • Save tatianass/b7153661f120ee7e26a58f0ab114cf5c to your computer and use it in GitHub Desktop.

Select an option

Save tatianass/b7153661f120ee7e26a58f0ab114cf5c to your computer and use it in GitHub Desktop.
This script go over my Summoners War runes and see how many monsters you can get to 280 speed (threshold for late game). Utilizes Requirement-First approach.
# Speed Build Analysis for 6-star Monsters in Summoners War
# This script analyzes the provided JSON data to determine how many 6-star monsters can reach a target speed (e.g., 200) using specific rune set combinations, while considering various constraints.
# Key Features:
# - Parses all runes (both inventory and equipped) to calculate their speed contributions.
# - Sorts monsters by their base speed and attempts to find optimal rune builds for each.
# - Categorizes monsters based on the rune set combinations used in their builds.
# - Provides flexibility in analysis through parameters like allowing used runes, using average base speed, and prioritizing broken sets.
# Note: This script assumes that the JSON structure is consistent with the provided format and that the necessary fields are present.
# Uses Requirement-First approach to optimize rune selection and minimize redundant calculations.
import json
import math
# Updated Constants based on your provided ID map
SWIFT, VIOLENT, DESPAIR, WILL = 3, 13, 10, 15
def calculate_average_base_spd(data):
# Filter unit_list for 6-star monsters (class == 6)
six_star_monsters = [
mon.get('spd', 0)
for mon in data.get('unit_list', [])
if mon.get('class') == 6
]
if not six_star_monsters:
return "No 6-star monsters found in the JSON."
# Calculate the average
avg_spd = sum(six_star_monsters) / len(six_star_monsters)
return {
"count": len(six_star_monsters),
"average_spd": round(avg_spd, 2)
}
def run_speed_analysis(data, target_spd=200, allow_used_runes=False, use_avg_base=False, broken_set_priority=False):
# 0. Get avg base SPD for 6-star monsters
avg_info = calculate_average_base_spd(data)
print(f"Average Base SPD for 6-star Monsters: {avg_info['average_spd']} (Count: {avg_info['count']})\n")
# 1. Parse ALL 3,700+ Runes (Inventory + Equipped)
runes = []
def parse_rune(r):
spd = 0
if r.get('pri_eff', [0,0])[0] == 8: spd += r['pri_eff'][1]
for sub in r.get('sec_eff', []):
if sub[0] == 8: spd += (sub[1] + sub[3]) # Value + Grind
return {'id': r['rune_id'], 'slot': r['slot_no'], 'spd': spd, 'set': r['set_id']}
# Inventory
for r in data.get('runes', []): runes.append(parse_rune(r))
# Equipped
for unit in data.get('unit_list', []):
for r in unit.get('runes', []): runes.append(parse_rune(r))
# 2. Setup Monsters (Sorted by Base SPD)
monsters = sorted([m for m in data.get('unit_list', []) if m.get('class') == 6],
key=lambda x: x.get('spd', 0), reverse=True)
used_runes = set()
categories = {cat: [] for cat in ["Violent Will", "Violent Broken", "Despair Will", "Despair Broken", "Swift Will", "Swift Broken"]}
# 3. Optimized Build Finder
def find_best_build(base_spd, available_runes, main_set_id, off_set_id=None):
# Filter runes by slot
slots = {i: [r for r in available_runes if r['slot'] == i] for i in range(1, 7)}
# Get fastest Main Set and fastest Offset/Any for every slot
best_m = {}
best_o = {}
for i in range(1, 7):
m_opts = [r for r in slots[i] if r['set'] == main_set_id]
if not m_opts: return None
best_m[i] = max(m_opts, key=lambda x: x['spd'])
o_opts = [r for r in slots[i] if (r['set'] == off_set_id if off_set_id else True)]
if not o_opts: return None
best_o[i] = max(o_opts, key=lambda x: x['spd'])
# Calculate where we gain the most speed by NOT using a main set rune
# Gain = Speed of best Offset - Speed of best Main Set
diffs = sorted([(best_o[i]['spd'] - best_m[i]['spd'], i) for i in range(1, 7)], reverse=True)
# Top 2 diffs are our Offset slots
off_slots = [diffs[0][1], diffs[1][1]]
build = [best_o[i] if i in off_slots else best_m[i] for i in range(1, 7)]
# Calculate Total
# swift_bonus = math.floor(base_spd * 0.25) if main_set_id == SWIFT else 0
# total = base_spd + swift_bonus + sum(r['spd'] for r in build)
sum_rune_spd = sum(r['spd'] for r in build)
total = base_spd + sum_rune_spd
return build if total >= target_spd else None
# 4. Processing Loop
for mon in monsters:
base = avg_info['average_spd'] if use_avg_base else mon.get('spd', 0)
# Filter out used runes
current_pool = runes if allow_used_runes else [r for r in runes if r['id'] not in used_runes]
# Define set requirements based on priority
requirements = []
if broken_set_priority:
requirements.append(("Violent Broken", VIOLENT, None))
requirements.append(("Despair Broken", DESPAIR, None))
requirements.append(("Swift Broken", SWIFT, None))
requirements.append(("Violent Will", VIOLENT, WILL))
requirements.append(("Despair Will", DESPAIR, WILL))
requirements.append(("Swift Will", SWIFT, WILL))
else:
requirements.append(("Violent Will", VIOLENT, WILL))
requirements.append(("Despair Will", DESPAIR, WILL))
requirements.append(("Swift Will", SWIFT, WILL))
requirements.append(("Violent Broken", VIOLENT, None))
requirements.append(("Despair Broken", DESPAIR, None))
requirements.append(("Swift Broken", SWIFT, None))
# Try each requirement in order
for cat, m_id, o_id in requirements:
res = find_best_build(base, current_pool, m_id, o_id)
if res:
categories[cat].append(mon.get('unit_id'))
if not allow_used_runes:
used_runes.update(r['id'] for r in res)
break
# 5. Output
print("\n=== SPEED BUILD CATEGORIES ===")
print(f"required SPD: {target_spd} | allow_used_runes: {allow_used_runes} | use_avg_base: {use_avg_base} | broken_set_priority: {broken_set_priority}\n")
print(f"{'SET CATEGORY':<15} | {'COUNT'}")
print("-" * 25)
for cat, units in categories.items():
print(f"{cat:<15} | {len(units)}")
with open('sw.json', 'r') as f:
data = json.load(f)
run_speed_analysis(data, target_spd=280, allow_used_runes=False, use_avg_base=True, broken_set_priority=False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment