diff --git a/.github/workflows/bun-test.yml b/.github/workflows/bun-test.yml index a6300fe..d9e5022 100644 --- a/.github/workflows/bun-test.yml +++ b/.github/workflows/bun-test.yml @@ -19,7 +19,7 @@ jobs: - name: Setup bun uses: oven-sh/setup-bun@v2 with: - bun-version: latest + bun-version: 1.3.1 - name: Install dependencies run: bun install diff --git a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts index a33646d..fedc080 100644 --- a/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts +++ b/lib/solvers/TraceLabelOverlapAvoidanceSolver/sub-solvers/OverlapAvoidanceStepSolver/OverlapAvoidanceStepSolver.ts @@ -31,10 +31,17 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { private detourCountByLabel: Record = {} private readonly PADDING_BUFFER = 0.1 + private readonly MAX_ATTEMPTS_PER_OVERLAP = 25 + private overlapAttemptCounts: Record = {} public override activeSubSolver: SingleOverlapSolver | null = null private overlapQueue: Overlap[] = [] private recentlyFailed: Set = new Set() + private currentOverlapId: string | null = null + + private getOverlapId(overlap: Overlap) { + return `${overlap.trace.mspPairId}-${overlap.label.globalConnNetId}` + } constructor(solverInput: OverlapCollectionSolverInput) { super() @@ -61,10 +68,14 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { } this.activeSubSolver = null this.recentlyFailed.clear() + this.currentOverlapId = null } else if (this.activeSubSolver.failed) { - const overlapId = `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}` + const overlapId = + this.currentOverlapId ?? + `${this.activeSubSolver.initialTrace.mspPairId}-${this.activeSubSolver.label.globalConnNetId}` this.recentlyFailed.add(overlapId) this.activeSubSolver = null + this.currentOverlapId = null } return } @@ -80,13 +91,19 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { return o.trace.globalConnNetId !== o.label.globalConnNetId }) - if (overlaps.length === 0) { + const viableOverlaps = overlaps.filter((o) => { + const overlapId = this.getOverlapId(o) + const attempts = this.overlapAttemptCounts[overlapId] ?? 0 + return attempts < this.MAX_ATTEMPTS_PER_OVERLAP + }) + + if (viableOverlaps.length === 0) { this.solved = true return } - const nonFailedOverlaps = overlaps.filter((o) => { - const overlapId = `${o.trace.mspPairId}-${o.label.globalConnNetId}` + const nonFailedOverlaps = viableOverlaps.filter((o) => { + const overlapId = this.getOverlapId(o) return !this.recentlyFailed.has(overlapId) }) @@ -100,6 +117,10 @@ export class OverlapAvoidanceStepSolver extends BaseSolver { const nextOverlap = this.overlapQueue.shift() if (nextOverlap) { + const overlapId = this.getOverlapId(nextOverlap) + const attemptCount = this.overlapAttemptCounts[overlapId] ?? 0 + this.overlapAttemptCounts[overlapId] = attemptCount + 1 + this.currentOverlapId = overlapId const traceToFix = this.allTraces.find( (t) => t.mspPairId === nextOverlap.trace.mspPairId, ) diff --git a/tests/assets/repro32-castellated-pinout.schematicTraceSolverInput.json b/tests/assets/repro32-castellated-pinout.schematicTraceSolverInput.json new file mode 100644 index 0000000..133143f --- /dev/null +++ b/tests/assets/repro32-castellated-pinout.schematicTraceSolverInput.json @@ -0,0 +1,168 @@ +{ + "chips": [ + { + "chipId": "schematic_component_0", + "center": { + "x": 0, + "y": 0 + }, + "width": 0.4, + "height": 2.5999999999999996, + "pins": [ + { + "pinId": "P.1", + "x": -0.6000000000000001, + "y": 1.0999999999999999 + }, + { + "pinId": "P.2", + "x": -0.6000000000000001, + "y": 0.8999999999999999 + }, + { + "pinId": "P.3", + "x": -0.6000000000000001, + "y": 0.6999999999999998 + }, + { + "pinId": "P.4", + "x": -0.6000000000000001, + "y": 0.4999999999999998 + }, + { + "pinId": "P.5", + "x": -0.6000000000000001, + "y": 0.2999999999999998 + }, + { + "pinId": "P.6", + "x": -0.6000000000000001, + "y": 0.09999999999999987 + }, + { + "pinId": "P.7", + "x": -0.6000000000000001, + "y": -0.10000000000000009 + }, + { + "pinId": "P.8", + "x": -0.6000000000000001, + "y": -0.30000000000000004 + }, + { + "pinId": "P.9", + "x": -0.6000000000000001, + "y": -0.5 + }, + { + "pinId": "P.10", + "x": -0.6000000000000001, + "y": -0.7 + }, + { + "pinId": "P.11", + "x": -0.6000000000000001, + "y": -0.8999999999999999 + }, + { + "pinId": "P.12", + "x": -0.6000000000000001, + "y": -1.0999999999999999 + }, + { + "pinId": "P.13", + "x": 0.6000000000000001, + "y": -1.0999999999999999 + }, + { + "pinId": "P.14", + "x": 0.6000000000000001, + "y": -0.8999999999999999 + }, + { + "pinId": "P.15", + "x": 0.6000000000000001, + "y": -0.6999999999999998 + }, + { + "pinId": "P.16", + "x": 0.6000000000000001, + "y": -0.4999999999999998 + }, + { + "pinId": "P.17", + "x": 0.6000000000000001, + "y": -0.2999999999999998 + }, + { + "pinId": "P.18", + "x": 0.6000000000000001, + "y": -0.09999999999999987 + }, + { + "pinId": "P.19", + "x": 0.6000000000000001, + "y": 0.10000000000000009 + }, + { + "pinId": "P.20", + "x": 0.6000000000000001, + "y": 0.30000000000000004 + }, + { + "pinId": "P.21", + "x": 0.6000000000000001, + "y": 0.5 + }, + { + "pinId": "P.22", + "x": 0.6000000000000001, + "y": 0.7 + }, + { + "pinId": "P.23", + "x": 0.6000000000000001, + "y": 0.8999999999999999 + }, + { + "pinId": "P.24", + "x": 0.6000000000000001, + "y": 1.0999999999999999 + } + ] + } + ], + "directConnections": [ + { + "pinIds": ["P.1", "P.18"], + "netId": "P.pin1 to P.pin18" + }, + { + "pinIds": ["P.2", "P.17"], + "netId": "P.pin2 to P.pin17" + }, + { + "pinIds": ["P.3", "P.16"], + "netId": "P.pin3 to P.pin16" + }, + { + "pinIds": ["P.4", "P.15"], + "netId": "P.pin4 to P.pin15" + }, + { + "pinIds": ["P.5", "P.14"], + "netId": "P.pin5 to P.pin14" + }, + { + "pinIds": ["P.6", "P.13"], + "netId": "P.pin6 to P.pin13" + }, + { + "pinIds": ["P.7", "P.19"], + "netId": "P.pin7 to P.pin19" + } + ], + "netConnections": [], + "availableNetLabelOrientations": {}, + "maxMspPairDistance": 2.4 +} diff --git a/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro04.test.ts b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro04.test.ts new file mode 100644 index 0000000..905ea12 --- /dev/null +++ b/tests/solvers/SchematicTracePipelineSolver/SchematicTracePipelineSolver_repro04.test.ts @@ -0,0 +1,21 @@ +import { readFileSync } from "fs" +import { test, expect } from "bun:test" +import { SchematicTracePipelineSolver } from "lib/index" +import type { InputProblem } from "lib/types/InputProblem" + +const fixturePath = new URL( + "../../assets/repro32-castellated-pinout.schematicTraceSolverInput.json", + import.meta.url, +) + +const inputProblem = JSON.parse( + readFileSync(fixturePath, "utf-8"), +) as InputProblem + +test("pipeline completes for castellated pinout with limited overlap retries", () => { + const solver = new SchematicTracePipelineSolver(inputProblem) + solver.solve() + + expect(solver.failed).toBeFalse() + expect(solver.solved).toBeTrue() +})