Files
NumberSwipe_New/campaign-200-r2/tools/score-page.mjs
T

2 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import fs from 'node:fs';const old=JSON.parse(fs.readFileSync('reports/old-scores.json')),pack=JSON.parse(fs.readFileSync('levels.json')),notes=JSON.parse(fs.readFileSync('design.json')),anchors=JSON.parse(fs.readFileSync('reports/player-anchors.json'));const current=notes.map(n=>({order:n.level.order,position:Math.ceil(n.level.order/10)+'-'+((n.level.order-1)%10+1),title:n.level.title,geometry:n.level.geometry,features:n.level.features,nodes:n.level.cells.length,...n.objective,oldOrder:n.oldOrder||null,playerBand:n.playerBand||null,estimatedBand:n.level.estimatedBand,source:n.source,tutorial:n.level.tutorial||null}));const previous=old.rows.map(n=>({...n,order:n.oldOrder,position:n.oldPosition,nodes:n.nodeCount,playerBand:anchors.needHint.includes(n.oldOrder)?'需要提示':anchors.moderate.includes(n.oldOrder)?'略难':anchors.likedUnknownStart.includes(n.oldOrder)?'喜欢的未知起点':null}));const series=(rows,col)=>`<div class="chart">${rows.map(r=>`<button title="${r.position} · ${r.title} · ${r.score}" data-order="${r.order}" style="height:${Math.max(2,r.score)}%;background:${r.tutorial?'#F5A524':col}"></button>`).join('')}</div>`;const bands=Array.from({length:10},(_,i)=>{const a=previous.slice(i*20,i*20+20),b=current.slice(i*20,i*20+20),mean=a=>Math.round(a.reduce((s,r)=>s+r.score,0)/a.length*10)/10;return `<tr><td>${i*20+1}–${i*20+20}</td><td>${mean(a)}</td><td>${mean(b)}</td></tr>`;}).join('');const html=`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>200关评分与重排对照</title><style>body{max-width:1120px;margin:24px auto;padding:0 16px;font:15px/1.65 system-ui;background:#f2f2f6;color:#23243a}a{color:#6558f5}section{background:white;padding:18px;border-radius:16px;margin:14px 0}.chart{height:150px;display:flex;align-items:end;gap:2px;overflow:auto}.chart button{min-width:3px;flex:1;border:0;padding:0;cursor:pointer}table{width:100%;border-collapse:collapse;font-size:13px}th,td{padding:7px;border-bottom:1px solid #e1e2ea;text-align:left}small{color:#6b6d80}select,input{font:inherit;padding:8px;max-width:100%;margin:4px}.scroll{overflow:auto}details{margin:12px 0}code{background:#f2f2f6}h1{font-size:25px}</style><h1>200关评分修订版:先测量,再排关</h1><p><a href="index.html">试玩修订版</a> · <a href="DESIGN.html">作者解与必要性反例</a></p><section><h2>评分方法与边界</h2><p>采用相同、可复现的推理模型评估新旧关卡:先应用共边、数字、颜色、奇偶、方向和门规则,再排除断区、孤格、额外终点及数字距离不足;只对剩余错误路线向后探查最多8步,单分支最多12000状态。记录真实决策点、非局部分支、错误可存活深度、探查量及连续被迫步数。</p><details><summary>展开代理分公式</summary><p><code>分数=2log2(1+格数/3)+5log2(1+决策点)+4log2(1+存活≥3步的错分支)+6×平均错路深度+3log2(1+探查状态/10)−5×被迫步占比</code>,四舍五入并限制在0–100。被8步/状态预算截断的深度是下界;原始指标同时保留,不以随机搜索顺序或仅格数评分。</p></details><p><b>它不是玩家通关率,也不能保证“大部分玩家都会卡住”。</b>本次用户反馈已证明个体差异:旧144关代理36分但需要提示,旧149关65分却略难。因此已试玩标签单独保存,优先于代理分;新关分档均标作候选,不伪称实测。</p></section><section><h2>同模型的新旧曲线</h2><p>旧版</p>${series(previous,'#a3a5b5')}<p>修订版;橙色为首次机制教学</p>${series(current,'#6558f5')}<table><thead><tr><th>关段</th><th>旧版均分</th><th>修订均分</th></tr></thead><tbody>${bands}</tbody></table></section><section><h2>个人反馈锚点</h2><p>需提示(想很久、点击指路才流畅):8-3/73、8-6/76、8-7/77、8-10/80、9-5/85、9-8/88、15-4/144。略难:15-9/149、16-8/158。喜欢的无起点:12-1/111、14-6/136。</p><p>原73→新38,76→48,77→58,85→78;111→65,136→105;149→109,158→119。旧80、88、144的六边组合主题通过宽盘面新关保留,原条带几何不再使用。</p><p>100关后:80个略难候选/个人略难参照,17个深想候选,3个未知起点;未知起点替代挑战名额。全200关无起点共5个,相隔至少20关。</p></section><section><h2>逐关评分与来源</h2><select id="version"><option value="new">修订版</option><option value="old">旧200关</option></select><select id="sort"><option value="order">按关序</option><option value="score">按代理分从难到易</option><option value="nodes">按格数</option></select><input id="filter" placeholder="搜索关号、玩法、个人标签"><div class="scroll"><table><thead><tr><th>关</th><th>标题/玩法</th><th>代理分</th><th>格</th><th>决策</th><th>非局部分支</th><th>错路深度</th><th>标签/来源</th></tr></thead><tbody id="rows"></tbody></table></div></section><script>const DATA=${JSON.stringify({new:current,old:previous}).replaceAll('<','\u003c')};function render(){let a=[...DATA[document.getElementById('version').value]],q=document.getElementById('filter').value.trim();if(q)a=a.filter(r=>JSON.stringify(r).includes(q));const sort=document.getElementById('sort').value;a.sort((x,y)=>sort==='score'?y.score-x.score:sort==='nodes'?y.nodes-x.nodes:x.order-y.order);document.getElementById('rows').innerHTML=a.map(r=>'<tr><td>'+r.position+'<small> / '+r.order+'</small></td><td>'+r.title+'<small>'+r.geometry+' '+r.features.join('+')+'</small></td><td>'+r.score+'</td><td>'+r.nodes+'</td><td>'+r.decisions+'</td><td>'+r.nonlocalBranches+'</td><td>'+r.meanWrongDepth+(r.capped?' ≥预算':'')+'</td><td>'+(r.playerBand||r.estimatedBand||'')+'<small>'+(r.source||'旧版本')+'</small></td></tr>').join('');}for(const id of ['version','sort','filter'])document.getElementById(id).addEventListener('input',render);document.querySelectorAll('[data-order]').forEach(b=>b.onclick=()=>{document.getElementById('filter').value=String(b.dataset.order);render();document.getElementById('filter').scrollIntoView({behavior:'smooth'});});render();</script></html>`;fs.writeFileSync('SCORES.html',html);fs.writeFileSync('DIFFICULTY.html',html);fs.writeFileSync('reports/new-scores.json',JSON.stringify({method:'reasoner-v1',rows:current},null,2));console.log('Built score comparison and per-level ranking.');