main
John Lauer Corrected Astra ESC routing: native zero-open result, full accounting and voiceover handoff a80479d 23d ago
from component_search import *
from soft_search import search_soft
import copy,sys,random
plans=json.load(open(sys.argv[1] if len(sys.argv)>1 else 'component-repaired.json'))
load(plans)
def gaps():return {p['net']:len(components(p['net']))-1 for p in plans if p['net'] not in ['GND','+3V3'] and len(components(p['net']))>1}
def pathgeo(plan,path):
 old=list(m.copper);m.copper.clear();register2(plan['net'],path,plan['width']);cs=list(m.copper);m.copper[:]=old;return cs
best=sum(gaps().values());print('INITIAL',gaps(),flush=True)
for iteration in range(150):
 missing=gaps()
 if not missing:break
 net=list(missing)[iteration%len(missing)];plan=next(p for p in plans if p['net']==net)
 before=copy.deepcopy(plans);old=list(m.copper)
 cs=components(net);pairs=sorted((unary_union([c['g'] for c in x]).distance(unary_union([c['g'] for c in y])),i,j) for i,x in enumerate(cs) for j,y in enumerate(cs[:i]))
 # Search against pads, planes, and this net, then identify exact donor paths.
 m.copper[:]=old
 candidates=[]
 for _,i,j in pairs:
  route=search_soft(net,plan['width'],cs[i],cs[j])
  if route:candidates.append(route)
  if len(candidates)>=3:break
 m.copper[:]=old
 if not candidates:
  print(iteration,'NO CANDIDATE',net,flush=True);continue
 scored=[]
 for route in candidates:
  new=pathgeo(plan,route);donors=[]
  for q in plans:
   if q['net'] in [net,'GND','+3V3']:continue
   for ix,path in enumerate(q['paths']):
    gc=pathgeo(q,path)
    if any(set(a['zs'])&set(b['zs']) and a['g'].distance(b['g'])<.205 for a in new for b in gc):donors.append((q['net'],ix))
  scored.append((len(donors),route,donors))
 _,route,donors=min(scored,key=lambda x:x[0]);print(iteration,'RIP',net,donors,flush=True)
 for q in plans:q['paths']=[p for i,p in enumerate(q['paths']) if (q['net'],i) not in donors]
 load(plans)
 # Recheck the route against all remaining copper before registering it.
 # Exact paths above came from the hard pad/plane mask; all conflicts removed.
 register2(net,route,plan['width']);plan['paths'].append(route)
 todo=list(dict.fromkeys(n for n,i in donors))
 for n in todo:
  q=next(p for p in plans if p['net']==n);route_components(q)
 remaining=gaps();score=sum(remaining.values());print('RESULT',score,remaining,flush=True)
 Path('soft-current.json').write_text(json.dumps(plans,indent=2))
 if score<best:
  best=score;Path('soft-best.json').write_text(json.dumps(plans,indent=2));print('BEST',best,flush=True)
 if score>max(best+3,5):
  plans=before;load(plans);print('REVERT regression',flush=True)