Last active
August 20, 2024 10:08
-
-
Save SebastianPoell/dbe5d82ac20b31fe3148d78b78c90560 to your computer and use it in GitHub Desktop.
pile_of_sand.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This script implements a simple cellular automaton to simulate sand, | |
# inspired by the game "Noita". View introduce a grid which holds | |
# boolean values (true means there is sand). Then, we define three | |
# simple rules how those cells evolve. Finally, we advance this grid | |
# over and over to get the simulation. | |
# | |
class PileOfSand | |
attr_reader :width, :height | |
def initialize(width:, height:) | |
@width, @height = width, height | |
# The grid (col, row) contains boolean values and a cell gets set | |
# to true if it is sand. | |
@grid = Array.new(width) { Array.new(height) } | |
end | |
# Sets the spawner position to generate a grain of sand | |
def start_spawn(x, y) | |
@spawner = { x: x, y: y } | |
end | |
# Removes the spawner position | |
def stop_spawn | |
@spawner = nil | |
end | |
# Advances the simulation of the cellular automaton. | |
# Each cell gets iterated and decides which cells to set/unset. | |
def advance | |
# Spawn new grain of sand if spawner is set | |
@grid[@spawner[:x]][@spawner[:y]] = true if @spawner | |
# Iterate blocks (col, row) and compute the next state of the grid. | |
# Please note, that for rows we are iterating from the bottom up to | |
# achieve a more realistic simulation. | |
@grid.size.times do |x| | |
@grid[x].size.downto(0) do |y| | |
# Do nothing for non-sand and bottom row | |
next if !@grid[x][y] || y == @grid[x].size - 1 | |
# If nothing is below, move there | |
if !@grid[x][y+1] | |
@grid[x][y+1] = true | |
@grid[x][y] = false | |
# If nothing is below/left, move there | |
elsif x > 0 && !@grid[x-1][y+1] | |
@grid[x-1][y+1] = true | |
@grid[x][y] = false | |
# If nothing is below/right, move there | |
elsif x < @grid.size - 1 && !@grid[x+1][y+1] | |
@grid[x+1][y+1] = true | |
@grid[x][y] = false | |
end | |
end | |
end | |
# Return advanced grid | |
@grid | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Use it like that:
Or use it with ruby2d:
Screen.Recording.2022-05-14.at.16.48.25.mov