const elementIds = [ "world", "toggleRun", "resetWorld", "speed", "worldSize", "viewMode", "year", "activeGroups", "urbanPopulation", "ethnicities", "cities", "polities", "wars", "routes", "farmingKnowledge", "metallurgyKnowledge", "deaths", "frameCost", "ethnicityList", "fpsCounter", "legend", "tooltip", "worldTrendGraph", "stateGraph", "historyRange", "currentDate", "historyFilter", "historySort", "historyMetric", "toggleActiveStates", "togglePastStates", "toggleEventIcons", "stateGraphInfo", "stateGraphTooltip", "eventLegend", "regenerateDialog", "confirmRegenerate", "cancelRegenerate", "triggerDisaster", "buildCity", "connectCities", "interventionStatus" ]; const els = Object.fromEntries(elementIds.map(id => [id === "world" ? "canvas" : id, document.getElementById(id)])); els.sim = document.querySelector(".sim"); const ctx = els.canvas.getContext("2d", { alpha: false }); let sim; let running = true; let lastStatsAt = 0; let lastGraphRenderAt = 0; let lastRenderAt = 0; let lastSimTickAt = 0; let renderDirty = true; let loopTimer = null; let loopScheduled = false; let fpsWindowStartedAt = 0; let fpsFrames = 0; let hoverState = null; let renderImage = null; let renderImageSize = 0; const LoopConfig = Object.freeze({ simTickMs: 33, renderMs: 66, stepBudgetMs: 8, idlePollMs: 180, hiddenPollMs: 1000 }); const graphState = { filter: "all", sort: "oldest", metric: "power", showActive: true, showPast: true, showEventIcons: true, hoverPolityId: null, rows: [], markers: [], scale: null }; const legendByMode = { terrain: () => terrainInfo.map(t => `${t.name}`).join(""), ethnicity: () => "民族の色・濃さは人口と優勢度", pressure: () => "赤いほど人口圧力が高い", polities: () => "", nomads: () => "遊牧集団", technology: () => "緑:農業 / 黄土色:冶金", pheromone: () => "明るい黄色ほど交通量が多い", resources: () => "明るい緑ほど資源が豊富" };