01020304050607080910111213141516171819202122232425

Advent of Code

2017/20

Particle Swarm

in C#

by encse

Suddenly, the GPU contacts you, asking for help. Someone has asked it to simulate too many particles, and it won't be able to finish them all in time to render the next frame at this rate.

It transmits to you a buffer (your puzzle input) listing each particle in order (starting with particle 0, then particle 1, particle 2, and so on). For each particle, it provides the X, Y, and Z coordinates for the particle's position (p), velocity (v), and acceleration (a), each in the format <X,Y,Z>.

Read the full puzzle.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace AdventOfCode.Y2017.Day20;

[ProblemName("Particle Swarm")]
class Solution : Solver {

    public object PartOne(string input) {
        var particles = Parse(input);
        return (
            from particle in particles
            orderby particle.acc.Len(), particle.vel.Len(), particle.pos.Len()
            select particle
        ).First().i;
    }

    public object PartTwo(string input) {
        var particles = Parse(input);
        var collisionTimes = (
            from p1 in particles
            from p2 in particles
            where p1.i != p2.i
            from collisionTime in p1.CollisionTime(p2)
            select collisionTime
        ).ToArray();
        var T = collisionTimes.Max();

        var t = 0;
        while (t <= T) {
            var particlesByPos = (from particle in particles orderby particle.pos.x, particle.pos.y, particle.pos.z select particle).ToArray();
            
            var particlePrev = particlesByPos[0];

            for (int i = 1; i < particlesByPos.Length; i++) {
                var particle = particlesByPos[i];
                if (particlePrev.pos.x == particle.pos.x && particlePrev.pos.y == particle.pos.y && particlePrev.pos.z == particle.pos.z) {
                    particlePrev.destroyed = true;
                    particle.destroyed = true;
                }
                particlePrev = particle;
            }

            if (particles.Any(p => p.destroyed)) {
                particles = particles.Where(particle => !particle.destroyed).ToList();
            }

            foreach (var particle in particles) {
                particle.Step();
            }

            t++;
        }
        return particles.Count;
    }


    List<Particle> Parse(string input) {
        var lines = input.Split('\n');
        return (
             from q in Enumerable.Zip(lines, Enumerable.Range(0, int.MaxValue), (line, i) => (i: i, line: line))
             let nums = Regex.Matches(q.line, "-?[0-9]+").Select(m => int.Parse(m.Value)).ToArray()
             let p = new Point(nums[0], nums[1], nums[2])
             let v = new Point(nums[3], nums[4], nums[5])
             let a = new Point(nums[6], nums[7], nums[8])
             select new Particle(q.i, p, v, a))
         .ToList();
    }
}

class Point {
    public int x;
    public int y;
    public int z;

    public int Len() => Math.Abs(x) + Math.Abs(y) + Math.Abs(z);

    public Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

class Particle {
    public int i;
    public Point pos;
    public Point vel;
    public Point acc;

    public bool destroyed = false;

    public Particle(int i, Point pos, Point vel, Point acc) {
        this.i = i;
        this.pos = pos;
        this.vel = vel;
        this.acc = acc;
    }

    public void Step() {
        (vel.x, vel.y, vel.z) = (vel.x + acc.x, vel.y + acc.y, vel.z + acc.z);
        (pos.x, pos.y, pos.z) = (pos.x + vel.x, pos.y + vel.y, pos.z + vel.z);
    }

    public IEnumerable<int> CollisionTime(Particle particle) {
        return
            from tx in CollisionTimeOnAxis(particle.acc.x - acc.x, particle.vel.x - vel.x, particle.pos.x - pos.x)
            from ty in CollisionTimeOnAxis(particle.acc.y - acc.y, particle.vel.y - vel.y, particle.pos.y - pos.y)
            from tz in CollisionTimeOnAxis(particle.acc.z - acc.x, particle.vel.z - vel.z, particle.pos.z - pos.z)
            where tx == ty && ty == tz
            select (tx);
    }

    private IEnumerable<int> CollisionTimeOnAxis(int da, int dv, int dp) =>
        SolveIntEq(da / 2, dv, dp);

    private IEnumerable<int> SolveIntEq(int a, int b, int c) {
        if (a == 0) {
            if (b == 0) {
                if (c == 0) {
                    yield return 0;
                }
            } else {
                yield return -c / b;
            }
        } else {
            var d = b * b - 4 * a * c;
            if (d == 0) {
                yield return -b / (2 * a);
            } else if (d > 0) {
                var ds = Math.Sqrt(d);
                if (ds * ds == d) {
                    yield return (int)((-b + ds) / (2 * a));
                    yield return (int)((-b - ds) / (2 * a));
                }

            }
        }
    }
}

Please ☆ my repo if you like it!

© 2025 Advent of Code is a registered trademark in the US Images provided by Bing image creator