How to correctly update the grid in falling sand simulation?

Viewed 125

So, recently I started doing some python programming, and came across a video on Youtube in which guy showcases some of his simulations made in pygame (https://www.youtube.com/watch?v=M39R2A8kuh8).

I decided to do the easiest one, the Falling Sand Simulation. I implemented eveything correctly, but when it came to updating the grid I just couldn't do it right. In the end cells are positioned correctly at the bottom of screen, but they don't fall slowly, instead they just instantly teleport there. That's happening because when for loop comes across the cell it is being updated and falling down one row down, then loop comes across that same cell once more and same thing happens

I tried fixing it with second array which holds old grid and for some reason it didn't work.

Here's the code (please ignore my bad code, just a beginner xd):

import pygame
import random
from time import sleep

pygame.init()

WIDTH, HEIGHT = 800, 800
WIN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Falling Sand Simulation")

BLACK = (0, 0, 0)
ORANGE = (158, 103, 32)

class Grid:

    def __init__(self, width, height):
        
        self.rows = int(width / 2)
        self.columns = int(width / 2)

        self.PreviousGrid = [[0 for i in range(self.columns)] for j in range(self.rows)]
        self.CurrentGrid =  [[0 for i in range(self.columns)] for j in range(self.rows)]

    def add_cell(self, xpos, ypos):

        xcell = int(xpos / 2)
        ycell = int(ypos / 2)

        self.CurrentGrid[xcell][ycell] = 1

    def update_grid(self):
        self.PreviousGrid = self.CurrentGrid
        for i in range(self.rows):
            if (i+1) != self.rows:
                for j in range(self.columns):
                    if (j+1) != self.columns:
                        if self.PreviousGrid[i][j] == 0:
                            pass
                        else:
                            if self.PreviousGrid[i][j+1] == 0:
                                self.CurrentGrid[i][j] = 0
                                self.CurrentGrid[i][j+1] = 1
                            elif self.PreviousGrid[i-1][j+1] == 0 and self.PreviousGrid[i+1][j+1] == 0:
                                self.CurrentGrid[i][j] = 0
                                choice = random.randint(0, 1)

                                if choice == 0:
                                    self.CurrentGrid[i-1][j+1] = 1
                                else:
                                    self.CurrentGrid[i+1][j+1] = 1
                            elif self.PreviousGrid[i-1][j+1] == 0:
                                self.CurrentGrid[i][j] = 0
                                self.CurrentGrid[i-1][j+1] = 1
                            elif self.PreviousGrid[i+1][j+1] == 0:
                                self.CurrentGrid[i][j] = 0
                                self.CurrentGrid[i+1][j+1] = 1

    def draw_grid(self, win):
        for i in range(self.rows):
            for j in range(self.columns):
                if self.CurrentGrid[i][j] == 0:
                    pass
                elif self.CurrentGrid[i][j] == 1:
                    pygame.draw.rect(win, ORANGE, pygame.Rect(int(i*2), int(j*2), 4, 4))

def main():
    run = True
    clock = pygame.time.Clock()

    grid = Grid(WIDTH, HEIGHT)

    update_rate = 0.05
    countdownMS = update_rate
    paused = False

    while run:
        clock.tick(30)
        WIN.fill(BLACK)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False

        sec = clock.get_rawtime()/100;
        countdownMS -= sec;

        if countdownMS < 0.0:
            grid.update_grid()
            countdownMS = update_rate
            grid.draw_grid(WIN)

            if pygame.mouse.get_pressed()[0]:
                xpos, ypos = event.pos
                grid.add_cell(xpos, ypos)
            
        pygame.display.update()

    pygame.quit()

if __name__ == '__main__':
    main()
1 Answers

You have to create a new empty grid in update_grid. Copy the bottom line of the old grid and fill the rest of the new grid depending on the previous grid:

class Grid:
    # [...]

    def update_grid(self):
        self.PreviousGrid = self.CurrentGrid

        # create a new and empty grid
        self.CurrentGrid = [[0 for i in range(self.columns)] for j in range(self.rows)]
        for i in range(self.rows):
            self.CurrentGrid[i][self.columns-1] = self.PreviousGrid[i][self.columns-1]
        
        # fill the new grid depending on the previous grid
        for i in range(self.rows):
            if i+1 < self.rows:
                for j in range(self.columns):
                    if j+1 < self.columns:
                        if self.PreviousGrid[i][j] == 1:
                            if self.PreviousGrid[i][j+1] == 0:
                                self.CurrentGrid[i][j+1] = 1
                            elif self.PreviousGrid[i-1][j+1] == 0 and self.PreviousGrid[i+1][j+1] == 0:
                                self.CurrentGrid[i+random.choice([-1, 1])][j+1] = 1
                            elif self.PreviousGrid[i-1][j+1] == 0:
                                self.CurrentGrid[i-1][j+1] = 1
                            elif self.PreviousGrid[i+1][j+1] == 0:
                                self.CurrentGrid[i+1][j+1] = 1
                            else:
                                self.CurrentGrid[i][j] = 1
Related