from pathlib import Path import re ROOT = Path(__file__).resolve().parents[1] def text(rel): return (ROOT / rel).read_text(encoding='utf-8') def must(cond, msg): if not cond: raise AssertionError(msg) index = text('index.html') ui_bind = text('js/ui_bind.js') ui_mouse = text('js/ui_input_mouse.js') ui_shared = text('js/ui_input_shared.js') view = text('js/world_view.js') data = text('js/data.js') temp = text('js/world_temperature_system.js') disease = text('js/tarinai_item_effects.js') spatial = text('js/sim_core.js') spatial_budget = text('js/world_spatial_budget.js') targeting = text('js/tarinai_item_targeting.js') ach = text('js/achievements.js') # Tool mode survives ordinary controls, but blank UI space explicitly clears it. must('isInteractiveUiTarget' in ui_bind and 'clearSelectedToolSilently' in ui_bind, 'blank-UI tool cancellation helpers are missing') must('!canvas?.contains?.(target) && !isInteractiveUiTarget(target)' in ui_bind, 'blank UI space is not the exclusive document-click cancellation path') must('target.closest("button, a, input, select, textarea, label, summary' in ui_bind, 'ordinary UI controls are not protected from tool cancellation') # Limits: 300, next slot 301 = infinity, default infinity. must('COLONY_LIMIT_MAX = 300' in ui_bind, 'colony limit max is not 300') must('COLONY_LIMIT_INFINITY_SLOT = 301' in ui_bind, 'infinity slot is not 301') must(re.search(r'max="301"[^>]*value="301"', index) is not None, 'limit slider does not default to infinity slot') # Right-drag context-menu suppression. must('suppressContextMenuUntil' in ui_mouse and 'contextmenu' in ui_mouse, 'right-pan context-menu suppression missing') must('suppressContextMenuUntil' in ui_shared, 'right-pan suppression timestamp is not set on pan end') # Zoom. must(re.search(r'BASE_ZOOM_MAX\s*=\s*4\.5', view) is not None, 'zoom maximum is not 4.5') # Disease probability reduced to one-third at the shared helper. must(re.search(r'Number\(base\)[^\n]*/\s*3', disease) is not None or '/ 3' in disease[disease.find('diseaseChance'):disease.find('diseaseChance')+240], 'diseaseChance is not reduced to one-third') # Summer correction removed completely. must(re.search(r'summerTemperatureBoost\s*:\s*0\b', data) is not None, 'summerTemperatureBoost is not zero') body = re.search(r'(?:function\s+)?seasonTemperatureBias\s*\([^)]*\)\s*\{([\s\S]*?)\n\s*\}', temp) must(body is not None and re.search(r'\breturn\s+0\s*;', body.group(1)), 'seasonTemperatureBias does not return zero') # Grass: dedicated spatial index and no global grass fallback re-add in food search. must('grassCells = new Map()' in spatial, 'dedicated grassCells index missing') must('nearbyGrass(' in spatial_budget and 'spatial.grassCells' in spatial_budget, 'nearbyGrass spatial query missing') must('type === "grass"' in targeting and 'nearbyFood' in targeting, 'grass global-bucket skip guard missing') # Ensure the hot-path global type bucket explicitly skips grass when nearbyFood exists. must(re.search(r'if\s*\(\s*type\s*===\s*["\']grass["\'][^)]*nearbyFood', targeting) is not None, 'food targeting does not skip the global grass bucket') # Achievements 73-75 and persistent Memento Mori progress. for aid in ('well_informed', 'lively_making', 'memento_mori'): must(aid in ach, f'achievement {aid} missing') must('memento_mori' in ach and 'PRE_UNLOCK_DESCRIPTION_IDS' in ach, 'Memento Mori pre-unlock condition visibility missing') must('totalDeathCount' in ach, 'persistent total death counter missing') # Current UI/achievement/colony changes. placement_preview = text('js/placement_preview_system.js') placement_log = text('js/world_placement_log.js') colony = text('js/colony_situation_system.js') components = text('css/components.css') must('achievementToastCondition' in index and 'achievement-toast-condition' in components, 'achievement unlock toast condition line missing') must('if (dom.toastCondition) dom.toastCondition.textContent = definition.description || "";' in ach, 'achievement toast does not render the completion condition') must('lively_making' in ach and 'PRE_UNLOCK_DESCRIPTION_IDS' in ach and 'manualTarinaiAddedCount' in ach, 'Lively Making persistent/pre-unlock progress is missing') must(r'title: "\u7e41\u6b96\u30fb\u4eba\u53e3"' in ach and r'title: "\u64cd\u4f5c"' in ach and 'id: "other"' in ach, 'six-category achievement reorganization missing') must('num(rect.bottom) > (worldRef.h || 0)' in placement_preview, 'placement preview still reserves an artificial bottom padding') must('rect.bottom > this.h' in placement_log, 'confirmed placement still reserves an artificial bottom padding') must('Math.max(5, Math.ceil(m.n * 0.10))' in colony, 'crisis mortality threshold is still too loose') must('criticalRatio' in colony and 'crisisMortalitySignal && crisisSystemicStress' in colony and 'crisisCollapseSignal' in colony, 'crisis no longer requires compound severe conditions') must('!m.populationIncreasing' in colony, 'crisis population-growth exclusion was lost') print('[OK] UI/limits/input/zoom/disease/grass/summer/achievement/placement/crisis regression audit passed')