Restructures the router so occupancy is tracked per connection, then adds
a rip-up-and-retry loop that recovers from bad greedy net orderings.
When a connection cannot reach its target through free space, a rip-up
search may pass through other nets' traces at an escalating penalty; the
router rips up the connections that path crosses (fully removing their
occupancy), routes the failing connection, and re-queues the ripped
connections. Passes iterate up to max_passes, keeping the best
(fewest-unrouted) result and stopping on full success or when a pass
changes nothing.
Thrash prevention: each connection may be ripped at most rip_cap times,
and the rip penalty escalates with a connection's rip count, so
repeatedly-ripped connections harden into walls. Fully deterministic
(sorted rip sets, connection-creation order, tuple-keyed A* — no RNG).
route()/route_dsn_board()/route_board() take rip_up and max_passes;
rip_up=False reproduces the previous greedy single-pass behaviour for
comparison. RouteResult now reports the unrouted connection count.