2 lines
6.3 KiB
JavaScript
2 lines
6.3 KiB
JavaScript
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.');
|