diff --git a/xtrn/tw2/bang.js b/xtrn/tw2/bang.js
index a4d8a3667933d6c918104dc0e403600b6553a06d..5184d15bc36fdee0cb9dd5525fe3399762347186 100644
--- a/xtrn/tw2/bang.js
+++ b/xtrn/tw2/bang.js
@@ -6,13 +6,25 @@
  *
  */
 
-var sectors=1000;
+var sectors=1000;	// Total sectors in the known universe
+var warpradius=1.5;	// Ratio of radius of a sphere containing the averge
+			// volume of space per sector to radius of max jump
+			// distance
+			// Eg: if the average sector was a 10 cubic mile
+			// sphere, it would have a radius of 1.34 miles.
+			// The maximum warp distance would be warpradius*1.34
+			// or (assuming 1.5) 2.01 miles.
+			// Obviously, the units we're using are a bit bigger.
+var spin=0.1;		// Distance in known universe radii the universe
+			// using the same ration to average bolume as above
+
+var wr=Math.pow(((4/3*Math.PI)/sectors)/((4/3)*Math.PI),1/3)*warpradius;	// Calculate warp radius
+var so=Math.pow(1/sectors,1/3)*spin;	// Calculate spin offset
 var universe=[];
 var ports=[];
 var planets=[];
-var i,j;
+var i,j,k;
 var sol;
-var warpradius=1.5;
 
 function SectorDistance(other)
 {
@@ -23,6 +35,15 @@ function SectorDistance(other)
 	));
 }
 
+function WarpDistance(other)
+{
+	return(Math.sqrt(
+		Math.pow(this.X-other.X,2)+
+		Math.pow(this.Y-other.Y-so,2)+
+		Math.pow(this.Z-other.Z,2)
+	));
+}
+
 function CubicSector(x,y,z)
 {
 	this.X=x;				// -1 to 1
@@ -36,6 +57,7 @@ function CubicSector(x,y,z)
 	this.Az=Math.atan2(this.Y,this.X);	// Azimuth from coreward (Radians)
 	this.Al=Math.acos(this.Z/this.R);	// Altitude from galactic plane (Radians)
 	this.Distance=SectorDistance;
+	this.WDistance=WarpDistance;
 }
 
 function SphereSector(R, Al, Az)
@@ -47,6 +69,7 @@ function SphereSector(R, Al, Az)
 	this.Y=this.R*Math.sin(Al)*Math.sin(Az);
 	this.Z=this.R*Math.cos(Al);
 	this.Distance=SectorDistance;
+	this.WDistance=WarpDistance;
 }
 
 var sol=new CubicSector(0,0,0);
@@ -56,7 +79,7 @@ for(i=0; i<sectors; i++) {
 			universe.push(sol);
 	}
 	else {
-		var x,y,z;
+		var x,y,z,n;
 
 check:
 		do {
@@ -70,27 +93,40 @@ check:
 					log("Duplicate location");
 					continue check;
 				}
+				
 			}
-			universe.push(new CubicSector(x,y,z));
-		} while(0);
+			n=new CubicSector(x,y,z);
+			if(sol.Distance(n)>1)		// Ensure the universe is spherical
+				continue check;
+			universe.push(n);
+			break;
+		} while(1);
 	}
 }
+log("len="+universe.length);
 
 universe.sort(function(a,b) { return(a.Distance(sol) - b.Distance(sol)) });
-var wr=Math.pow((2*2*2/sectors)/((4/3)*Math.PI),1/3)*warpradius;	// warpradius 1.5 times the average sector sphere.
 var totalwarps=0;
 for(i in universe) {
 	universe[i].Warps=[];
 	for(j in universe) {
 		if(i==j)
 			continue;
-		if(universe[i].Distance(universe[j]) <= wr) {
+		if(universe[i].WDistance(universe[j]) <= wr) {
 			universe[i].Warps.push(j);
 			totalwarps++;
 		}
 	}
-	writeln(format("%f/%f/%f - %f",universe[i].X,universe[i].Y,universe[i].Z,universe[i].Distance(sol)));
-	writeln("  Warps: "+universe[i].Warps.join(', '));
 }
-log("Radius="+wr);
 log("Average warps per sector: "+(totalwarps/sectors));
+log("Total Warps: "+totalwarps);
+
+// Check for one-way warps
+var oneways=0;
+for(i in universe) {
+	for(j in universe[i].Warps) {
+		if(!universe[universe[i].Warps[j]].Warps.some(function(a,b,c) { return(a==i) } ))
+			oneways++;
+	}
+}
+log("One-way warps: "+oneways+" ("+parseInt(oneways/totalwarps*100)+"%)");