"""TypeChart chart-only reference v1. Python 3 standard library; no network. Run in this downloaded folder. Rebuilds results from pinned charts-v1.json. Profiles enumerate mathematical type sets, including sets with no species. This is not a damage simulator, competitive ranking, or gameplay experiment. """ import csv import io import json from itertools import combinations from pathlib import Path BASE = Path(__file__).resolve().parent def generate(): charts = json.loads((BASE / 'charts-v1.json').read_text()) profiles, deltas, transitions = [], [], [] for era, chart in charts.items(): types = list(chart) for defenders in [(t,) for t in types] + list(combinations(types, 2)): values = {a: chart[defenders[0]][a] * (chart[defenders[1]][a] if len(defenders) == 2 else 1) for a in types} groups = {str(v): [a for a, n in values.items() if n == v] for v in [0, .25, .5, 1, 2, 4]} assert sum(map(len, groups.values())) == len(types) profiles.append({'era': era, 'defenders': list(defenders), 'multipliers': values, 'groups': groups}) for old, new in [('gen1', 'gen2_5'), ('gen2_5', 'gen6plus')]: common = list(charts[old]) single = [] for defender in common: for attack in common: before, after = charts[old][defender][attack], charts[new][defender][attack] if before != after: single.append({'from': old, 'to': new, 'attack': attack, 'defender': defender, 'before': before, 'after': after}) changed_profiles = 0 for defenders in [(t,) for t in common] + list(combinations(common, 2)): changed = False for attack in common: before = charts[old][defenders[0]][attack] * (charts[old][defenders[1]][attack] if len(defenders) == 2 else 1) after = charts[new][defenders[0]][attack] * (charts[new][defenders[1]][attack] if len(defenders) == 2 else 1) if before != after: changed = True deltas.append({'from': old, 'to': new, 'defenders': '/'.join(defenders), 'attack': attack, 'before': before, 'after': after}) changed_profiles += changed transitions.append({'from': old, 'to': new, 'commonTypes': len(common), 'newTypes': [t for t in charts[new] if t not in charts[old]], 'singleTypeChanges': single, 'commonTypeChangedProfiles': changed_profiles}) assert [sum(p['era'] == era for p in profiles) for era in charts] == [120, 153, 171] assert charts['gen1']['Psychic']['Ghost'] == 0 assert charts['gen2_5']['Steel']['Ghost'] == .5 and charts['gen6plus']['Steel']['Ghost'] == 1 assert charts['gen1']['Fire']['Ice'] == 1 and charts['gen2_5']['Fire']['Ice'] == .5 assert charts['gen6plus']['Ground']['Grass'] * charts['gen6plus']['Water']['Grass'] == 4 assert charts['gen6plus']['Ground']['Electric'] * charts['gen6plus']['Water']['Electric'] == 0 result = {'version': '1.0.0', 'model': 'chart-only multiplication', 'profiles': profiles, 'transitions': transitions, 'changedCells': deltas} return result if __name__ == '__main__': result = generate() (BASE / 'results-v1.json').write_text(json.dumps(result, indent=2) + '\n') buf = io.StringIO(newline='') writer = csv.DictWriter(buf, fieldnames=['from', 'to', 'defenders', 'attack', 'before', 'after']) writer.writeheader() writer.writerows(result['changedCells']) (BASE / 'changes-v1.csv').write_text(buf.getvalue()) print(json.dumps({'profiles': len(result['profiles']), 'transitions': result['transitions'], 'changedCells': len(result['changedCells'])}, indent=2))