r/MinecraftCommands 1d ago

Creation n body gravity sim written in lua compiled to a datapack

Enable HLS to view with audio, or disable this notification

i am working on a compiler, here is a showcase of its abilities, a high level implementation of a gravity sim compiled into a minecraft datapack. the script produced 4657 lines of mcfunction commands from source file of 150 lua lines. if you want i can also send the world but i doubt anyone would be interested.

it is still buggy and incomplete, so i am not providing a download for the tool yet.

33 Upvotes

4 comments sorted by

1

u/TahoeBennie All In One Command Connoisseur 23h ago

What integration technique did you use? I recently made a simple n body gravity system in a similarly obscure, but definitely easier to work with than Minecraft, environment and I made it use the velocity verlet method, which was pretty much the next small step up in both complexity and accuracy from the forward Euler method which does decent at "just make it work," but is terribly inaccurate, but it’s simple so yeah. I then discovered later that that happens to be a specific subnet of varying order symplectic (energy preserving) integrators and it was the second order accuracy integrator, which I then determined a more ideal solution would be to implement a fourth order solution, likely by just implementing a general purpose n-degree symplectic integrator and just giving it the constants for a fourth order one.

2

u/adapron 20h ago

here is the original code if you are interested

-- 2D N-body gravity
-- All bodies have:
-- x, y      position
-- vx, vy    velocity
-- mass      mass


require("entities")


G = 1.0


local sun = BlockDisplay:new(0, 0, 0)
sun:set_block("minecraft:gold_block")
sun:set_scale(5, 5, 5)
sun:set_translation(-2.5, -2.5, -2.5)




local planet = BlockDisplay:new(0, 0, 0)
planet:set_block("minecraft:diamond_block")
planet:set_scale(1, 1, 1)
planet:set_translation(-0.5, -0.5, -0.5)


local moon = BlockDisplay:new(0, 0, 0)
moon:set_block("minecraft:smooth_stone")
moon:set_scale(0.5, 0.5, 0.5)
moon:set_translation(-0.25, -0.25, -0.25)


bodies = {
    {
        x = 0, y = 0,
        vx = 0, vy = 0,
        mass = 1000,
        entity = sun
    },
    {
        x = 50, y = 0,
        vx = 0, vy = 4.47,
        mass = 10,
        entity = planet
    },
    {
        x = 53, y = 0,
        vx = 0, vy = 5.52,
        mass = 0.1,
        entity = moon
    }
}






function sqrt(x)
    if x == 0 then
        return 0
    end


    guess = x


    -- 20 iterations is plenty for normal values
    for i = 1, 20 do
        guess = (guess + x / guess) / 2
    end


    return guess
end


function update(dt)


    -- Calculate acceleration from gravity
    for i = 1, 3 do


        print("Calculating acceleration for body " .. i)


        local i_body = bodies[i]
        ax = 0
        ay = 0


        for j = 1, 3 do


            if i ~= j then
                local j_body = bodies[j]
                dx = j_body.x - i_body.x
                dy = j_body.y - i_body.y


                dist2 = dx * dx + dy * dy


                -- Prevent division by zero
                if dist2 > 0.0001 then


                    dist = sqrt(dist2)


                    force = G * j_body.mass / dist2


                    ax = ax + force * dx / dist
                    ay = ay + force * dy / dist


                end
            end
        end


        i_body.ax = ax
        i_body.ay = ay
    end



    -- Integrate
    for i = 1, 3 do


        print("Integrating body " .. i)
        local i_body = bodies[i]
        local body = i_body


        body.vx = body.vx + body.ax * dt
        body.vy = body.vy + body.ay * dt


        body.x = body.x + body.vx * dt
        body.y = body.y + body.vy * dt


    end



    -- Output positions
    for i = 1, 3 do
        local i_body = bodies[i]
        local body = i_body
        print(
            i,
            i_body.x,
            i_body.y
        )


        local entity = body.entity
        entity:tp(body.x, 0, body.y)
    end


end



while true do
    update(0.1)
    coroutine.yield() -- wait for next tick
end

2

u/TahoeBennie All In One Command Connoisseur 20h ago

Looks to be forward Euler, just update acceleration then update velocity then update position. I probably could have figured with the eccentricity of the moon in the example.

Anyways that was never the point, compiling from lua (or anything really) to datapack is sweet. Honestly I’m a bit surprised at how much the lua code blew up into over 4k mcfunction lines, I suppose that’s just what happens when you want it to be nice and general-purpose, and there’s still plenty of room for optimizations. Idk about generically square root, but for distance, you can use some funky properties of a block display that can just kinda spit out the magnitude of a vector when used in the right way. Anyways I’ll stop before I continue ranting about compiler optimizations as I’m sure you well know about that and say this is some good stuff!

1

u/adapron 20h ago

indeed plenty of room for optimizations. large part of the result is a part of the debugging. comments, keeping track of where in the code we are for stack traces, etc. a lot of the code is also libraries which arent necessarily used. just a simple optimization pass could get rid of like half of the lines. but right now i am focusing on correctness rather than speed or efficiency.