Down the drain it goes
Mathematica
The 2D code generates a graph showing the path of the water. I displayed the vertex numbers for convenient cross-checking with the 3D display.  Normally the vertex numbers would be hidden.
Input:
r=10;c=7;
result=flow2D[{r,c},3]

Actually, the result contains multiple objects. The first object, result[[1]], is the 2D graph shown here.


The 3 dimensional pipes are Tube's  (3D lines) plotted in 3 space.  The coordinates computed based on the vertices of the 2D graph along a 3rd coordinate that was randomly generated (This allows the pipes to assume different positions along y each time the code is run.)
The axes are displayed to help the reader convince herself that the 3D rendering is indeed based on the 2D rendering.
The 3D input of interest here is:
Graphics3D[{CapForm[None],verticalPipes,allRungs,Darker@Red,connections},
ImageSize-&gt;600,Axes-&gt; True,Ticks-&gt;{Range[1,2 r,2],Range[c],Range[10]},ViewPoint-&gt;{0,-2,1.5}]

The number or rows, columns and entry column are taken from the 2D code.  They do not have to be re-entered.

2D Code
In the coming days I will document and tidy up the code for 2D and 3D.
flow2D[{rows_,columns_},startColumn_]:=
Module[{r=rows,c=columns,g,j,h,ends,middle,midcuts,direction=&quot;down&quot;,turns=0,path,rungs},

   (*complete gridgraph*)
g=GridGraph[{r,c},VertexSize-&gt; Medium,GraphStyle-&gt;&quot;Prototype&quot;,EdgeStyle-&gt;&quot;Thick&quot;,
  VertexLabels-&gt;&quot;Name&quot;,ImagePadding-&gt; 30,ImageSize-&gt;470];

(*horizontal pipes that must be removed*)
ends=Table[r(c1-1)+r1\[UndirectedEdge] r(c1)+r1,{c1,1,c-1},{r1,{1,r}}];

(*horizontal pipes to consider removing *)
middle=Table[r(c1-1)+r1\[UndirectedEdge] r(c1)+r1,{c1,1,c-1},{r1,2,r-1}];
midcuts=RandomSample[#,RandomInteger[Round[{r/15,2r/5}]]]&amp;/@middle;

rungs=Flatten[midcuts(*Join[ends,midcuts]*)];

j=EdgeDelete[g,Flatten[Join[ends,midcuts]]];

h[path_]:= Module[{start=path[[-1]],right,left,up,down,newnodes}, 
     {v=NeighborhoodGraph[j,start,1,(*VertexLabels\[Rule]&quot;Name&quot;,*)ImagePadding-&gt;25],
     VertexList[v]};newnodes=Complement[VertexList[v],path];
     If[newnodes=={},path,  
     h[Append[path,
  Switch[direction,
   &quot;down&quot;,Which[
     MemberQ[newnodes,start+r],(turns++;direction=&quot;right&quot;;start+r),
     MemberQ[newnodes,start-r],(turns++;direction=&quot;left&quot;;start-r),
     MemberQ[newnodes,start-1],start-1],
   &quot;right&quot;,Which[
     MemberQ[newnodes,start-1],(turns++;direction=&quot;down&quot;;start-1),
     MemberQ[newnodes,start+r],start+r],  
   &quot;left&quot;,Which[
     MemberQ[newnodes,start-1],(turns++;direction=&quot;down&quot;;start-1),
     MemberQ[newnodes,start-r],start-r]
    ]]]]];
{HighlightGraph[j,path=h[{r*startColumn}],ImageSize-&gt;300],path,rungs,ends,midcuts}]

convert[node_,r_,c_]:=Append[footing[[Quotient[node-1,r]+1]],Mod[node-1,r]+1(*Mod[node,r]*)]
connect[a_\[UndirectedEdge]b_,r_,c_]:=Tube[Line[{convert[a,r,c],convert[b,r,c]}],0.2]

3D Code and results
r=10;c=7;
result=flow2D[{r,c},3];
g2D=result[[1]];
path2D=result[[2]];
\[AliasDelimiter]
xScale=2;
footing = {#, RandomInteger[{1, 6}]} &amp; /@ Range[1,xScale c, xScale];
verticalPipes=Tube[Line[{Append[#,1],Append[#,r]}],.19]&amp;/@footing;
Graphics3D[{CapForm[None],verticalPipes},ImageSize-&gt;600,Axes-&gt;True,AxesEdge-&gt;Automatic,ViewPoint-&gt;{0,-2,1.5},
Ticks-&gt;{Range[1,2 r,2],Range[c],Range[10]}];

path3D=UndirectedEdge@@@Partition[Riffle[stops=path2D,Rest@stops],2];
allRungs=connect[#,r,c]&amp;/@rungs;
connections=connect[#,r,c]&amp;/@path3D;

path2D;
g2D
Graphics3D[{CapForm[None],verticalPipes,allRungs,Darker@Red,connections},
ImageSize-&gt;600,Axes-&gt; True,Ticks-&gt;{Range[1,2 r,2],Range[c],Range[10]},ViewPoint-&gt;{0,-2,1.5}]