Spaces:
Running
Running
File size: 6,071 Bytes
9bd4ce5 463f868 9bd4ce5 463f868 9bd4ce5 | 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 | import json
import re
from collections import defaultdict
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")))
from compiler.aliases import (
CONDITION_SEMANTIC_SPECIAL_CASES,
CONDITION_TRUE_ALIASES,
EFFECT_GRAMMAR_CONVENIENCES,
EFFECT_SEMANTIC_SPECIAL_CASES,
EFFECT_TRUE_ALIASES,
IGNORED_CONDITIONS,
KEYWORD_CONDITIONS,
TRIGGER_ALIASES,
)
def analyze():
# Load metadata
with open("data/metadata.json", "r", encoding="utf-8") as f:
metadata = json.load(f)
parser_trigger_aliases = set(TRIGGER_ALIASES)
parser_effect_aliases = set(EFFECT_TRUE_ALIASES) | set(EFFECT_GRAMMAR_CONVENIENCES) | set(EFFECT_SEMANTIC_SPECIAL_CASES)
parser_condition_aliases = (
set(CONDITION_TRUE_ALIASES)
| set(CONDITION_SEMANTIC_SPECIAL_CASES)
| set(KEYWORD_CONDITIONS)
| set(IGNORED_CONDITIONS)
)
parser_ignored_conds = set(IGNORED_CONDITIONS)
# Load consolidated abilities
with open("data/consolidated_abilities.json", "r", encoding="utf-8") as f:
consolidated = json.load(f)
# Load cards compiled to get counts
with open("data/cards_compiled.json", "r", encoding="utf-8") as f:
compiled = json.load(f)
all_members = compiled.get("member_db", {})
# Map pseudocode to card count
pseudo_to_count = defaultdict(int)
pseudo_to_cards = defaultdict(list)
for mid, mdata in all_members.items():
for ability in mdata.get("abilities", []):
p = ability.get("pseudocode", "")
if p:
pseudo_to_count[p] += 1
pseudo_to_cards[p].append(mdata.get("card_no", mid))
# Extract keywords from pseudocode
effects = defaultdict(int)
conditions = defaultdict(int)
triggers = defaultdict(int)
costs = defaultdict(int)
targets = defaultdict(int)
keyword_usage_cards = {
"EFFECT": defaultdict(set),
"CONDITION": defaultdict(set),
"TRIGGER": defaultdict(set),
"COST": defaultdict(set),
"TARGET": defaultdict(set),
}
for pseudo, count in pseudo_to_count.items():
cards = pseudo_to_cards[pseudo]
lines = pseudo.split("\n")
for line in lines:
line = line.strip()
# Extract Effects
if "EFFECT:" in line:
match = re.search(r"EFFECT:\s*([\w|]+)", line)
if match:
for e in match.group(1).split("|"):
effects[e] += count
for c in cards:
keyword_usage_cards["EFFECT"][e].add(c)
# Extract Conditions
if "CONDITION:" in line:
match = re.search(r"CONDITION:\s*([\w|]+)", line)
if match:
for c_kw in match.group(1).split("|"):
conditions[c_kw] += count
for c in cards:
keyword_usage_cards["CONDITION"][c_kw].add(c)
# Extract Triggers
if "TRIGGER:" in line:
match = re.search(r"TRIGGER:\s*([\w|]+)", line)
if match:
for t in match.group(1).split("|"):
triggers[t] += count
for c in cards:
keyword_usage_cards["TRIGGER"][t].add(c)
# Extract Costs
if "COST:" in line:
match = re.search(r"COST:\s*([\w|]+)", line)
if match:
for cost_kw in match.group(1).split("|"):
costs[cost_kw] += count
for c in cards:
keyword_usage_cards["COST"][cost_kw].add(c)
# Extract Targets (-> KEYWORD)
targets_match = re.findall(r"->\s*([\w|]+)", line)
for tm in targets_match:
for target_kw in tm.split("|"):
targets[target_kw] += count
for c in cards:
keyword_usage_cards["TARGET"][target_kw].add(c)
# Compare with metadata
meta_opcodes = set(metadata.get("opcodes", {}).keys())
meta_conditions = set(metadata.get("conditions", {}).keys())
meta_triggers = set(metadata.get("triggers", {}).keys())
meta_costs = set(metadata.get("costs", {}).keys())
meta_targets = set(metadata.get("targets", {}).keys())
# Extras that are often handled without opcodes or are meta
ignored_keywords = {
"COUNT_VAL",
"PLAYER",
"SELF",
"OPPONENT",
"VARIABLE",
"PER_CARD",
"AND",
"OR",
"NOT",
"TRUE",
"FALSE",
"IF",
"THEN",
"ELSE",
"VALUE_GT",
"VALUE_GE",
"VALUE_LT",
"VALUE_LE",
"VALUE_EQ",
"VALUE_NE",
}
def print_gaps(category, found, meta, parser_aliases, ignore=set()):
print(f"\n=== {category} GAPS (Not in Metadata AND Not Aliased) ===")
missing = []
for kw, count in sorted(found.items(), key=lambda x: -x[1]):
if kw not in meta and kw not in parser_aliases and kw not in ignore and not kw.isdigit():
missing.append((kw, count))
if not missing:
print("No gaps found.")
else:
for kw, count in missing:
cards = sorted(list(keyword_usage_cards[category][kw]))[:5]
print(f"{kw:<25} Usage: {count:>3} Cards: {', '.join(cards)}")
print_gaps("TRIGGER", triggers, meta_triggers, parser_trigger_aliases)
print_gaps("EFFECT", effects, meta_opcodes, parser_effect_aliases)
print_gaps(
"CONDITION", conditions, meta_conditions, parser_condition_aliases, ignored_keywords.union(parser_ignored_conds)
)
print_gaps("COST", costs, meta_costs, set()) # Costs don't have aliases usually
print_gaps("TARGET", targets, meta_targets, set(), ignored_keywords)
if __name__ == "__main__":
analyze()
|