private bool processRank(Particle p, Rank rank, ref Dictionary<Particle, Rank> ranks)
        {
            _moveAttempts[p] = true;
            bool particleMoved = false;

            // Try to move the Particle in its preferred direction
            Direction dir = rank.MaxDirection;
            int offset = Math.Sign(rank.Pulls[dir]);
            bool canMove = canMoveInDirection(p, rank, dir, offset, ref ranks);
            if (canMove) {
                moveParticle(p, dir, offset);
                particleMoved = true;
            }

            // If unsuccessful, try to move in the other direction
            else {
                dir = (rank.MaxDirection == Direction.Vertical) ? Direction.Horizontal : Direction.Vertical;
                offset = Math.Sign(rank.Pulls[dir]);
                canMove = canMoveInDirection(p, rank, dir, offset, ref ranks);
                if (canMove) {
                    moveParticle(p, dir, offset);
                    particleMoved = true;
                }
            }

            // Either way, this Particle has been processed, so remove it from the Dictionary
            ranks.Remove(p);
            _moveAttempts[p] = false;
            return particleMoved;
        }
 // HELPER FUNCTIONS
 private void initialize()
 {
     // Add a random number of random particles
     for (int p = 0; p < ParticleCount; ++p) {
         int atomicNum = _rand.Next(0, ElementCount);
         int x, y;
         do {
             x = _rand.Next(0, Size);
             y = _rand.Next(0, Size);
         } while (_lattice[x, y] != null);
         Particle particle = new Particle(atomicNum, x, y, Temperature);
         _lattice[x, y] = particle;
         _moveAttempts.Add(particle, false);
     }
 }
        private bool canMoveInDirection(Particle p, Rank rank, Direction dir, int offset, ref Dictionary<Particle, Rank> ranks)
        {
            int x = p.Position.X;
            int y = p.Position.Y;

            // If the neighboring position is off the lattice then retun false
            bool vertMax = (dir == Direction.Vertical);
            if ( ( vertMax && (y + offset < 0 || Size <= y + offset)) ||
                 (!vertMax && (x + offset < 0 || Size <= x + offset)) )
            {
                return false;
            }

            // Otherwise, if the position is open, return true
            Particle neighbor = (vertMax ? _lattice[x, y + offset] : _lattice[x + offset, y]);
            if (neighbor == null)
                return true;

            // Recursive case: the position is occupied, so return whether or not the neighbor can move
            if (!ranks.ContainsKey(neighbor))
                return false;
            if (_moveAttempts[p])
                return true;
            bool neighborCanMove = processRank(neighbor, ranks[neighbor], ref ranks);
            return neighborCanMove;
        }
        private Rank getParticleRank(Particle particle)
        {
            int x = particle.Position.X;
            int y = particle.Position.Y;

            // Get the pulls in all directions
            double sumLeft = 0d;
            for (int px = 0; px < x; ++px) {
                Particle other = _lattice[px, y];
                double electronegativity = (other?.Electronegativity ?? 1d);
                sumLeft += electronegativity / Math.Abs(px - x);
            }
            double sumRight = 0d;
            for (int px = x + 1; px < Size; ++px) {
                Particle other = _lattice[px, y];
                double electronegativity = (other?.Electronegativity ?? 1d);
                sumRight += electronegativity / Math.Abs(px - x);
            }
            double sumUp = 0d;
            for (int py = 0; py < y; ++py) {
                Particle other = _lattice[x, py];
                double electronegativity = (other?.Electronegativity ?? 1d);
                sumUp += electronegativity / Math.Abs(py - y);
            }
            double sumDown = 0d;
            for (int py = y + 1; py < Size; ++py) {
                Particle other = _lattice[x, py];
                double electronegativity = (other?.Electronegativity ?? 1d);
                sumDown += electronegativity / Math.Abs(py - y);
            }

            // Store the effective pull in each direction
            Dictionary<Direction, double> pulls = new Dictionary<Direction, double>() {
                { Direction.Vertical, sumDown - sumUp },
                { Direction.Horizontal, sumRight - sumLeft },
            };

            // Get the max Direction
            double max = 0d;
            Direction maxDir = Direction.Vertical;
            double absVert = Math.Abs(pulls[Direction.Vertical]);
            if (absVert > max) {
                max = absVert;
                maxDir = Direction.Vertical;
            }
            double absHorz = Math.Abs(pulls[Direction.Horizontal]);
            if (absHorz > max) {
                max = absHorz;
                maxDir = Direction.Horizontal;
            }

            // Return the rank for this Particle
            Rank r = new Rank() {
                Pulls = pulls,
                MaxDirection = maxDir,
            };
            return r;
        }
        public void moveParticle(Particle p, Direction dir, int offset)
        {
            int x = p.Position.X;
            int y = p.Position.Y;

            if (_lattice[x, y].Equals(p))
                _lattice[x, y] = null;
            bool vertMax = (dir == Direction.Vertical);
            if (vertMax) {
                _lattice[x, y + offset] = p;
                p.Position = new Point(x, y + offset);
            }
            else {
                _lattice[x + offset, y] = p;
                p.Position = new Point(x + offset, y);
            }
        }