纠正仓库内容为衍生项目,标明 campaign-200-r2 最新版
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
root=Path(r'G:\项目\NumberSwipe-Variants')
|
||||
p=root/'tools/definitions.mjs';s=p.read_text(encoding='utf-8-sig').replace('四种拐角跨区域配合','横竖与多个拐角跨区域配合');p.write_text(s,encoding='utf-8')
|
||||
for name in ['levels.json','design.json']:
|
||||
p=root/'modes/direction'/name;data=json.loads(p.read_text(encoding='utf-8-sig'))
|
||||
if name=='levels.json':data['levels'][6]['title']='横竖与多个拐角跨区域配合'
|
||||
else:data[6]['level']['title']='横竖与多个拐角跨区域配合'
|
||||
p.write_text(json.dumps(data,ensure_ascii=False,indent=2),encoding='utf-8')
|
||||
@@ -0,0 +1,2 @@
|
||||
import {solve} from './engine-template.mjs';
|
||||
for(const n of [14,16,18,19]){let cells=[];for(let q=0;cells.length<n;q++)for(let r=0;r<3&&cells.length<n;r++)cells.push({id:'c'+cells.length,q,r});const start=Date.now();try{const p=solve({mode:'base',geometry:'hex',cells},{limit:60001,nodeLimit:8000000});console.log(n,p.length,Date.now()-start);}catch(e){console.log(n,e.message,Date.now()-start);}}
|
||||
@@ -0,0 +1,23 @@
|
||||
from pathlib import Path
|
||||
root=Path(r'G:\项目\NumberSwipe-Variants')
|
||||
p=root/'tests/rules.test.mjs';s=p.read_text(encoding='utf-8-sig')
|
||||
s=s.replace('canAppend,violations,hint,assertStructure}', 'canAppend,violations,hint,assertStructure,doorOpen,directionAt}')
|
||||
s=s.replace("const straight=(a.x===b.x&&b.x===d.x)||(a.y===b.y&&b.y===d.y);if(straight!==(c.direction==='straight'))return false;", "const side=x=>x.x<b.x?'l':x.x>b.x?'r':x.y<b.y?'u':'d';if([side(a),side(d)].sort().join('')!==c.direction)return false;")
|
||||
s=s.replace(" for(const g of level.gates||[])", " for(const w of level.walls||[])for(let i=1;i<p.length;i++)if(w.includes(p[i-1])&&w.includes(p[i]))return false;\n for(const door of level.doors||[])for(let i=1;i<p.length;i++)if(door.edge.includes(p[i-1])&&door.edge.includes(p[i])&&!(Boolean(door.initialOpen)!==Boolean(i%2)))return false;\n for(const g of level.gates||[])")
|
||||
s=s.replace('all 56 author','all 40 author').replace('assert.equal(n,56)','assert.equal(n,40)')
|
||||
a=s.index("test('relative difference");b=s.index("test('alternating classes",a)
|
||||
s=s[:a]+'''test('six exact channel orientations are reversible and reject other ports',()=>{
|
||||
const coords={l:[-1,0],r:[1,0],u:[0,-1],d:[0,1]};for(const kind of ['lr','du','ru','dr','dl','lu']){const cells=[{id:'center',x:0,y:0,direction:kind},...Object.entries(coords).map(([id,[x,y]])=>({id,x,y}))];const l={mode:'direction',geometry:'square',cells};const [a,b]=kind;assert.equal(violations(l,[a,'center',b],{complete:false}).length,0);assert.equal(violations(l,[b,'center',a],{complete:false}).length,0);assert.equal(violations(l,[a,'center'],{complete:false}).length,0);const wrong=Object.keys(coords).find(k=>!kind.includes(k));assert.ok(violations(l,[a,'center',wrong],{complete:false}).length);assert.ok(violations(l,['center',a],{complete:false}).length);assert.ok(violations(l,[a,'center'],{complete:true}).length);}
|
||||
});
|
||||
test('permanent walls block both directions and never react to visited count',()=>{const l={mode:'walls',geometry:'square',cells:[{id:'a',x:0,y:0},{id:'b',x:1,y:0},{id:'c',x:0,y:1},{id:'d',x:1,y:1}],walls:[['a','b']]};assert.equal(canAppend(l,['a'],'b'),false);assert.equal(canAppend(l,['b'],'a'),false);assert.equal(canAppend(l,['c','d','b'],'a'),false);assert.ok(violations(l,['a','b']).length);});
|
||||
test('step door starts closed, includes start, toggles and restores after undo',()=>{const d={edge:['b','d'],initialOpen:false};assert.deepEqual([0,1,2,3,4].map(t=>doorOpen(d,t)),[false,true,false,true,false]);const l={mode:'rhythm-door',geometry:'square',cells:[{id:'a',x:0,y:0},{id:'b',x:1,y:0},{id:'c',x:0,y:1},{id:'d',x:1,y:1}],doors:[d]};assert.equal(canAppend(l,['a','b'],'d'),false);assert.equal(canAppend(l,['a','c','d'],'b'),true);assert.equal(canAppend(l,['b'],'d'),true);assert.equal(doorOpen(d,0),false);});
|
||||
test('dynamic connectivity retains future door opportunities and static walls cannot replace it',()=>{const pack=packs.find(p=>p.mode==='rhythm-door');for(const l of pack.levels){assert.deepEqual(solve(l,{prune:true}),solve(l,{prune:false}));const copy=structuredClone(l);copy.walls=copy.doors.map(d=>d.edge);delete copy.doors;assert.notEqual(solve(copy).length,1);}});
|
||||
test('hex fusion needs each permanent wall and the alternating rule',()=>{const pack=packs.find(p=>p.mode==='hex-alternation');assert.ok(pack.levels[6].cells.some(c=>c.r===2));for(const l of pack.levels){assert.ok(l.walls.length);assert.ok(solve(l,{omit:'alternation'}).length>=2);for(let i=0;i<l.walls.length;i++)assert.ok(solve(l,{omit:'wall:'+i}).length>=2);}});
|
||||
''' +s[b:]
|
||||
p.write_text(s,encoding='utf-8')
|
||||
p=root/'tests/offline-ui.test.mjs';s=p.read_text(encoding='utf-8-sig');a=s.index("test('fast drag");s=s[:a]+'''test('fast drag samples crossed cells and preserves prefix after release',()=>{const ui=boot('walls'),p=solve(JSON.parse(fs.readFileSync(new URL('../modes/walls/levels.json',import.meta.url),'utf8')).levels[0])[0];const start=ui.snapshot().centers[p[0]],next=ui.snapshot().centers[p[1]];ui.canvas.handlers.pointerdown({preventDefault(){},pointerId:1,clientX:start[0],clientY:start[1]});ui.canvas.handlers.pointermove({preventDefault(){},clientX:next[0],clientY:next[1]});ui.canvas.handlers.pointerup({});assert.deepEqual(ui.snapshot().path,p.slice(0,2));});
|
||||
test('step door UI shows current and next state and undo/restart restore it',()=>{const ui=boot('rhythm-door'),l=JSON.parse(fs.readFileSync(new URL('../modes/rhythm-door/levels.json',import.meta.url),'utf8')).levels[0],p=solve(l)[0];assert.deepEqual(ui.snapshot().doors,[false]);assert.match(ui.get('constraints').children[0].textContent,/门关.*就开/);ui.press(p[0]);assert.deepEqual(ui.snapshot().doors,[true]);assert.match(ui.get('constraints').children[0].textContent,/门开.*就关/);ui.press(p[1]);assert.deepEqual(ui.snapshot().doors,[false]);ui.get('undo').click();assert.deepEqual(ui.snapshot().doors,[true]);ui.get('restart').click();assert.deepEqual(ui.snapshot().doors,[false]);});
|
||||
test('alternation keeps blue/orange fill after visits and uses clear colour legend',()=>{for(const mode of ['block-alternation','hex-alternation']){const ui=boot(mode),l=JSON.parse(fs.readFileSync(new URL('../modes/'+mode+'/levels.json',import.meta.url),'utf8')).levels[0],p=solve(l)[0];const colours=()=>ui.draws.filter(d=>d.kind==='fill').map(d=>d.color);assert.ok(colours().includes('#64acf3'));assert.ok(colours().includes('#ffae55'));ui.press(p[0]);ui.press(p[1]);assert.ok(colours().includes('#64acf3'));assert.ok(colours().includes('#ffae55'));assert.match(ui.get('constraints').children[0].textContent,/蓝.*橙.*换颜色/);}});
|
||||
test('retired modes are absent from the active launcher',()=>{const html=fs.readFileSync(new URL('../index.html',import.meta.url),'utf8');for(const id of ['relative','order','window','gates'])assert.ok(!html.includes('modes/'+id+'/'));assert.ok(html.includes('modes/walls/'));assert.ok(html.includes('modes/rhythm-door/'));});
|
||||
''';p.write_text(s,encoding='utf-8')
|
||||
p=root/'tools/validate.mjs';s=p.read_text(encoding='utf-8-sig').replace('7 个玩法','5 个玩法');p.write_text(s,encoding='utf-8')
|
||||
@@ -0,0 +1,30 @@
|
||||
from pathlib import Path
|
||||
root=Path(r'G:\项目\NumberSwipe-Variants')
|
||||
p=root/'tools/ui-template.mjs';s=p.read_text(encoding='utf-8-sig')
|
||||
s=s.replace('canAppend,hint}', 'canAppend,hint,doorOpen,directionNames}')
|
||||
s=s.replace("for(const c of level.cells)if(c.direction)add(c.direction==='straight'?'↔ 必须直行':'↱ 必须转弯',[c.id]);", "for(const c of level.cells)if(c.direction)add(directionNames[c.direction]+' · 可反向',[c.id]);")
|
||||
s=s.replace("add('○ → △ → ○ · 每步换一类',[]);", "add('🔵 蓝 → 🟠 橙 → 🔵 蓝 · 每步换颜色',[]);")
|
||||
s=s.replace(" for(let i=0;i<(level.gates||[]).length;i++)add", " for(let i=0;i<(level.walls||[]).length;i++)add('黑黄墙 · 永久封闭',level.walls[i]);\n for(const d of level.doors||[])add(doorOpen(d,path.length)?'门开 · 再落一格就关':'门关 · 再落一格就开',d.edge);\n for(let i=0;i<(level.gates||[]).length;i++)add")
|
||||
s=s.replace("ctx.fillStyle=badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4':'#f0f0f6';", "ctx.fillStyle=c.category==='circle'?'#64acf3':c.category==='triangle'?'#ffae55':badIds.has(c.id)?'#ffe0dc':visited?'#ddd8ff':'#f0f0f6';")
|
||||
s=s.replace(" for(let i=0;i<(level.gates||[]).length;i++){const gate=", " for(const edge of level.walls||[])drawBarrier(edge,'wall');\n for(const d of level.doors||[])drawBarrier(d.edge,doorOpen(d,path.length)?'open':'closed');\n for(let i=0;i<(level.gates||[]).length;i++){const gate=")
|
||||
s=s.replace("if(c.direction)symbol=c.direction==='straight'?'↔':'↱';", "if(c.direction)symbol='';")
|
||||
s=s.replace(" ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=", " if(c.direction)drawChannel(c.direction,x,y-(step?font*.72:0),Math.min(unit*.28,step?font*.4:20));\n ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=")
|
||||
s=s.replace(" if(bad.length)setStatus", " if(level.doors?.length)renderConstraints();\n if(bad.length)setStatus")
|
||||
a=s.index('function inside(')
|
||||
s=s[:a]+'''function drawChannel(kind,x,y,r){const vector={l:[-1,0],r:[1,0],u:[0,-1],d:[0,1]};const [a,b]=kind.split('').map(k=>vector[k]);ctx.beginPath();ctx.moveTo(x+a[0]*r,y+a[1]*r);ctx.lineTo(x,y);ctx.lineTo(x+b[0]*r,y+b[1]*r);ctx.lineCap='round';ctx.lineJoin='round';ctx.lineWidth=9;ctx.strokeStyle='#c1daf7';ctx.stroke();ctx.lineWidth=4;ctx.strokeStyle='#15497b';ctx.stroke();}
|
||||
function drawBarrier(edge,kind){const left=polygons.filter(p=>p.id===edge[0]),right=polygons.filter(p=>p.id===edge[1]);let common=null;for(const a of left)for(const b of right)for(let i=0;i<a.p.length;i++)for(let j=0;j<b.p.length;j++){const p=a.p[i],q=a.p[(i+1)%a.p.length],r=b.p[j],t=b.p[(j+1)%b.p.length];if(Math.hypot(p[0]-t[0],p[1]-t[1])<.01&&Math.hypot(q[0]-r[0],q[1]-r[1])<.01)common=[p,q];}if(!common)return;const [a,b]=common;const point=f=>[a[0]+(b[0]-a[0])*f,a[1]+(b[1]-a[1])*f];ctx.lineCap='butt';ctx.lineWidth=9;ctx.strokeStyle=kind==='open'?'#23895f':kind==='wall'?'#272638':'#aa4d25';ctx.beginPath();if(kind==='open'){ctx.moveTo(...point(.05));ctx.lineTo(...point(.26));ctx.moveTo(...point(.74));ctx.lineTo(...point(.95));}else{ctx.moveTo(...point(.05));ctx.lineTo(...point(.95));}ctx.stroke();if(kind==='wall'){ctx.lineWidth=4;ctx.strokeStyle='#ffc542';ctx.setLineDash([5,4]);ctx.beginPath();ctx.moveTo(...point(.05));ctx.lineTo(...point(.95));ctx.stroke();ctx.setLineDash([]);}if(kind!=='wall'){const [x,y]=point(.5);ctx.font='700 11px system-ui';ctx.textAlign='center';ctx.fillStyle=kind==='open'?'#12623f':'#993b1e';ctx.fillText(kind==='open'?'开':'关',x+10,y-8);}}
|
||||
''' +s[a:]
|
||||
s=s.replace("markError(id,path.length?'此处类别相同或门尚未打开。':'请从数字 1 开始。');", "markError(id,path.length?(level.walls?.some(e=>e.includes(path.at(-1))&&e.includes(id))?'这是永久墙,不能穿过。':level.doors?.some(d=>d.edge.includes(path.at(-1))&&d.edge.includes(id))?'门现在关闭;绕行落格后会切换。':'同色格不能连续经过,请换另一种颜色。'):'请从数字 1 开始。');")
|
||||
s=s.replace("openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell))", "openedGates:(level.gates||[]).map(g=>path.includes(g.switchCell)),doors:(level.doors||[]).map(d=>doorOpen(d,path.length))")
|
||||
p.write_text(s,encoding='utf-8')
|
||||
p=root/'tools/build.mjs';s=p.read_text(encoding='utf-8-sig').replace('Built 7 standalone pages + index.','Built 5 standalone pages + index.').replace('一笔路径,七种推理','一笔路径,五种推理').replace('56 个机制验证候选关','40 个机制验证候选关').replace('门和类别限制会阻止移动','永久墙、关闭的门和同色移动会阻止移动')
|
||||
s=s.replace("${esc(d.rule)}</p></a>", "${esc(d.rule)}</p></a>")
|
||||
s=s.replace('</div><footer>40 个', '</div><details><summary>设计说明与答案图</summary>${definitions.map(d=>`<p><a href="modes/${d.id}/DESIGN.html">${d.title} · 设计评审</a></p>`).join(\'\')}</details><footer>40 个')
|
||||
p.write_text(s,encoding='utf-8')
|
||||
p=root/'tools/design-pages.mjs';s=p.read_text(encoding='utf-8-sig')
|
||||
s=s.replace("import {units,constraints}", "import {units,constraints,directionNames}")
|
||||
s=s.replace("c.category==='circle'?'#e4f3ff':c.category==='triangle'?'#fff0d4'", "c.category==='circle'?'#64acf3':c.category==='triangle'?'#ffae55'")
|
||||
s=s.replace("c.direction==='straight'?'↔':c.direction==='turn'?'↱':''", "c.direction?({lr:'─',du:'│',ru:'└',dr:'┌',dl:'┐',lu:'┘'})[c.direction]:''")
|
||||
s=s.replace("for(let i=0;i<(l.gates||[]).length;i++){", "for(const item of [...(l.walls||[]).map(edge=>({edge,kind:'wall'})),...(l.doors||[]).map(d=>({edge:d.edge,kind:'door'}))]){const [a,b]=item.edge.map(id=>tr(centers.get(id))),x=(a[0]+b[0])/2,y=(a[1]+b[1])/2,angle=Math.atan2(b[1]-a[1],b[0]-a[0])*180/Math.PI;svg+=`<line x1=\"${x}\" y1=\"${y-s*.38}\" x2=\"${x}\" y2=\"${y+s*.38}\" stroke=\"${item.kind==='wall'?'#272638':'#aa4d25'}\" stroke-width=\"8\" transform=\"rotate(${angle} ${x} ${y})\"/>`;if(item.kind==='wall')svg+=`<line x1=\"${x}\" y1=\"${y-s*.38}\" x2=\"${x}\" y2=\"${y+s*.38}\" stroke=\"#ffc542\" stroke-width=\"4\" stroke-dasharray=\"5 4\" transform=\"rotate(${angle} ${x} ${y})\"/>`;else svg+=`<text x=\"${x+8}\" y=\"${y-8}\" font-size=\"12\">步进门</text>`;}for(let i=0;i<(l.gates||[]).length;i++){")
|
||||
s=s.replace('Generated 56 original boards, 56 solutions and 7 design review pages.','Generated 40 original boards, 40 solutions and 5 design review pages.')
|
||||
p.write_text(s,encoding='utf-8')
|
||||
@@ -0,0 +1,42 @@
|
||||
from pathlib import Path
|
||||
root=Path(r'G:\项目\NumberSwipe-Variants')
|
||||
p=root/'tools/engine-template.mjs'
|
||||
s=p.read_text(encoding='utf-8-sig')
|
||||
s=s.replace(" for(let i=0;i<(level.gates||[]).length;i++)result.push", " for(let i=0;i<(level.walls||[]).length;i++)result.push({key:'wall:'+i,kind:'wall',edge:level.walls[i]});\n for(let i=0;i<(level.doors||[]).length;i++)result.push({key:'door:'+i,kind:'door',...level.doors[i]});\n for(let i=0;i<(level.gates||[]).length;i++)result.push")
|
||||
a=s.index('export function directionAt(');b=s.index('export function violations(',a)
|
||||
s=s[:a]+'''export function directionAt(level,path,k){
|
||||
if(k===0||k===path.length-1)return null;
|
||||
const [a,b,c]=[path[k-1],path[k],path[k+1]].map(id=>level.cells.find(x=>x.id===id));
|
||||
const side=(x)=>x.x<b.x?'l':x.x>b.x?'r':x.y<b.y?'u':'d';
|
||||
return [side(a),side(c)].sort().join('');
|
||||
}
|
||||
export function doorOpen(door,visitedCount){return Boolean(door.initialOpen)!==Boolean(visitedCount%2);}
|
||||
export const directionNames={lr:'左右直行',du:'上下直行',ru:'上与右拐角',dr:'右与下拐角',dl:'下与左拐角',lu:'左与上拐角'};
|
||||
export function edgeBlocked(level,path,a,b,{omit=null}={}){
|
||||
for(let i=0;i<(level.walls||[]).length;i++)if(omit!=='wall:'+i&&level.walls[i].includes(a)&&level.walls[i].includes(b))return true;
|
||||
for(let i=0;i<(level.doors||[]).length;i++)if(omit!=='door:'+i&&level.doors[i].edge.includes(a)&&level.doors[i].edge.includes(b)&&!doorOpen(level.doors[i],path.length))return true;
|
||||
return false;
|
||||
}
|
||||
''' +s[b:]
|
||||
s=s.replace(" else if(r.kind==='gate')for", " else if(r.kind==='wall'||r.kind==='door')for(let i=1;i<path.length;i++){if(r.edge.includes(path[i-1])&&r.edge.includes(path[i])&&(r.kind==='wall'||!doorOpen(r,i))){fail=true;where=r.edge;}}\n else if(r.kind==='gate')for")
|
||||
s=s.replace("r.kind==='gate'?'开关尚未打开此门':'类别没有交替'", "r.kind==='wall'?'永久墙不能穿过':r.kind==='door'?'这一步门是关闭的':r.kind==='gate'?'开关尚未打开此门':'蓝橙颜色必须交替'")
|
||||
s=s.replace(" const last=path.at(-1);if(!g.adj", " const last=path.at(-1);if(edgeBlocked(level,path,last,id))return false;if(!g.adj")
|
||||
s=s.replace(" const adj=g.adj.map((a,i)=>a.filter(j=>!alternating||g.cells[i].category!==g.cells[j].category));", " const adj=g.adj.map((a,i)=>a.filter(j=>(!alternating||g.cells[i].category!==g.cells[j].category)&&!active.some(r=>r.kind==='wall'&&r.edge.includes(g.cells[i].id)&&r.edge.includes(g.cells[j].id))));")
|
||||
s=s.replace("let blocked=false;for(const r of active)", "let blocked=edgeBlocked(level,path,g.cells[i].id,g.cells[j].id,{omit});for(const r of active)")
|
||||
s=s.replace("!['straight','turn'].includes(r.value)", "!Object.keys(directionNames).includes(r.value)")
|
||||
s=s.replace(" if(r.kind==='gate'&&", " if((r.kind==='wall'||r.kind==='door')&&(r.edge.length!==2||!g.adj[g.ids.get(r.edge[0])].includes(g.ids.get(r.edge[1]))))throw Error('墙或门不在共边');\n if(r.kind==='gate'&&")
|
||||
s=s.replace(" const pairs=[];", " if((level.doors||[]).length>1)throw Error('步进门只允许一道门');\n const physicalEdges=[...(level.walls||[]),...(level.doors||[]).map(d=>d.edge)].map(e=>[...e].sort().join('|'));if(new Set(physicalEdges).size!==physicalEdges.length)throw Error('墙门共边重复');\n const pairs=[];")
|
||||
p.write_text(s,encoding='utf-8')
|
||||
p=root/'tools/generate.mjs';s=p.read_text(encoding='utf-8-sig')
|
||||
a=s.index(" }else if(def.id==='direction'){");b=s.index(" }else if(def.id.includes('alternation'))",a)
|
||||
s=s[:a]+''' }else if(def.id==='direction'){
|
||||
const kinds=['lr','du','ru','dr','dl','lu'];const pool={};for(const kind of kinds)pool[kind]=[];
|
||||
for(let i=1;i<n-1;i++)pool[directionAt(level,author,i)].push(author[i]);
|
||||
let pattern=index===0?['lr']:index===1?['corner']:index===2?['du']:index===3?['corner','corner']:index<6?['lr','du','corner']:['lr','du','corner','corner'];
|
||||
const selected=[];for(const requested of pattern){const available=requested==='corner'?kinds.slice(2).filter(k=>pool[k].length):[requested];if(!available.length)return null;const kind=pick(available);if(!pool[kind].length)return null;const id=pick(pool[kind]);pool[kind].splice(pool[kind].indexOf(id),1);level.cells.find(c=>c.id===id).direction=kind;selected.push(kind);}
|
||||
if(index===3&&selected[0]===selected[1])return null;
|
||||
''' +s[b:]
|
||||
s=s.replace(" }else if(def.id.includes('alternation'))for(let i=0;i<n;i++)level.cells.find(c=>c.id===author[i]).category=i%2?'triangle':'circle';", " }else if(def.id.includes('alternation')){for(let i=0;i<n;i++)level.cells.find(c=>c.id===author[i]).category=i%2?'triangle':'circle';if(def.geometry==='hex'){const g=graph(level);let edges=[];for(let a=0;a<n;a++)for(const b of g.adj[a])if(a<b&&Math.abs(author.indexOf(g.cells[a].id)-author.indexOf(g.cells[b].id))!==1&&g.cells[a].category!==g.cells[b].category)edges.push([g.cells[a].id,g.cells[b].id]);const count=index<3?1:index<6?2:3;if(edges.length<count)return null;level.walls=shuffle(edges).slice(0,count);}}\n else if(def.id==='walls'){const g=graph(level);let edges=[];for(let a=0;a<n;a++)for(const b of g.adj[a])if(a<b&&Math.abs(author.indexOf(g.cells[a].id)-author.indexOf(g.cells[b].id))!==1)edges.push([g.cells[a].id,g.cells[b].id]);const count=index<2?1:index<4?2:index<6?3:4;if(edges.length<count)return null;level.walls=shuffle(edges).slice(0,count);}\n else if(def.id==='rhythm-door'){const eligible=[];for(let i=0;i<n-1;i+=2)if(index===0?i===0:index<4?i>=2:i>=Math.floor(n/3))eligible.push(i);if(!eligible.length)return null;const e=pick(eligible);level.doors=[{edge:[at(e),at(e+1)],initialOpen:false}];}")
|
||||
s=s.replace("r.value==='straight'?'直行':'转弯'", "({lr:'左右直行',du:'上下直行',ru:'上与右',dr:'右与下',dl:'下与左',lu:'左与上'})[r.value]")
|
||||
s=s.replace("if(r.kind==='gate')return", "if(r.kind==='wall')return `永久墙 ${r.edge.join('—')} 断开表面邻接,需绕行`;if(r.kind==='door'){const crossing=author.findIndex((id,i)=>i>0&&r.edge.includes(id)&&r.edge.includes(author[i-1]));return `第 ${crossing} 格后门开,从 ${author[crossing-1]} 走向 ${author[crossing]},落格后门关;空盘门关、起步也切换`;}if(r.kind==='gate')return")
|
||||
p.write_text(s,encoding='utf-8')
|
||||
@@ -0,0 +1,4 @@
|
||||
from pathlib import Path
|
||||
p=Path(r'G:\项目\NumberSwipe-Variants\tools\generate.mjs');s=p.read_text(encoding='utf-8-sig');a=s.index('function hex(index){');b=s.index('function blocks(',a)
|
||||
s=s[:a]+'''function hex(index){const count=[6,8,11,12,14,16,18,19][index];let cells=[];const rows=index>=4?3:2;for(let q=0;cells.length<count;q++)for(let r=0;r<rows&&cells.length<count;r++)cells.push({id:'c'+cells.length,q,r});if(index===3){cells[5].r=2;cells[5].q=1;}return cells;}
|
||||
''' +s[b:];p.write_text(s,encoding='utf-8')
|
||||
Reference in New Issue
Block a user