diff --git a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
index 6795ff7..c77f5bd 100644
--- a/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
+++ b/lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2.ts
@@ -83,7 +83,16 @@ export class SchematicTraceSingleLineSolver2 extends BaseSolver {
{ x: pin2.x, y: pin2.y },
)
- // Seed search
+ // Check if base elbow path has no collisions - if so, use it directly
+ const baseCollision = findFirstCollision(this.baseElbow, this.obstacles)
+ if (!baseCollision) {
+ // No collisions found, use the base elbow path as the solution
+ this.solvedTracePath = this.baseElbow
+ this.solved = true
+ return
+ }
+
+ // Base elbow has collisions, proceed with pathfinding
this.queue.push({ path: this.baseElbow, collisionChipIds: new Set() })
this.visited.add(pathKey(this.baseElbow))
}
diff --git a/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/collision-free-long-traces.snap.svg b/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/collision-free-long-traces.snap.svg
new file mode 100644
index 0000000..aff3f62
--- /dev/null
+++ b/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/collision-free-long-traces.snap.svg
@@ -0,0 +1,86 @@
+
\ No newline at end of file
diff --git a/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/diagonal-collision-free.snap.svg b/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/diagonal-collision-free.snap.svg
new file mode 100644
index 0000000..2d63165
--- /dev/null
+++ b/tests/solvers/SchematicTraceSingleLineSolver2/__snapshots__/diagonal-collision-free.snap.svg
@@ -0,0 +1,89 @@
+
\ No newline at end of file
diff --git a/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-long-traces.test.ts b/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-long-traces.test.ts
new file mode 100644
index 0000000..13c0be9
--- /dev/null
+++ b/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-long-traces.test.ts
@@ -0,0 +1,190 @@
+import { expect, test } from "bun:test"
+import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
+
+test("long collision-free traces should be used directly - showcasing bounty #68", () => {
+ // This test demonstrates the bounty #68 feature: "Allow long traces that don't cross any other traces"
+ // When the direct elbow path has no collisions, it should be used immediately without pathfinding
+
+ const inputProblem = {
+ chips: [
+ // Left chip with pin facing right
+ {
+ chipId: "chip_left",
+ center: { x: -5, y: 0 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "left_pin",
+ x: -4.5, // Right edge of left chip
+ y: 0,
+ },
+ ],
+ },
+ // Right chip with pin facing left - far away to create a long trace
+ {
+ chipId: "chip_right",
+ center: { x: 5, y: 2 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "right_pin",
+ x: 4.5, // Left edge of right chip
+ y: 2,
+ },
+ ],
+ },
+ // Obstacle chip that doesn't interfere with the direct path
+ {
+ chipId: "obstacle",
+ center: { x: 0, y: -3 },
+ width: 1.5,
+ height: 1.5,
+ pins: [],
+ },
+ ],
+ directConnections: [],
+ netConnections: [],
+ availableNetLabelOrientations: {},
+ }
+
+ const chipMap = {
+ chip_left: inputProblem.chips[0],
+ chip_right: inputProblem.chips[1],
+ obstacle: inputProblem.chips[2],
+ }
+
+ const pins = [
+ {
+ pinId: "left_pin",
+ x: -4.5,
+ y: 0,
+ chipId: "chip_left",
+ _facingDirection: "x+" as const, // Facing right
+ },
+ {
+ pinId: "right_pin",
+ x: 4.5,
+ y: 2,
+ chipId: "chip_right",
+ _facingDirection: "x-" as const, // Facing left
+ },
+ ]
+
+ const solver = new SchematicTraceSingleLineSolver2({
+ inputProblem: inputProblem as any,
+ chipMap: chipMap as any,
+ pins: pins as any,
+ })
+
+ // Should be solved immediately since the long trace has no collisions
+ expect(solver.solved).toBe(true)
+ expect(solver.solvedTracePath).not.toBeNull()
+ expect(solver.solvedTracePath?.length).toBeGreaterThan(2) // Elbow path has multiple points
+
+ // The path should be the collision-free base elbow path
+ expect(solver.solvedTracePath).toEqual(solver.baseElbow)
+
+ // Verify the path connects the pins correctly
+ const path = solver.solvedTracePath!
+ expect(path[0]).toEqual({ x: -4.5, y: 0 })
+ expect(path[path.length - 1]).toEqual({ x: 4.5, y: 2 })
+
+ expect(solver).toMatchSolverSnapshot(import.meta.path)
+})
+
+test("collision-free diagonal connection with obstacles nearby", () => {
+ // Another test case showing long traces work when obstacles don't interfere
+
+ const inputProblem = {
+ chips: [
+ // Bottom-left chip
+ {
+ chipId: "chip_bl",
+ center: { x: -4, y: -3 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "bl_pin",
+ x: -3.5, // Right edge
+ y: -3,
+ },
+ ],
+ },
+ // Top-right chip - creating a diagonal long trace
+ {
+ chipId: "chip_tr",
+ center: { x: 4, y: 3 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "tr_pin",
+ x: 3.5, // Left edge
+ y: 3,
+ },
+ ],
+ },
+ // Obstacle chips that don't block the path (positioned away from the elbow)
+ {
+ chipId: "obstacle1",
+ center: { x: -1, y: 2 },
+ width: 0.5,
+ height: 0.5,
+ pins: [],
+ },
+ {
+ chipId: "obstacle2",
+ center: { x: 1, y: -2 },
+ width: 0.5,
+ height: 0.5,
+ pins: [],
+ },
+ ],
+ directConnections: [],
+ netConnections: [],
+ availableNetLabelOrientations: {},
+ }
+
+ const chipMap = {
+ chip_bl: inputProblem.chips[0],
+ chip_tr: inputProblem.chips[1],
+ obstacle1: inputProblem.chips[2],
+ obstacle2: inputProblem.chips[3],
+ }
+
+ const pins = [
+ {
+ pinId: "bl_pin",
+ x: -3.5,
+ y: -3,
+ chipId: "chip_bl",
+ _facingDirection: "x+" as const,
+ },
+ {
+ pinId: "tr_pin",
+ x: 3.5,
+ y: 3,
+ chipId: "chip_tr",
+ _facingDirection: "x-" as const,
+ },
+ ]
+
+ const solver = new SchematicTraceSingleLineSolver2({
+ inputProblem: inputProblem as any,
+ chipMap: chipMap as any,
+ pins: pins as any,
+ })
+
+ // Should be solved immediately with collision-free elbow
+ expect(solver.solved).toBe(true)
+ expect(solver.solvedTracePath).not.toBeNull()
+ expect(solver.solvedTracePath).toEqual(solver.baseElbow)
+
+ expect(solver).toMatchSolverSnapshot(
+ import.meta.path,
+ "diagonal-collision-free",
+ )
+})
diff --git a/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-optimization.test.ts b/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-optimization.test.ts
new file mode 100644
index 0000000..5717ced
--- /dev/null
+++ b/tests/solvers/SchematicTraceSingleLineSolver2/collision-free-optimization.test.ts
@@ -0,0 +1,72 @@
+import { expect, test } from "bun:test"
+import { SchematicTraceSingleLineSolver2 } from "lib/solvers/SchematicTraceLinesSolver/SchematicTraceSingleLineSolver2/SchematicTraceSingleLineSolver2"
+
+test("collision-free base elbow should be used directly without pathfinding", () => {
+ // Simple test case where pins are far apart with no obstacles in between
+ const inputProblem = {
+ chips: [
+ {
+ chipId: "chip1",
+ center: { x: -2, y: 0 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "pin1",
+ x: -1.5,
+ y: 0,
+ },
+ ],
+ },
+ {
+ chipId: "chip2",
+ center: { x: 2, y: 0 },
+ width: 1,
+ height: 1,
+ pins: [
+ {
+ pinId: "pin2",
+ x: 1.5,
+ y: 0,
+ },
+ ],
+ },
+ ],
+ directConnections: [],
+ netConnections: [],
+ availableNetLabelOrientations: {},
+ }
+
+ const chipMap = {
+ chip1: inputProblem.chips[0],
+ chip2: inputProblem.chips[1],
+ }
+
+ const pins = [
+ {
+ pinId: "pin1",
+ x: -1.5,
+ y: 0,
+ chipId: "chip1",
+ _facingDirection: "x+" as const,
+ },
+ {
+ pinId: "pin2",
+ x: 1.5,
+ y: 0,
+ chipId: "chip2",
+ _facingDirection: "x-" as const,
+ },
+ ]
+
+ const solver = new SchematicTraceSingleLineSolver2({
+ inputProblem: inputProblem as any,
+ chipMap: chipMap as any,
+ pins: pins as any,
+ })
+
+ // Should be solved immediately since there are no collisions
+ expect(solver.solved).toBe(true)
+ expect(solver.solvedTracePath).not.toBeNull()
+ expect(solver.solvedTracePath?.length).toBeGreaterThan(0)
+})