ID:45760
Title:wireless wonder
Author:nathan q
Date:2008-05-01 09:15:09
Score:18646.8324
Result:185961.00 (cyc: 31, node: 2313)
CPU Time:39.2716
Status:Passed
Comments:
Based on:none
Code:
function W = solver(B)

% gettingBetter13: maze for initiation
% gettingBetter11: terminate pathfinder1 routes if we run into a live line
% gettingBetter10: "voltage" is only on when pins are part of a net
% gettingBetter9: better maze search - cut corners
% gettingBetter8: maze search to bring more points into net
% gettingBetter7: no maze. submitted as      the same, only more so
% gettingBetter3: start with most valuable nets, not highest voltages
% gettingBetter2: multiple nets
% gettingBetter: bring mutiple pins into the network
% wifi2: keep track of voltage to avoid crossings

V=B;    % voltage map - voltage that location has IF it's connected - i.e. unwired pins have value
Vlive=0*V;  % voltage if live - unwired pins have zero

Bv = B(:);
sizeB = size(B);
W = zeros(0,4);

BvNZ = Bv(logical(Bv));   % non-zero elements

pinIDs = sort(unique(BvNZ),'descend');
nIDs = numel(pinIDs);    % there is always a zero in the list

% evaluate total value of each net
for i=1:nIDs
    totalV(i) = sum(Bv(Bv==pinIDs(i)));
end

[totalV,isort] = sort(totalV,'descend');
pinIDs=pinIDs(isort);

if isempty (pinIDs) return; end


% loop over possible nets
for i=1:nIDs

    % start with the highest-valued group (first in the list)
    pinVal = pinIDs(i);

    % number of pins in this net
    nPins = sum(BvNZ==pinVal);
    if nPins<=1
        continue
    end
    livePins = false(nPins,1);    % true for pins that are connected

    %locations of pins in this net
    pinLocs2D = zeros(nPins,2);

    pinLocs1D = find(Bv==pinVal);
%     for j=1:nPins
%         [pinLocs2D(j,1),pinLocs2D(j,2)] = ind2sub(sizeB,pinLocs1D(j));   % this could probably be faster
%     end
    % next 2 lines are indeed MUCH faster than ind2sub
    pinLocs2D(:,2) = floor((pinLocs1D-1)/sizeB(1))+1;
    pinLocs2D(:,1) = pinLocs1D -  (pinLocs2D(:,2)-1)*sizeB(1); 

    if nPins==2
        [wLink,V,Vlive] = pathfinder1(pinLocs2D(1,:), pinLocs2D(2,:), V,Vlive,pinVal);
        if any(wLink)
            W = [W; wLink];
        end
        continue
    end

    
    % Get the net started
    % Find the pin nearest the centroid, and find something to connect it to

    % find centroid of the prospective net:
    ic=sum(pinLocs2D(:,1))/nPins; 
    jc=sum(pinLocs2D(:,2))/nPins; 

    % distance from each pin to centroid:
    cdist = abs(ic-pinLocs2D(:,1)) + abs(jc-pinLocs2D(:,2));
    
    for k=1:3
        % These 3 attempts to start a net are costly - 50% time increase
        % for a 3% score descrease wrt to a single shot.
        if k==1 % first attempt: pick the pin nearest the centroid of the net
            [cdistMin,iPin] = min(cdist);
        elseif k==2  % 2nd attempt: mid-range
            [cdistMin,iPin] = min(abs(cdist-mean(cdist)));
        elseif k==3   % 3rd attempt: outlier
            [cdistMin,iPin] = max(cdist);
        end

        %staircase distance from pin1 to every other pin
        dist = sum(abs(pinLocs2D(iPin*ones(nPins,1),:) - pinLocs2D),2);
        dist(iPin) = Inf;
        wLink=[];
        while isempty(wLink) && any(dist<Inf)
            [distMin,iTarget] = min(dist);
            [wLink,V,Vlive] = pathfinder1(pinLocs2D(iPin,:), pinLocs2D(iTarget,:), V, Vlive, pinVal);
            dist(iTarget) = Inf;   % don't check last target pin again
        end
        if any(wLink)
            W=[W; wLink];
            livePins(iPin)=1;
            livePins(iTarget)=1;
            break
        end

        % Still here? we failed to initiate. try with maze solver.
        dist = sum(abs(pinLocs2D(iPin*ones(nPins,1),:) - pinLocs2D),2);
        dist(iPin) = Inf;
        wLink=[];
        while isempty(wLink) && any(dist<Inf)
            [distMin,iTarget] = min(dist);
            [wLink,V,Vlive] = maze(pinLocs2D(iPin,:), pinLocs2D(iTarget,:), V, Vlive);
            dist(iTarget) = Inf;   % don't check last target pin again
        end
        if any(wLink)
            W=[W; wLink];
            livePins(iPin)=1;
            livePins(iTarget)=1;
            break
        end


    end
    

