Last active
March 7, 2026 13:33
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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