import numpy as np
import matplotlib.pyplot as plt

class LennardJonesSimulation:
    def __init__(self, num_particles=5, epsilon=0.5, sigma=1.0, dt=0.001, box_size=10.0):
        self.num_particles = num_particles
        self.positions = box_size * np.random.rand(2, num_particles)  # 2D positions
        self.velocities = 0.1 * (2 * np.random.rand(2, num_particles) - 1)  # Random velocities in the range [-0.1, 0.1]
        self.epsilon = epsilon
        self.sigma = sigma
        self.dt = dt
        self.box_size = box_size

    def compute_forces(self):
        forces = np.zeros_like(self.positions)
        for i in range(self.num_particles):
            for j in range(self.num_particles):
                if i != j:
                    r = self.positions[:, i] - self.positions[:, j]
                    dist = np.linalg.norm(r)
                    F = 24 * self.epsilon * (2 * (self.sigma / dist)**12 - (self.sigma / dist)**6) * r / dist**2
                    forces[:, i] += F
        return forces

    def integrate(self, forces):
        self.velocities += forces * self.dt
        self.positions += self.velocities * self.dt
        # Apply periodic boundary conditions
        self.positions = np.mod(self.positions, self.box_size)

    def simulate(self, steps=100000):
        plt.figure(figsize=(6,6))
        for _ in range(steps):
            forces = self.compute_forces()
            self.integrate(forces)
            if _ % 20 == 0:  # Plot every 20 steps for clarity
                plt.scatter(self.positions[0], self.positions[1], s=50)
        plt.xlim(0, self.box_size)
        plt.ylim(0, self.box_size)
        plt.title("Lennard-Jones Particles Dynamics")
        plt.show()

# Create a simulation instance and run
sim = LennardJonesSimulation()
sim.simulate()