%     if isempty(W)   % failed to initiated? try with maze search
%         
%     end
    
    % compute distance matrix
%     D = abs (repmat(pinLocs2D(:,1),[1 nPins]) - repmat(pinLocs2D(:,1)',[nPins 1])) + ...
%         abs (repmat(pinLocs2D(:,2),[1 nPins]) - repmat(pinLocs2D(:,2)',[nPins 1])) ; 
%     D = abs ( pinLocs2D(:,ones(1,nPins)) - pinLocs2D(:,ones(1,nPins))' ) + ...
%         abs ( pinLocs2D(:,2*ones(1,nPins)) - pinLocs2D(:,2*ones(1,nPins))' ) ; 
    
%     D(~livePins,:) = Inf;
%     D(:,~livePins) = Inf;
 %   D(logical(eye(nPins)))=Inf;
 
 
        % now loop over all pins, and for ones that aren't live, try to connect them. 
    % Now roam over all unconnected pins and try to get them into the net
    % THIS IS SLOW
    if any(livePins)    % For now, only do this if we've already succeeded on the first connection (but that's STUPID).
        for k=1:4  % multiple passes may help
            for iPin=1:nPins       %loop over all pins
                if livePins(iPin)  % ignore pins that are wired up
                    continue
                end
                dist = sum(abs(pinLocs2D(iPin*ones(nPins,1),:) - pinLocs2D),2);      % staircase distance to all other pins
                dist(iPin) = Inf;
                dist(~livePins)=Inf;   % don't connect pin to itself or non-wired pins
                [distMin,iTarget] = min(dist);
                %                 [wLink,V] = pathfinder1(pinLocs2D(iPin,:), pinLocs2D(iTarget,:), V);
                wLink=[];
                % This loop works through all possible target pins
                while isempty(wLink) && any(dist<Inf)
                    [distMin,iTarget] = min(dist);
                    [wLink,V,Vlive] = pathfinder1(pinLocs2D(iPin,:), pinLocs2D(iTarget,:), V, Vlive, pinVal);
                    dist(iTarget) = Inf;   % don't check last target pin again
                end

                if any(wLink)
                    W=[W; wLink];
                    livePins(iPin)=1;
                    livePins(iTarget)=1;  % this shouldn't be necessary
                end
            end
        end
    end

    % repeat the exercise, using maze search
    if any(livePins) && any(~livePins)    % For now, only do this if we've already succeeded on the first connection (but that's STUPID).
        %     for k=1:4  % multiple passes may help
        for iPin=1:nPins       %loop over all pins
            if livePins(iPin)  % ignore pins that are wired up
                    continue
                end
                % recomputing distance here is a bit wasteful - could be smarter
                dist = sum(abs(pinLocs2D(iPin*ones(nPins,1),:) - pinLocs2D),2);      % staircase distance to all other pins
                dist(iPin) = Inf;
                dist(~livePins)=Inf;   % don't connect pin to itself or non-wired pins
   %             [distMin,iTarget] = min(dist);
                wLink=[];
                % This loop works through all possible target pins
                while isempty(wLink) && any(dist<Inf)
                    [distMin,iTarget] = min(dist);
                    [wLink,V,Vlive] = maze(pinLocs2D(iPin,:), pinLocs2D(iTarget,:), V, Vlive);
                    dist(iTarget) = Inf;   % don't check last target pin again
                end

                if any(wLink)
                    W=[W; wLink];
                    livePins(iPin)=1;
                    livePins(iTarget)=1;  % this shouldn't be necessary
                end
            end
    %    end  % for k=1:4
    end

end


return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [W,V,Vlive]=maze(loc1,loc2,V,Vlive)

i1=loc1(1); j1=loc1(2); i2=loc2(1); j2=loc2(2); 

B=V;
B(logical(V))=1;
v=V(i1,j1); 
B(V==v)=0;   % ok to pass through points at our voltage
sizeB=size(B);
% at this stage B is a binary map - 0=allowed, 1=off-limits


% pad board with a wall of ones, set up some auxiliary boards
B1=ones(sizeB+[2 2]);
T1 = B1-1;    % trail map - entries will show the sequence number on the trail
V1=-B1;      % padded voltage map
B1(2:sizeB(1)+1,2:sizeB(2)+1) = B;
V1(2:sizeB(1)+1,2:sizeB(2)+1) = Vlive;


i1=i1+1;j1=j1+1;i2=i2+1;j2=j2+1;


W=zeros(0,4);
dsq = (i1-i2)^2+(j1-j2)^2;
keepgoing=true;

% values of B
% 0: available
% 1: off limits
% 2: explored and rejected
% 3: behind us on current track

B1(i1,j1)=3;

i=i1;j=j1;
moves=false(1,4);

% moves are right up left down
diMoves = [0 -1 0 1];
djMoves = [1 0 -1 0];

trail=zeros(1000,2);
trail(1,:) = [i1 j1];
nTrail=1;
T1(i1,j1) = 1;

arrived=false;

while 1
    % moves are right up left down
    moves = ~[B1(i,j+1) B1(i-1,j) B1(i,j-1) B1(i+1,j)];
    % no move available? 
    if ~any(moves) 
        if nTrail==1 % at square 1 with nowhere to go - failure
            break
        end
        B1(i,j)=2;   % mark the square as a reject
        T1(i,j)=0;  
        nTrail = nTrail-1;  % backtrack
        i=trail(nTrail,1);
        j=trail(nTrail,2);
    else
        % signed distances to destination
        Di = i2-i;
        Dj = j2-j;
%         newDist = abs(Di-diMoves) + abs(Dj-djMoves);
%         newDist(~moves) = Inf;
%         [minDist,iMove] = min(newDist);
        newDsq = (Di-diMoves).^2 + (Dj-djMoves).^2;
         newDsq(~moves) = Inf;
         [minDsq,iMove] = min(newDsq);
        i = i + diMoves(iMove);
        j = j + djMoves(iMove);
        nTrail=nTrail+1;
        trail(nTrail,:)=[i j];
        T1(i,j)=nTrail;
        B1(i,j) = 3;
        % see if we're adjacent to any visited location other than our last stop - if so, shorten the trail:
        [trail,nTrail,T1] = shortCut(i,j,trail,nTrail,T1);
        % !!!TODO!!! insert a check here for clear line-of-sight to destination
        %            or maybe to any point of target voltage
        if V1(i,j)==v || (i==i2 && j==j2)
            arrived=true;
            break;
        end
    end
%     dsq = (i-i2)^2+(j-j2)^2;
%     % we have at most 4 possible moves
%     dx1 = sign (i2-i); 
%     dy1 = sign(j2-j);
%     if abs(dx)>abs(dy) 
%         dx=dx1;
%         dy=0;
%     elseif abs(
end

if arrived
    W=zeros(nTrail-1,4);
    W(:,1) = trail(1:nTrail-1,1);
    W(:,2) = trail(1:nTrail-1,2);
    W(:,3) = trail(2:nTrail,1);
    W(:,4) = trail(2:nTrail,2);
    W=W-1; % correct for initial padding of board
    [V,Vlive] = updateV(V,Vlive,W,V(i1-1,j1-1));

else
    W=[];
end

return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [trail,nTrail,T1] = shortCut(i,j,trail,nTrail,T1);
% Check if we're adjacent to any visited location other than our very last
% stop - if so, shorten the trail.
%
% !!!!!TODO!!!!!! NEED TO CHECK WE don't cut out GOOD PINS we visited ON THE WAY
if T1(i-1,j)>0 && T1(i-1,j)<nTrail-1
    iTrail = T1(i-1,j);  % step number on trail
    for k= iTrail+1:nTrail
        T1(trail(k,1),trail(k,2))=0;
    end
    nTrail=iTrail+1;
    trail(nTrail,:) = [i j];
    T1(i,j)=nTrail;
elseif T1(i+1,j)>0 && T1(i+1,j)<nTrail-1
    iTrail = T1(i+1,j);  % step number on trail
    for k= iTrail+1:nTrail
        T1(trail(k,1),trail(k,2))=0;
    end
    nTrail=iTrail+1;
    trail(nTrail,:) = [i j];
    T1(i,j)=nTrail;
elseif T1(i,j-1)>0 && T1(i,j-1)<nTrail-1
    iTrail = T1(i,j-1);  % step number on trail
    for k= iTrail+1:nTrail
        T1(trail(k,1),trail(k,2))=0;
    end
    nTrail=iTrail+1;
    trail(nTrail,:) = [i j];
    T1(i,j)=nTrail;
elseif T1(i,j+1)>0 && T1(i,j+1)<nTrail-1
    iTrail = T1(i,j+1);  % step number on trail
    for k= iTrail+1:nTrail
        T1(trail(k,1),trail(k,2))=0;
    end
    nTrail=iTrail+1;
    trail(nTrail,:) = [i j];
    T1(i,j)=nTrail;
end
return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [W,V,Vlive] = pathfinder1(loc1,loc2,V,Vlive,v);

% return a series of moves (links) from loc1 to loc2 on the board B
% this is pathfinder1 - looks for simple horiz and vert paths only

i1=loc1(1);
j1=loc1(2);
i2=loc2(1);
j2=loc2(2);

W=[];

okLocs = V>0 & V~=v;


% check for very special case - pins are adjacent
if abs(i1-i2)+abs(j1-j2)==1   
    W = [i1 j1 i2 j2]; 
    [V,Vlive] = updateV(V,Vlive,W,v);
    return
end

dir=1;

% less special - on the same row
if i1==i2
    if j2<j1
        t=j1; j1=j2; j2=t;          
        t=i1; i1=i2; i2=t;  
        dir = -1;
        %[j2,j1]=copyVar(j1,j2); [i2,i1]=copyVar(i1,i2);
    end
    if sum(okLocs(i1,j1+1:j2-1))==0     % clear path between them
        if dir==-1
            if any(Vlive(i1,j1+1:j2-1)==v)
                j1 = j1+find(Vlive(i1,j1+1:j2-1)==v,1,'last');   % stop if we hit a live wire before our destination
            end
        else
            if any(Vlive(i1,j1+1:j2-1)==v)
                j2 = j1+find(Vlive(i1,j1+1:j2-1)==v,1,'first');   % stop if we hit a live wire before our destination
            end
        end
         L = ones(j2-j1,1);   % length of path
       W = [ i1*L (j1:j2-1)' i2*L (j1+1:j2)' ];
        [V,Vlive] = updateV(V,Vlive,W,v);
        return
    else
        return
    end
end

% less special - on the same column
if j1==j2
    if i2<i1
        t=j1; j1=j2; j2=t;          
        t=i1; i1=i2; i2=t;  
        dir = -1;
    end
    if sum(okLocs(i1+1:i2-1,j1)) == 0    % clear path between them
        if dir==-1
            if any(Vlive(i1+1:i2-1,j1)==v)
                i1 = i1+find(Vlive(i1+1:i2-1,j1)==v,1,'last');   % stop if we hit a live wire before our destination
            end
        else
            if any(Vlive(i1+1:i2-1,j1)==v)
                i2 = i1+find(Vlive(i1+1:i2-1,j1)==v,1,'first');   % stop if we hit a live wire before our destination
            end
        end
        L = ones(i2-i1,1);   % length of path
        W = [ (i1:i2-1)' j1*L (i1+1:i2)' j2*L ];
        [V,Vlive] = updateV(V,Vlive,W,v);
        return
    else
        return
    end
end

% if we get this far, pins are not on the same row or col. 
% Try the 2 possible routes around the sides of a rectangle, using this function
% recursively to get to the corner.

if ~okLocs(i1,j2)
    [W1,V1,Vl1] = pathfinder1([i1 j1],[i1 j2],V,Vlive,v);
    if any(W1)
        [W2,V2,Vl2] = pathfinder1([i1 j2],[i2 j2],V1,Vl1,v);
        if any(W2)
            W=[W1; W2];
            V = V2;
            Vlive = Vl2;
            return
        end
    end
end
if isempty(W) && ~okLocs(i2,j1)
    [W1,V1,Vl1] = pathfinder1([i1 j1],[i2 j1],V,Vlive,v);
    if any(W1)
        [W2,V2,Vl2] = pathfinder1([i2 j1],[i2 j2],V1,Vl1,v);
        if any(W2)
            W=[W1; W2];
            V =V2; 
            Vlive = Vl2;
            return
        end
    end
end

% if we get this far, pathfinder1 has failed 

return % end of pathfinder1
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



function [V,Vlive] = updateV(V,Vlive,W,v)
% Update the voltage map with a new set of connectors.
% We need to be told the voltage (pin value) v because the order of links is
% unreliable - we can't infer it from that. 
n = size(W,1);
%v = V(W(1,1),W(1,2));

V(W(1,1),W(1,2))=v;
Vlive(W(1,1),W(1,2))=v;
% this loop could be faster
for i=1:n;
    V(W(i,3),W(i,4)) = v;
    Vlive(W(i,3),W(i,4)) = v;
end

return

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function[b1,b2] = copyVar(a1,a2)
b1=a1;
b2=a2;
return