### view 2017/day20.d @ 26:8db386034a68

day 20
author Jordi Gutiérrez Hermoso Thu, 21 Dec 2017 03:37:31 -0500
line wrap: on
line source
```
import std.stdio;
import std.string: split;
import std.conv: to;
import std.algorithm: map, filter, minElement, sum, any;
import std.array: array;
import std.math: sqrt;
import std.range: enumerate;

struct particle {
double[3] pos;
double[3] vel;
double[3] acc;
}

auto parseLine(string line) {
auto p = line.split(", ").map!(
x => x[3..\$-1].split(",").map!(to!double).array.to!(double[3])
);
particle res = {p[0], p[1], p[2]};
return res;
}

auto norm(double[3] v) {
return v[].map!(x => x*x).sum.sqrt;
}

auto unit(particle p) {
auto
pos = p.pos,
vel = p.vel,
acc = p.acc;
pos[] /= pos.norm;
vel[] /= vel.norm;
acc[] /= acc.norm;
particle u = {pos, vel, acc};
return u;
}

auto slowestParticle(particle[] particles) {
// This should use minIndex, from later Phobos
auto norms = particles.map!(p => p.acc.norm);
auto min = norms.minElement;
return norms.enumerate.filter!(x => x[1] == min).front[0];
}

void evolve(ref particle p) {
p.vel[] += p.acc[];
p.pos[] += p.vel[];
}

void collide(ref particle[] particles) {
int[double[3]] counter;
foreach(p; particles) {
counter[p.pos] = counter.get(p.pos, 0) + 1;
}
particles = particles.filter!(p => counter[p.pos] == 1).array;
}

bool canCollide(particle[] particles) {
return particles.map!(unit).any!(
function(particle p) {
// Check if the position, velocity, and acceleration fail to be
// mostly collinear
double[3] diff1 = p.pos, diff2 = p.vel;
diff1[] -= p.vel[];
diff2[] -= p.acc[];
return norm(diff1) > 0.001 || norm(diff2) > 0.001;
}
);
}

auto evolve(particle[] particles) {
while(particles.canCollide) {
collide(particles);
foreach(ref particle; particles) {
evolve(particle);
}
}
return particles.length;
}

void main(string[] args) {
auto particles = File(args[1]).byLineCopy.map!(parseLine).array;
writeln(particles.slowestParticle);
writeln(particles.evolve);
}
```