23 lines
4.8 KiB
Python
23 lines
4.8 KiB
Python
from pathlib import Path
|
|
root=Path(r'G:\项目\NumberSwipe-Variants\campaign-50')
|
|
p=root/'ui.mjs';s=p.read_text(encoding='utf-8')
|
|
s=s.replace("let toastTimer=null;", "let toastTimer=null;let channelViews=[];")
|
|
s=s.replace("const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));", "const bad=violations(level,path),badIds=new Set(bad.flatMap(b=>b.cells));channelViews=[];")
|
|
s=s.replace("if(c.direction)drawChannel(c.direction,x,y-(value?unit*.23:0),Math.min(unit*.25,value?unit*.14:22),foreground);", "if(c.direction){const radius=Math.min(unit*.26,28),state=directionState(c,badIds);drawDirectionPath(c,x,y);drawChannel(c.direction,x,y,radius,state);channelViews.push({cell:c.id,kind:c.direction,center:[x,y],radius,state});}")
|
|
s=s.replace("if(value){ctx.font='800 '+font+'px '+BOARD_FONT;ctx.fillText", "if(value&&c.direction){const size=Math.max(10,Math.min(18,unit*.22));ctx.fillStyle=visited||start?'#FFFFFF':UI_THEME.cell;roundedRect(x+unit*.105,y-unit*.38,unit*.28,unit*.28,unit*.07);ctx.fill();ctx.fillStyle=UI_THEME.ink;ctx.font='800 '+size+'px '+BOARD_FONT;ctx.fillText(String(value),x+unit*.245,y-unit*.24);}\n else if(value){ctx.font='800 '+font+'px '+BOARD_FONT;ctx.fillText")
|
|
a=s.index('function drawChannel(');b=s.index('function drawBarrier(',a)
|
|
s=s[:a]+'''function sideOf(c,id){const a=level.cells.find(a=>a.id===id);return a.x<c.x?'l':a.x>c.x?'r':a.y<c.y?'u':'d';}
|
|
function directionState(c,badIds=new Set()){const k=path.indexOf(c.id);if(k<0)return 'idle';if(k===0||badIds.has(c.id))return 'error';const entry=sideOf(c,path[k-1]);if(!c.direction.includes(entry))return 'error';if(k<path.length-1){const exit=sideOf(c,path[k+1]);if([entry,exit].sort().join('')!==c.direction)return 'error';}return 'correct';}
|
|
function drawDirectionPath(c,x,y){const k=path.indexOf(c.id);if(k<0)return;const vectors={l:[-1,0],r:[1,0],u:[0,-1],d:[0,1]};ctx.beginPath();if(k>0){const [dx,dy]=vectors[sideOf(c,path[k-1])];ctx.moveTo(x+dx*unit*.44,y+dy*unit*.44);ctx.lineTo(x,y);}else ctx.moveTo(x,y);if(k<path.length-1){const [dx,dy]=vectors[sideOf(c,path[k+1])];ctx.lineTo(x+dx*unit*.44,y+dy*unit*.44);}ctx.lineCap='round';ctx.lineJoin='round';ctx.lineWidth=unit*.14;ctx.strokeStyle='#BEB8FF';ctx.stroke();}
|
|
function drawChannel(kind,x,y,r,state='idle'){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=10;ctx.strokeStyle=state==='correct'?'#C8C2FF':state==='error'?'#FFD5D7':'#DFE1EC';ctx.stroke();ctx.lineWidth=5;ctx.strokeStyle=state==='correct'?'#FFFFFF':state==='error'?'#E5484D':UI_THEME.ink;ctx.stroke();}
|
|
''' +s[b:]
|
|
s=s.replace("records:records.length,screen:", "records:records.length,directionMarkers:channelViews,screen:")
|
|
p.write_text(s,encoding='utf-8')
|
|
p=root/'tests/ui.test.mjs';s=p.read_text(encoding='utf-8-sig').replace("import {solve}","import {solve,graph}")
|
|
s=s.replace('append(e){this.children.push(e);}', 'append(...e){this.children.push(...e);}')
|
|
s=s.replace("const window={devicePixelRatio:1", "get('screen-play').className='screen hidden';get('screen-select').className='screen';const window={devicePixelRatio:1")
|
|
s+='''
|
|
test('direction glyph stays centered and same size on landing, exit, undo and errors',()=>{const ui=boot();ui.select(11);const l=pack.levels[10],p=solve(l)[0],c=l.cells.find(c=>c.direction),at=p.indexOf(c.id);const initial=ui.snapshot().directionMarkers.find(m=>m.cell===c.id);assert.equal(initial.state,'idle');for(const id of p.slice(0,at+1))ui.press(id);let active=ui.snapshot().directionMarkers.find(m=>m.cell===c.id);assert.deepEqual(active.center,initial.center);assert.equal(active.radius,initial.radius);assert.equal(active.state,'correct');assert.ok(ui.draws.some(d=>d.kind==='stroke'&&d.colour==='#BEB8FF'));ui.press(p[at+1]);active=ui.snapshot().directionMarkers.find(m=>m.cell===c.id);assert.deepEqual(active.center,initial.center);assert.equal(active.radius,initial.radius);ui.get('undo').click();assert.deepEqual(ui.snapshot().directionMarkers.find(m=>m.cell===c.id).center,initial.center);ui.get('restart').click();const g=graph(l);let wrong=null;function search(prefix){if(wrong)return;const last=prefix.at(-1);if(last===c.id){if(prefix.length>1){const from=l.cells.find(x=>x.id===prefix.at(-2));const side=from.x<c.x?'l':from.x>c.x?'r':from.y<c.y?'u':'d';if(!c.direction.includes(side))wrong=prefix;}return;}for(const n of g.adj[g.ids.get(last)]){const id=l.cells[n].id;if(!prefix.includes(id))search([...prefix,id]);}}search([p[0]]);assert.ok(wrong);for(const id of wrong)ui.press(id);const bad=ui.snapshot().directionMarkers.find(m=>m.cell===c.id);assert.equal(bad.state,'error');assert.deepEqual(bad.center,initial.center);assert.equal(bad.radius,initial.radius);});
|
|
'''
|
|
p.write_text(s,encoding='utf-8')
|