Solution

particles.c:

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "terminal_draw.h"
 
typedef struct
{
    int x;
    int y;
    int dx;
    int dy;
    Color color;
} Particle;
 
void draw_border(int width, int height)
{
    for (int x = 0; x < width; ++x)
    {
        draw_char(x, 0, '-', BLUE);
        draw_char(x, height - 1, '-', BLUE);
    }
    for (int y = 1; y < height - 1; ++y)
    {
        draw_char(0, y, '|', BLUE);
        draw_char(width - 1, y, '|', BLUE);
    }
}
 
int random_in_range(int min, int max)
{
    if (min > max)
    {
        int temp = min;
        min = max;
        max = temp;
    }
    int range = max - min + 1;
    return min + (rand() % range);
}
 
Particle random_particle(int width, int height)
{
    Particle p;
    p.x = random_in_range(1, width - 2);
    p.y = random_in_range(1, height - 2);
    p.dx = random_in_range(-1, 1);
    p.dy = random_in_range(-1, 1);
    p.color = (Color)random_in_range(BLACK, WHITE);
    return p;
}
 
void draw_particles(Particle *particles, int count)
{
    for (int i = 0; i < count; ++i)
    {
        draw_char(particles[i].x, particles[i].y, '*', particles[i].color);
    }
}
 
void move_particles(Particle *particles, int count, int width, int height)
{
    for (int i = 0; i < count; ++i)
    {
        particles[i].x += particles[i].dx;
        particles[i].y += particles[i].dy;
 
        if (particles[i].x <= 0)
        {
            particles[i].x = 1;
            particles[i].dx = -particles[i].dx;
        }
        else if (particles[i].x >= width - 1)
        {
            particles[i].x = width - 2;
            particles[i].dx = -particles[i].dx;
        }
 
        if (particles[i].y <= 0)
        {
            particles[i].y = 1;
            particles[i].dy = -particles[i].dy;
        }
        else if (particles[i].y >= height - 1)
        {
            particles[i].y = height - 2;
            particles[i].dy = -particles[i].dy;
        }
    }
}
 
bool is_colliding(Particle *particles, int count, int index)
{
    if (index < 0 || index >= count)
    {
        return false;
    }
 
    for (int i = 0; i < count; ++i)
    {
        if (i != index &&
            particles[i].x == particles[index].x &&
            particles[i].y == particles[index].y)
        {
            return true;
        }
    }
    return false;
}
 
void change_colors(Particle *particles, int count)
{
    for (int i = 0; i < count; ++i)
    {
        if (is_colliding(particles, count, i))
        {
            particles[i].color = (Color)random_in_range(BLACK, WHITE);
        }
    }
}
 
int main(void)
{
    srand((unsigned int)time(NULL));
 
    int width, height;
    get_screen_size(&width, &height);
 
    int num_particles;
    printf("Enter the number of particles: ");
    if (scanf("%d", &num_particles) != 1 || num_particles <= 0)
    {
        printf("Invalid input.\n");
        return 1;
    }
 
    Particle *particles = malloc((size_t)num_particles * sizeof(Particle));
    if (particles == NULL)
    {
        printf("Memory allocation failed.\n");
        return 1;
    }
 
    for (int i = 0; i < num_particles; ++i)
    {
        particles[i] = random_particle(width, height);
    }
 
    for (int step = 0; step < 100; ++step)
    {
        clear_screen();
        draw_border(width, height);
        draw_particles(particles, num_particles);
        move_particles(particles, num_particles, width, height);
        change_colors(particles, num_particles);
        sleep_ms(300);
    }
 
    free(particles);
    particles = NULL;
    return 0;
}