### Colliding Flow 6

Snakes grow at their heads and tails following a vector field that changes over time. They stop when they hit another snake.

```// Forked from "Colliding Flow 4" by mathrabbit
// https://turtletoy.net/turtle/26c2a0352e

// Forked from "Colliding Flow 3" by mathrabbit
// https://turtletoy.net/turtle/5515eb16b3

// Forked from "Colliding Flow 2" by mathrabbit

const RAD = 80; // min=10, max=100, step=5
const OUTER_BOUNDS = 95; // min=10, max=110, step=5 // kill particles that exceed this
const N_PARTICLES = 1500; // min=10, max=300, step=1
const ITERS = 700;
const STEPSIZE = 0.5;
const COLLIDE_DIST = 1.25; // min=0.5, max=5, step=0.5
const BOUNDS_KILL_CHANCE = 1.0;

// bools
const GROW_HEADS = 1; // min=0, max=1, step=1
const GROW_TAILS = 0; // min=0, max=1, step=1
const SQUARE_BOUNDS = 1; // min=0, max=1, step=1

//=========================================================================
// MAIN FUNCTIONS TO PLAY WITH

// x and y in the range -100 to 100
// t in the range 0 to ITERS * STEPSIZE
const SWIRL = 25; // min=-50, max=50, step=5
let field = (x, y, t) => ({
x: -x/50 + Math.sin(y / 10 + t/40) / 2 - y * SWIRL/1000,
y: -y/50 + 1 - t/5000 + Math.sin(x/10) / 10 + x* SWIRL/1000,
})

let inBoundsCircle = (p) =>
dist(0, 0, p.x, p.y) < OUTER_BOUNDS;
let inBoundsSquare = (p) =>
Math.abs(p.x) < OUTER_BOUNDS && Math.abs(p.y) < OUTER_BOUNDS;

//let inBounds = inBoundsCircle;
let inBounds = SQUARE_BOUNDS ? inBoundsSquare : inBoundsCircle;

let startNewParticle = () => {
let theta = rand(0, 2 * Math.PI);
return makeParticle(
);
}

//=========================================================================
// UTILS

let rand = (min, max) =>
Math.random() * (max - min) + min;
let randInt = (min, max) =>
Math.floor(rand(min, max));
let remap = (x, oldmin, oldmax, newmin, newmax) => {
let t = (x - oldmin) / (oldmax - oldmin);
return t * (newmax - newmin) + newmin;
}
let choose = (arr) =>
arr[randInt(0, arr.length)];
let chance = (prob) =>
Math.random() < prob;
let dist = (x1, y1, x2, y2) =>
Math.sqrt((x1-x2) * (x1-x2) + (y1-y2) * (y1-y2));
let len = (p) =>
dist(0, 0, p.x, p.y);
let normalize = (p) => {
let length = len(p);
if (length === 0) { return {x: 0, y: 0}; }
return {
x: p.x / length,
y: p.y / length,
};
}

let makeParticle = (x, y, headAlive, tailAlive) => ({
points: [{x:x, y:y}], // [head, ... history ..., tail]
tailAlive: tailAlive,
})
particle.points[0];
let getTail = (particle) =>
particle.points[particle.points.length-1];

let moveTo = (particle, isHead, x, y) => {
let p2 = {x: x, y: y};
}
let moveBy = (particle, isHead, dx, dy) => {
let p2 = {x: p.x + dx, y: p.y + dy};
}

let FAR = 99999999;
let distToTrail = (part1, isHead, part2) => {
// closest distance from p1's current head or tail to p2's whole trail
if (part1 === part2) { return FAR; }
let lowestDist = FAR;
for (let p2 of part2.points) {
lowestDist = Math.min(lowestDist, dist(
p1.x, p1.y, p2.x, p2.y
));
}
return lowestDist;
}
let distToAny = (particle, isHead, particles) => {
let lowestDist = FAR;
for (let p2 of particles) {
lowestDist = Math.min(lowestDist, distToTrail(particle, isHead, p2));
}
return lowestDist
}

//=========================================================================
// MAIN

console.log('-------------------');
let PARTICLES = [];

// add particles one at a time
let fails = 0;  // number of consecutive failures to place a particle
while (PARTICLES.length < N_PARTICLES && fails < 100) {
let particle = startNewParticle();
if (distToAny(particle, true, PARTICLES) < COLLIDE_DIST) {
fails += 1;
continue;
}
fails = 0;
PARTICLES.push(particle);
// iterate this particle
for (let iter = 0; iter < ITERS; iter++) {
let t = iter * STEPSIZE;
if (!inBounds(p) && chance(BOUNDS_KILL_CHANCE)) {
continue;
}
let force = field(p.x, p.y, t);
moveBy(particle, true, force.x * STEPSIZE, force.y * STEPSIZE);
if (distToAny(particle, true, PARTICLES) < COLLIDE_DIST) {
}
}
if (particle.tailAlive) {
let p = getTail(particle);
if (!inBounds(p) && chance(BOUNDS_KILL_CHANCE)) {
particle.tailAlive = false;
continue;
}
let force = field(p.x, p.y, t);
moveBy(particle, false, -force.x * STEPSIZE, -force.y * STEPSIZE);
if (distToAny(particle, false, PARTICLES) < COLLIDE_DIST) {
particle.tailAlive = false;
}
}
break;
}
}
}

//=========================================================================
// DRAW
// The walk function will be called until it returns false.

let numSegs = 0;
for (let particle of PARTICLES) {
numSegs += particle.points.length - 1;
}
console.log('' + numSegs + ' line segments');

Canvas.setpenopacity(1);
const turtle = new Turtle();
turtle.pendown();
function walk(ii) {
let points = PARTICLES[ii].points;
turtle.jump(points[0].x, points[0].y);
for (let jj = 1; jj < points.length; jj++) {
turtle.goto(points[jj].x, points[jj].y);
numSegs += 1;
}
return ii < PARTICLES.length - 1;
}
```