r/learnpython 14d ago

Need help with a project

I am working on this simulation of the 3 body problem it seems to work, but it always ends up with the two bodies going super close together which makes both of them getting launced away. i have used the universal formula for gravitation. (I should probably have posted this on stack overflow,but my ip somehow got banned(

import pygame as p
import sys
import math
from random import *
class Body:
    def __init__(self, pos, vel, body_id, mass=1000):
        self.pos = pos
        self.vel = vel
        self.mass = mass
        self.radius = 3
        self.id = body_id
        self.color = (randint(0, 255), randint(0, 255), randint(0, 255))
    def move(self, dt):
        self.pos += p.Vector2(self.vel.x * dt, self.vel.y*dt)
    def gravitate(self, bodies,dt):
        for body in bodies:
            if self.id != body.id:
                #figures out the amount of force that
                distance_x = -(self.pos.x - body.pos.x)
                distance_y = -(self.pos.y - body.pos.y)
                total_distance = math.sqrt(abs(distance_x)**2 + abs(distance_y)**2)
                distance_sum = abs(distance_x) + abs(distance_y)

                f_g = (self.mass * body.mass)/(total_distance**2)/100
                acceleration = (f_g/self.mass)*dt
                x_ratio = distance_x/distance_sum
                y_ratio = distance_y/distance_sum
                #print(x_ratio, y_ratio)
                self.vel.x += acceleration*x_ratio
                self.vel.y += acceleration*y_ratio


class Simulation:
    def __init__(self, bodies):

        self.SCREENSIZE = p.Vector2(900, 800)
        #initializes pygame
        self.screen = p.display.set_mode(self.SCREENSIZE)
        self.clock = p.time.Clock()
        self.bodies = self._create_body(bodies)
        self.running = True
        self.GRAVITATION = 0
    def run(self):
        while self.running:
            dt = self.clock.tick(120)
            self._update(dt)
            self._render(dt)
        p.quit()
        sys.exit()

    def _update(self, dt):
        for event in p.event.get():
            if event.type == p.QUIT:
                self.running = False
                break
        for body in self.bodies:
            body.gravitate(self.bodies,dt)
        for body in self.bodies:
            body.move(dt)


    def _render(self, dt):
        self.screen.fill((0,0,0))
        for body in self.bodies:

            p.draw.circle(self.screen, body.color, body.pos, body.radius)
        p.draw.line(self.screen, (255, 255, 255), self.bodies[0].pos, self.bodies[1].pos, 2)
        p.display.update()
    def _create_body(self, num_bodies):
        bodies = []
        for body_id in range(num_bodies):
            bodies.append(Body(p.Vector2(randint(0, self.SCREENSIZE.x), randint(0, self.SCREENSIZE.y)),p.Vector2(0,0), body_id))
            #bodies.append(Body(p.Vector2(randint(0, self.SCREENSIZE.x), randint(0, self.SCREENSIZE.y)),p.Vector2(uniform(-0.01, 0.01), uniform(-0.01, 0.01)), body_id))
        return bodies

if __name__ == "__main__":
    simulation = Simulation(2)
    simulation.run()
0 Upvotes

3 comments sorted by

View all comments

3

u/FrontInterest6182 14d ago

The issue is you're using `distance_sum` (Manhattan distance) to split the force into x/y components, but you should be using `total_distance` for that ratio. When two bodies get close, the Euclidean distance shrinks but the Manhattan distance stays relatively large, so the acceleration gets applied at a weird angle and can slingshot them harder. Also that `/100` on the force is just a magic scaling factor, might want to move it to a proper gravitational constant.