🌠 Π§Π°ΡΡ‚ΡŒ 44: Бимуляция ВсСлСнной Π½Π° Arduino (N-Body Problem)

ΠœΡ‹ симулируСм Π³Ρ€Π°Π²ΠΈΡ‚Π°Ρ†ΠΈΠΎΠ½Π½ΠΎΠ΅ взаимодСйствиС 100 частиц Π² 2D-пространствС, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ N-Body с ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ Барнса-Π₯Π°Ρ‚!

Бимуляция Π³Π°Π»Π°ΠΊΡ‚ΠΈΠΊΠΈ:

cpp

#include <avr/random.h>
#include <avr/pgmspace.h>

#define PARTICLE_COUNT 64
#define GRAVITY 10
#define SOFTENING 2
#define TIME_STEP 1

// Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° частицы
struct Particle {
    int16_t x;
    int16_t y;
    int16_t vx;
    int16_t vy;
    int16_t mass;
};

Particle particles[PARTICLE_COUNT];

// ΠšΠ²Π°Π΄Ρ€ΠΎΠ΄Π΅Ρ€Π΅Π²ΠΎ (для ΠΎΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΠΈ Barnes-Hut)
struct QuadNode {
    int16_t x, y;           // Π¦Π΅Π½Ρ‚Ρ€ масс
    int16_t mass;           // Буммарная масса
    int16_t minX, maxX;     // Π“Ρ€Π°Π½ΠΈΡ†Ρ‹
    int16_t minY, maxY;
    uint8_t childCount;
    QuadNode* children[4];  // Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ индСксы вмСсто ΡƒΠΊΠ°Π·Π°Ρ‚Π΅Π»Π΅ΠΉ
};

QuadNode nodes[PARTICLE_COUNT * 4];
uint8_t nodeCount = 0;

// Π‘Ρ‚Ρ€ΠΎΠΈΠΌ ΠΊΠ²Π°Π΄Ρ€ΠΎΠ΄Π΅Ρ€Π΅Π²ΠΎ
void buildQuadTree(uint8_t nodeIdx, uint8_t* particleList, uint8_t count) {
    QuadNode* node = &nodes[nodeIdx];
    
    // ВычисляСм Π³Ρ€Π°Π½ΠΈΡ†Ρ‹
    int16_t minX = 32767, maxX = -32768;
    int16_t minY = 32767, maxY = -32768;
    int32_t totalMass = 0;
    int32_t cx = 0, cy = 0;
    
    for (uint8_t i = 0; i < count; i++) {
        Particle* p = &particles[particleList[i]];
        if (p->x < minX) minX = p->x;
        if (p->x > maxX) maxX = p->x;
        if (p->y < minY) minY = p->y;
        if (p->y > maxY) maxY = p->y;
        totalMass += p->mass;
        cx += p->x * p->mass;
        cy += p->y * p->mass;
    }
    
    node->minX = minX;
    node->maxX = maxX;
    node->minY = minY;
    node->maxY = maxY;
    node->mass = totalMass;
    node->x = cx / totalMass;
    node->y = cy / totalMass;
    
    // Если Π² ΡƒΠ·Π»Π΅ ΠΎΠ΄Π½Π° частица ΠΈΠ»ΠΈ малСнький Ρ€Π°Π·ΠΌΠ΅Ρ€ - останавливаСмся
    if (count <= 1 || (maxX - minX < 5 && maxY - minY < 5)) {
        node->childCount = 0;
        return;
    }
    
    // Π Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ Π½Π° 4 ΠΏΠΎΠ΄ΡƒΠ·Π»Π°
    int16_t midX = (minX + maxX) / 2;
    int16_t midY = (minY + maxY) / 2;
    
    uint8_t childLists[4][PARTICLE_COUNT];
    uint8_t childCounts[4] = {0, 0, 0, 0};
    
    for (uint8_t i = 0; i < count; i++) {
        Particle* p = &particles[particleList[i]];
        uint8_t quadrant = 0;
        if (p->x > midX) quadrant |= 1;
        if (p->y > midY) quadrant |= 2;
        childLists[quadrant][childCounts[quadrant]++] = particleList[i];
    }
    
    // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠ΅ ΡƒΠ·Π»Ρ‹
    for (uint8_t i = 0; i < 4; i++) {
        if (childCounts[i] > 0) {
            node->children[i] = &nodes[nodeCount++];
            buildQuadTree(nodeCount - 1, childLists[i], childCounts[i]);
            node->childCount++;
        } else {
            node->children[i] = NULL;
        }
    }
}

