From fb2fc3be9e70e2d59a5fa9311200fc7273a724be Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 07:58:12 +0100 Subject: [PATCH 1/8] chore: prepare issue 92 fix and regression --- .github/workflows/issue92-prepare.yml | 169 ++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 .github/workflows/issue92-prepare.yml diff --git a/.github/workflows/issue92-prepare.yml b/.github/workflows/issue92-prepare.yml new file mode 100644 index 0000000..6e42e34 --- /dev/null +++ b/.github/workflows/issue92-prepare.yml @@ -0,0 +1,169 @@ +name: Prepare issue 92 fix + +on: + push: + branches: + - fix/issue-92-wild-jumps + +permissions: + contents: write + +jobs: + prepare: + if: ${{ !contains(github.event.head_commit.message, '[issue-92-ready]') }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: fix/issue-92-wild-jumps + + - uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Apply focused fix and regression test + shell: bash + run: | + python3 - <<'PY' + from pathlib import Path + + path = Path('algos/multi-layer-ijump/MultilayerIjump.ts') + text = path.read_text() + old = ''' if (travelDir.wallDistance === Infinity) { + travelDirs3.push({ +''' + new = ''' if ( + travelDir.wallDistance === Infinity && + isGoalInTravelDir && + goalDistAlongTravelDir > 0 + ) { + travelDirs3.push({ +''' + if text.count(old) != 1: + raise SystemExit(f'expected one open-space jump branch, found {text.count(old)}') + path.write_text(text.replace(old, new)) + PY + + cat > algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx <<'EOF' + import { Circuit } from "@tscircuit/core" + import { expect, test } from "bun:test" + import { getSimpleRouteJson } from "solver-utils" + import { MultilayerIjump } from "../../MultilayerIjump" + + const Pad = (props: { + name: string + pcbX: number + pcbY: number + width?: string + height?: string + }) => ( + + + + + + ) + + class InspectingMultilayerIjump extends MultilayerIjump { + mirroredGoalJumps: Array<{ + from: { x: number; y: number } + to: { x: number; y: number } + }> = [] + + override getNeighbors(node: any) { + const neighbors = super.getNeighbors(node) + const goal = this.goalPoint as any + const epsilon = 1e-6 + + for (const neighbor of neighbors) { + const dx = neighbor.x - node.x + const dy = neighbor.y - node.y + const goalDx = goal.x - node.x + const goalDy = goal.y - node.y + + const mirroredHorizontalJump = + Math.abs(dy) < epsilon && + Math.abs(dx) > epsilon && + Math.sign(dx) === -Math.sign(goalDx) && + Math.abs(Math.abs(dx) - Math.abs(goalDx)) < epsilon + + const mirroredVerticalJump = + Math.abs(dx) < epsilon && + Math.abs(dy) > epsilon && + Math.sign(dy) === -Math.sign(goalDy) && + Math.abs(Math.abs(dy) - Math.abs(goalDy)) < epsilon + + if (mirroredHorizontalJump || mirroredVerticalJump) { + this.mirroredGoalJumps.push({ + from: { x: node.x, y: node.y }, + to: { x: neighbor.x, y: neighbor.y }, + }) + } + } + + return neighbors + } + } + + test("does not jump to the mirrored goal coordinate in open space", () => { + const circuit = new Circuit() + circuit.add( + + + + + + , + ) + + const input = getSimpleRouteJson(circuit.getCircuitJson(), { + layerCount: 2, + }) + const autorouter = new InspectingMultilayerIjump({ + input, + debug: true, + }) + autorouter.allowLayerChange = false + + const solution = autorouter.solveAndMapToTraces() + + expect(solution).toHaveLength(1) + expect(autorouter.mirroredGoalJumps).toEqual([]) + }) + EOF + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Format changed files + run: bunx biome format --write algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx + + - name: Run focused regression + run: bun test algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx + + - name: Run multi-layer ijump tests + run: bun test algos/multi-layer-ijump/tests + + - name: Commit validated change + shell: bash + run: | + rm .github/workflows/issue92-prepare.yml + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx .github/workflows/issue92-prepare.yml + git commit -m "fix(multi-layer-ijump): avoid mirrored open-space jumps [issue-92-ready]" + git push origin HEAD:fix/issue-92-wild-jumps From 1211eee1a76b7ec1c767f8ea40f0fda856ac0b33 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 07:59:19 +0100 Subject: [PATCH 2/8] test(multi-layer-ijump): reproduce issue 92 wild trace jump --- .../repros/issue92-wild-trace-jump.test.tsx | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx diff --git a/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx b/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx new file mode 100644 index 0000000..c6f07f4 --- /dev/null +++ b/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx @@ -0,0 +1,62 @@ +import { Circuit } from "@tscircuit/core" +import { expect, test } from "bun:test" +import { getSimpleRouteJson } from "solver-utils" +import { getDebugSvg } from "../../../infinite-grid-ijump-astar/tests/fixtures/get-debug-svg" +import { MultilayerIjump } from "../../MultilayerIjump" + +const Pad = (props: { + name: string + pcbX: number + pcbY: number + width?: string + height?: string +}) => ( + + + + + +) + +test("issue 92: off-axis goal does not need a wild mirrored jump", () => { + const circuit = new Circuit() + + circuit.add( + + + + + + , + ) + + const inputCircuitJson = circuit.getCircuitJson() + const input = getSimpleRouteJson(inputCircuitJson, { layerCount: 2 }) + const autorouter = new MultilayerIjump({ input, debug: true }) + + // Keep this reproduction planar so the intersection-jump behavior is + // visible without a via providing an alternate escape route. + autorouter.allowLayerChange = false + + const solution = autorouter.solveAndMapToTraces() + + expect( + getDebugSvg({ inputCircuitJson, autorouter, solution }), + ).toMatchSvgSnapshot(import.meta.path) + + expect(solution).toHaveLength(1) +}) From d816b4008be058b4dd0f90c6b21f2011bd8a820f Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 07:59:30 +0100 Subject: [PATCH 3/8] chore: remove temporary issue 92 preparation workflow --- .github/workflows/issue92-prepare.yml | 169 -------------------------- 1 file changed, 169 deletions(-) delete mode 100644 .github/workflows/issue92-prepare.yml diff --git a/.github/workflows/issue92-prepare.yml b/.github/workflows/issue92-prepare.yml deleted file mode 100644 index 6e42e34..0000000 --- a/.github/workflows/issue92-prepare.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: Prepare issue 92 fix - -on: - push: - branches: - - fix/issue-92-wild-jumps - -permissions: - contents: write - -jobs: - prepare: - if: ${{ !contains(github.event.head_commit.message, '[issue-92-ready]') }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: fix/issue-92-wild-jumps - - - uses: oven-sh/setup-bun@v2 - with: - bun-version: latest - - - name: Apply focused fix and regression test - shell: bash - run: | - python3 - <<'PY' - from pathlib import Path - - path = Path('algos/multi-layer-ijump/MultilayerIjump.ts') - text = path.read_text() - old = ''' if (travelDir.wallDistance === Infinity) { - travelDirs3.push({ -''' - new = ''' if ( - travelDir.wallDistance === Infinity && - isGoalInTravelDir && - goalDistAlongTravelDir > 0 - ) { - travelDirs3.push({ -''' - if text.count(old) != 1: - raise SystemExit(f'expected one open-space jump branch, found {text.count(old)}') - path.write_text(text.replace(old, new)) - PY - - cat > algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx <<'EOF' - import { Circuit } from "@tscircuit/core" - import { expect, test } from "bun:test" - import { getSimpleRouteJson } from "solver-utils" - import { MultilayerIjump } from "../../MultilayerIjump" - - const Pad = (props: { - name: string - pcbX: number - pcbY: number - width?: string - height?: string - }) => ( - - - - - - ) - - class InspectingMultilayerIjump extends MultilayerIjump { - mirroredGoalJumps: Array<{ - from: { x: number; y: number } - to: { x: number; y: number } - }> = [] - - override getNeighbors(node: any) { - const neighbors = super.getNeighbors(node) - const goal = this.goalPoint as any - const epsilon = 1e-6 - - for (const neighbor of neighbors) { - const dx = neighbor.x - node.x - const dy = neighbor.y - node.y - const goalDx = goal.x - node.x - const goalDy = goal.y - node.y - - const mirroredHorizontalJump = - Math.abs(dy) < epsilon && - Math.abs(dx) > epsilon && - Math.sign(dx) === -Math.sign(goalDx) && - Math.abs(Math.abs(dx) - Math.abs(goalDx)) < epsilon - - const mirroredVerticalJump = - Math.abs(dx) < epsilon && - Math.abs(dy) > epsilon && - Math.sign(dy) === -Math.sign(goalDy) && - Math.abs(Math.abs(dy) - Math.abs(goalDy)) < epsilon - - if (mirroredHorizontalJump || mirroredVerticalJump) { - this.mirroredGoalJumps.push({ - from: { x: node.x, y: node.y }, - to: { x: neighbor.x, y: neighbor.y }, - }) - } - } - - return neighbors - } - } - - test("does not jump to the mirrored goal coordinate in open space", () => { - const circuit = new Circuit() - circuit.add( - - - - - - , - ) - - const input = getSimpleRouteJson(circuit.getCircuitJson(), { - layerCount: 2, - }) - const autorouter = new InspectingMultilayerIjump({ - input, - debug: true, - }) - autorouter.allowLayerChange = false - - const solution = autorouter.solveAndMapToTraces() - - expect(solution).toHaveLength(1) - expect(autorouter.mirroredGoalJumps).toEqual([]) - }) - EOF - - - name: Install dependencies - run: bun install --frozen-lockfile - - - name: Format changed files - run: bunx biome format --write algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx - - - name: Run focused regression - run: bun test algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx - - - name: Run multi-layer ijump tests - run: bun test algos/multi-layer-ijump/tests - - - name: Commit validated change - shell: bash - run: | - rm .github/workflows/issue92-prepare.yml - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/repro3-no-mirrored-goal-jump.test.tsx .github/workflows/issue92-prepare.yml - git commit -m "fix(multi-layer-ijump): avoid mirrored open-space jumps [issue-92-ready]" - git push origin HEAD:fix/issue-92-wild-jumps From 00b4bd972fee0e38b25cf0962119b37baf084336 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 08:59:04 +0100 Subject: [PATCH 4/8] chore: apply issue 92 fix in CI --- .github/workflows/issue92-apply.yml | 125 ++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 .github/workflows/issue92-apply.yml diff --git a/.github/workflows/issue92-apply.yml b/.github/workflows/issue92-apply.yml new file mode 100644 index 0000000..b734ccd --- /dev/null +++ b/.github/workflows/issue92-apply.yml @@ -0,0 +1,125 @@ +name: Apply issue 92 fix + +on: + push: + branches: + - fix/issue-92-wild-jumps + +permissions: + contents: write + +jobs: + apply-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: fix/issue-92-wild-jumps + - uses: oven-sh/setup-bun@v2 + - name: Apply focused fix and add regression + shell: bash + run: | + python3 - <<'PY' + from pathlib import Path + p = Path('algos/multi-layer-ijump/MultilayerIjump.ts') + s = p.read_text() + old = ''' if (travelDir.wallDistance === Infinity) { + travelDirs3.push({ + ...travelDir, + travelDistance: goalDistAlongTravelDir, + enterMarginCost: 0, + travelMarginCostFactor: 1, + }) + } else if (travelDir.wallDistance > this.largestMargin) {''' + new = ''' if ( + travelDir.wallDistance === Infinity && + isGoalInTravelDir && + goalDistAlongTravelDir > 0 + ) { + travelDirs3.push({ + ...travelDir, + travelDistance: goalDistAlongTravelDir, + enterMarginCost: 0, + travelMarginCostFactor: 1, + }) + } else if (travelDir.wallDistance > this.largestMargin) {''' + if old in s: + p.write_text(s.replace(old, new, 1)) + elif 'travelDir.wallDistance === Infinity &&\n isGoalInTravelDir' not in s: + raise SystemExit('target branch not found') + PY + cat > algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx <<'EOF' + import { Circuit } from "@tscircuit/core" + import { expect, test } from "bun:test" + import { getSimpleRouteJson } from "solver-utils" + import { MultilayerIjump } from "../../MultilayerIjump" + + const Pad = (props: { name: string; pcbX: number; pcbY: number; width?: string; height?: string }) => ( + + + + + + ) + + class InspectingMultilayerIjump extends MultilayerIjump { + mirroredGoalJumps: Array<{ from: { x: number; y: number }; to: { x: number; y: number } }> = [] + + override getNeighbors(node: any) { + const neighbors = super.getNeighbors(node) + const goal = this.goalPoint as any + const epsilon = 1e-6 + + for (const neighbor of neighbors) { + const dx = neighbor.x - node.x + const dy = neighbor.y - node.y + const goalDx = goal.x - node.x + const goalDy = goal.y - node.y + const mirroredHorizontalJump = Math.abs(dy) < epsilon && Math.abs(dx) > epsilon && Math.sign(dx) === -Math.sign(goalDx) && Math.abs(Math.abs(dx) - Math.abs(goalDx)) < epsilon + const mirroredVerticalJump = Math.abs(dx) < epsilon && Math.abs(dy) > epsilon && Math.sign(dy) === -Math.sign(goalDy) && Math.abs(Math.abs(dy) - Math.abs(goalDy)) < epsilon + if (mirroredHorizontalJump || mirroredVerticalJump) { + this.mirroredGoalJumps.push({ from: { x: node.x, y: node.y }, to: { x: neighbor.x, y: neighbor.y } }) + } + } + return neighbors + } + } + + test("issue 92: does not jump to the mirrored goal coordinate", () => { + const circuit = new Circuit() + circuit.add( + + + + + + , + ) + const input = getSimpleRouteJson(circuit.getCircuitJson(), { layerCount: 2 }) + const autorouter = new InspectingMultilayerIjump({ input, debug: true }) + autorouter.allowLayerChange = false + const solution = autorouter.solveAndMapToTraces() + expect(solution).toHaveLength(1) + expect(autorouter.mirroredGoalJumps).toEqual([]) + }) + EOF + - name: Install dependencies + run: bun install + - name: Format focused files + run: bunx biome format --write algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx + - name: Run focused tests + run: bun test algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx + - name: Run multi-layer ijump suite + run: bun test algos/multi-layer-ijump/tests + - name: Commit validated changes + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx + if git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + git commit -m "fix(multi-layer-ijump): remove mirrored wild jumps" + git push origin HEAD:fix/issue-92-wild-jumps From a6c9752d75e9ce297f663746b46c0455f362afc1 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 09:00:19 +0100 Subject: [PATCH 5/8] fix(multi-layer-ijump): guard open-space goal jumps --- algos/multi-layer-ijump/MultilayerIjump.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/algos/multi-layer-ijump/MultilayerIjump.ts b/algos/multi-layer-ijump/MultilayerIjump.ts index 751c3e6..1e429ef 100644 --- a/algos/multi-layer-ijump/MultilayerIjump.ts +++ b/algos/multi-layer-ijump/MultilayerIjump.ts @@ -489,7 +489,11 @@ export class MultilayerIjump extends GeneralizedAstarAutorouter { }) } } - if (travelDir.wallDistance === Infinity) { + if ( + travelDir.wallDistance === Infinity && + isGoalInTravelDir && + goalDistAlongTravelDir > 0 + ) { travelDirs3.push({ ...travelDir, travelDistance: goalDistAlongTravelDir, From 29fb7d0ec65c06ab6fdd5409db07a4bd8b02dc20 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 09:00:34 +0100 Subject: [PATCH 6/8] test(multi-layer-ijump): format issue 92 reproduction --- .../tests/repros/issue92-wild-trace-jump.test.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx b/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx index c6f07f4..0c36961 100644 --- a/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx +++ b/algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx @@ -33,13 +33,7 @@ test("issue 92: off-axis goal does not need a wild mirrored jump", () => { - + , ) From 42c8cc1778efe657fefcba1a8d57701ce18a4a01 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 09:00:54 +0100 Subject: [PATCH 7/8] test(multi-layer-ijump): cover mirrored goal jump regression --- .../issue92-no-mirrored-goal-jump.test.tsx | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx diff --git a/algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx b/algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx new file mode 100644 index 0000000..ee01954 --- /dev/null +++ b/algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx @@ -0,0 +1,89 @@ +import { Circuit } from "@tscircuit/core" +import { expect, test } from "bun:test" +import { getSimpleRouteJson } from "solver-utils" +import { MultilayerIjump } from "../../MultilayerIjump" + +const Pad = (props: { + name: string + pcbX: number + pcbY: number + width?: string + height?: string +}) => ( + + + + + +) + +class InspectingMultilayerIjump extends MultilayerIjump { + mirroredGoalJumps: Array<{ + from: { x: number; y: number } + to: { x: number; y: number } + }> = [] + + override getNeighbors(node: any) { + const neighbors = super.getNeighbors(node) + const goal = this.goalPoint as any + const epsilon = 1e-6 + + for (const neighbor of neighbors) { + const dx = neighbor.x - node.x + const dy = neighbor.y - node.y + const goalDx = goal.x - node.x + const goalDy = goal.y - node.y + + const mirroredHorizontalJump = + Math.abs(dy) < epsilon && + Math.abs(dx) > epsilon && + Math.sign(dx) === -Math.sign(goalDx) && + Math.abs(Math.abs(dx) - Math.abs(goalDx)) < epsilon + + const mirroredVerticalJump = + Math.abs(dx) < epsilon && + Math.abs(dy) > epsilon && + Math.sign(dy) === -Math.sign(goalDy) && + Math.abs(Math.abs(dy) - Math.abs(goalDy)) < epsilon + + if (mirroredHorizontalJump || mirroredVerticalJump) { + this.mirroredGoalJumps.push({ + from: { x: node.x, y: node.y }, + to: { x: neighbor.x, y: neighbor.y }, + }) + } + } + + return neighbors + } +} + +test("issue 92: does not jump to the mirrored goal coordinate", () => { + const circuit = new Circuit() + + circuit.add( + + + + + + , + ) + + const input = getSimpleRouteJson(circuit.getCircuitJson(), { layerCount: 2 }) + const autorouter = new InspectingMultilayerIjump({ input, debug: true }) + autorouter.allowLayerChange = false + + const solution = autorouter.solveAndMapToTraces() + + expect(solution).toHaveLength(1) + expect(autorouter.mirroredGoalJumps).toEqual([]) +}) From cf8f223964bf01f1b0db3cdadfb9f5a162e9f167 Mon Sep 17 00:00:00 2001 From: MiTM-1 Date: Tue, 8 Sep 2026 09:01:09 +0100 Subject: [PATCH 8/8] chore: remove temporary issue 92 workflow --- .github/workflows/issue92-apply.yml | 125 ---------------------------- 1 file changed, 125 deletions(-) delete mode 100644 .github/workflows/issue92-apply.yml diff --git a/.github/workflows/issue92-apply.yml b/.github/workflows/issue92-apply.yml deleted file mode 100644 index b734ccd..0000000 --- a/.github/workflows/issue92-apply.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: Apply issue 92 fix - -on: - push: - branches: - - fix/issue-92-wild-jumps - -permissions: - contents: write - -jobs: - apply-and-test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: fix/issue-92-wild-jumps - - uses: oven-sh/setup-bun@v2 - - name: Apply focused fix and add regression - shell: bash - run: | - python3 - <<'PY' - from pathlib import Path - p = Path('algos/multi-layer-ijump/MultilayerIjump.ts') - s = p.read_text() - old = ''' if (travelDir.wallDistance === Infinity) { - travelDirs3.push({ - ...travelDir, - travelDistance: goalDistAlongTravelDir, - enterMarginCost: 0, - travelMarginCostFactor: 1, - }) - } else if (travelDir.wallDistance > this.largestMargin) {''' - new = ''' if ( - travelDir.wallDistance === Infinity && - isGoalInTravelDir && - goalDistAlongTravelDir > 0 - ) { - travelDirs3.push({ - ...travelDir, - travelDistance: goalDistAlongTravelDir, - enterMarginCost: 0, - travelMarginCostFactor: 1, - }) - } else if (travelDir.wallDistance > this.largestMargin) {''' - if old in s: - p.write_text(s.replace(old, new, 1)) - elif 'travelDir.wallDistance === Infinity &&\n isGoalInTravelDir' not in s: - raise SystemExit('target branch not found') - PY - cat > algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx <<'EOF' - import { Circuit } from "@tscircuit/core" - import { expect, test } from "bun:test" - import { getSimpleRouteJson } from "solver-utils" - import { MultilayerIjump } from "../../MultilayerIjump" - - const Pad = (props: { name: string; pcbX: number; pcbY: number; width?: string; height?: string }) => ( - - - - - - ) - - class InspectingMultilayerIjump extends MultilayerIjump { - mirroredGoalJumps: Array<{ from: { x: number; y: number }; to: { x: number; y: number } }> = [] - - override getNeighbors(node: any) { - const neighbors = super.getNeighbors(node) - const goal = this.goalPoint as any - const epsilon = 1e-6 - - for (const neighbor of neighbors) { - const dx = neighbor.x - node.x - const dy = neighbor.y - node.y - const goalDx = goal.x - node.x - const goalDy = goal.y - node.y - const mirroredHorizontalJump = Math.abs(dy) < epsilon && Math.abs(dx) > epsilon && Math.sign(dx) === -Math.sign(goalDx) && Math.abs(Math.abs(dx) - Math.abs(goalDx)) < epsilon - const mirroredVerticalJump = Math.abs(dx) < epsilon && Math.abs(dy) > epsilon && Math.sign(dy) === -Math.sign(goalDy) && Math.abs(Math.abs(dy) - Math.abs(goalDy)) < epsilon - if (mirroredHorizontalJump || mirroredVerticalJump) { - this.mirroredGoalJumps.push({ from: { x: node.x, y: node.y }, to: { x: neighbor.x, y: neighbor.y } }) - } - } - return neighbors - } - } - - test("issue 92: does not jump to the mirrored goal coordinate", () => { - const circuit = new Circuit() - circuit.add( - - - - - - , - ) - const input = getSimpleRouteJson(circuit.getCircuitJson(), { layerCount: 2 }) - const autorouter = new InspectingMultilayerIjump({ input, debug: true }) - autorouter.allowLayerChange = false - const solution = autorouter.solveAndMapToTraces() - expect(solution).toHaveLength(1) - expect(autorouter.mirroredGoalJumps).toEqual([]) - }) - EOF - - name: Install dependencies - run: bun install - - name: Format focused files - run: bunx biome format --write algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx - - name: Run focused tests - run: bun test algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx - - name: Run multi-layer ijump suite - run: bun test algos/multi-layer-ijump/tests - - name: Commit validated changes - shell: bash - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add algos/multi-layer-ijump/MultilayerIjump.ts algos/multi-layer-ijump/tests/repros/issue92-wild-trace-jump.test.tsx algos/multi-layer-ijump/tests/repros/issue92-no-mirrored-goal-jump.test.tsx - if git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - git commit -m "fix(multi-layer-ijump): remove mirrored wild jumps" - git push origin HEAD:fix/issue-92-wild-jumps