// ВычислСниС силы (Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌ Барнса-Π₯Π°Ρ‚)
void computeGravity(uint8_t particleIdx, uint8_t nodeIdx) {
    Particle* p = &particles[particleIdx];
    QuadNode* node = &nodes[nodeIdx];
    
    // Если это лист ΠΈΠ»ΠΈ частица достаточно Π΄Π°Π»Π΅ΠΊΠΎ
    int16_t dx = node->x - p->x;
    int16_t dy = node->y - p->y;
    int16_t dist2 = dx * dx + dy * dy + SOFTENING;
    
    if (node->childCount == 0 || 
        (node->maxX - node->minX) * (node->maxX - node->minX) < dist2) {
        // ВычисляСм силу ΠΊΠ°ΠΊ ΠΎΡ‚ ΠΎΠ΄Π½ΠΎΠΉ большой массы
        int16_t invDist = 1 / sqrt(dist2);
        int16_t force = node->mass * invDist;
        
        p->vx += dx * force / 1000;
        p->vy += dy * force / 1000;
    } else {
        // РСкурсивно ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΈΠΌ ΠΏΠΎ дСтям
        for (uint8_t i = 0; i < 4; i++) {
            if (node->children[i] != NULL) {
                computeGravity(particleIdx, node->children[i] - nodes);
            }
        }
    }
}

// Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ Π³Π°Π»Π°ΠΊΡ‚ΠΈΠΊΠΈ
void initGalaxy() {
    for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
        // Π‘ΠΏΠΈΡ€Π°Π»ΡŒΠ½Π°Ρ Π³Π°Π»Π°ΠΊΡ‚ΠΈΠΊΠ°
        float angle = random(0, 360) * 3.14159 / 180;
        float radius = random(0, 100) * random(0, 100) / 100;
        particles[i].x = radius * cos(angle);
        particles[i].y = radius * sin(angle);
        particles[i].vx = -radius * sin(angle) * 0.1;
        particles[i].vy = radius * cos(angle) * 0.1;
        particles[i].mass = random(10, 100);
    }
}

// Бимуляция Π³Π°Π»Π°ΠΊΡ‚ΠΈΠΊΠΈ
void simulateGalaxy() {
    // Π‘Ρ‚Ρ€ΠΎΠΈΠΌ ΠΊΠ²Π°Π΄Ρ€ΠΎΠ΄Π΅Ρ€Π΅Π²ΠΎ
    nodeCount = 0;
    uint8_t allParticles[PARTICLE_COUNT];
    for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
        allParticles[i] = i;
    }
    buildQuadTree(nodeCount++, allParticles, PARTICLE_COUNT);
    
    // ВычисляСм силы
    for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
        computeGravity(i, 0);
    }
    
    // ОбновляСм ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ
    for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
        particles[i].x += particles[i].vx * TIME_STEP / 1000;
        particles[i].y += particles[i].vy * TIME_STEP / 1000;
    }
}

// ΠžΡ‚Ρ€ΠΈΡΠΎΠ²ΠΊΠ° Π³Π°Π»Π°ΠΊΡ‚ΠΈΠΊΠΈ Π½Π° OLED
void renderGalaxy() {
    // ΠžΡ‡ΠΈΡ‰Π°Π΅ΠΌ экран
    clearScreen();
    
    for (uint8_t i = 0; i < PARTICLE_COUNT; i++) {
        uint8_t screenX = 64 + particles[i].x / 4;
        uint8_t screenY = 32 + particles[i].y / 4;
        if (screenX < 128 && screenY < 64) {
            drawPixel(screenX, screenY, 1);
        }
    }
    
    updateDisplay();
}

void setup() {
    initGalaxy();
    initOLED();
}

void loop() {
    for (uint8_t i = 0; i < 10; i++) {
        simulateGalaxy();
    }
    renderGalaxy();
    delay(50);
}

Π’Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΡ‚ΡŒΡΡ

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *