I'm working on sort of automaton where there is one red with "state = 2" square which checks neighboring squares in order up, left, down, right and if one of them is has "state = 0" it will change its to "state = 2" and their state will change to 1. The issue is, when the red square is "going" left and up it doesn't show up. Also when i changed the code to render at the begging of the update function of squares class it looked like there were 2 squares that had "state = 2".
grid_size = 50
updated = False
class squares:
def __init__(self,position,state = 0):
self.position = position
self.state = state
self.operation_done = False
def change_state(self):
self.state = 2
def update(self):
global updated
if not updated:
if self.state == 2:
self.operation_done =False
if self.position[1] - grid_size >= 0 and not self.operation_done:
up = (self.position[0], self.position[1] - grid_size)
neighbor_up = squares_dict[up]
if neighbor_up.state == 0:
neighbor_up.change_state()
self.operation_done = True
self.state = 1
updated = True
if self.position[0] - grid_size >= 0 and not self.operation_done:
left = (self.position[0] - grid_size, self.position[1])
neighbor_left = squares_dict[left]
if neighbor_left.state == 0:
neighbor_left.change_state()
self.operation_done = True
self.state = 1
updated = True
if self.position[1] + grid_size <= 450 and not self.operation_done:
down = (self.position[0], self.position[1] + grid_size)
neighbor_down = squares_dict[down]
if neighbor_down.state == 0:
neighbor_down.change_state()
self.operation_done = True
self.state = 1
updated = True
if self.position[0] + grid_size <= 450 and not self.operation_done:
right = (self.position[0] + grid_size, self.position[1])
neighbor_right = squares_dict[right]
if neighbor_right.state == 0:
neighbor_right.change_state()
self.operation_done = True
self.state = 1
updated = True
if self.state == 2:
pygame.draw.rect(screen_display,(255,0,0), (*self.position,grid_size,grid_size))
elif self.state == 1:
pygame.draw.rect(screen_display,(255,255,255), (*self.position,grid_size,grid_size))
elif self.state == 0:
pygame.draw.rect(screen_display,(0,0,0), (*self.position,grid_size,grid_size))
I have no idea whats the issue as it works right when it "goes" right and down.
I think the issue is that your code is advancing the state
of the square while it's still processing the world. So say you update the east-neighbour, but the very next cell to process is x + 1
, and the value of the state has just been reset!
You need to keep the "current state" and the "next, calculated state" separate.
What I have implemented below is keeping two states, self.state
and self.next_state
. As the update-algorithm proceeds through the grid, tests are made on self.state
but changes stored in self.next_state
. Once the entire grid-world is processed, then squares can switch to the next state.
I had to guess at what your program was supposed to do, maybe I got those parts wrong. Apologies in advance.
import pygame
import random
# Window size
WINDOW_WIDTH = 500
WINDOW_HEIGHT = 500
UPDATE_MILLIS = 1200 # time between updates
GRID_CELLS = 10 # N x N sized world-grid
GRID_SIZE = 50 # How biug cells are drawn in the window
class Square:
def __init__(self,position,state = 0):
self.position = position # ( column, row ) position in the world grid
self.state = state # Current state used for calculations
self.next_state = state # The next state we become
# References to our neighbours, with grid wrap-around
self.north = None
self.east = None
self.south = None
self.west = None
def map_neighbours( self, world_grid, grid_width, grid_height ):
""" Link references to other cells in the grid """
x, y = self.position
# NORTH
if ( y == 0 ):
self.north = world_grid[grid_height-1][x] # wrap-around top->bottom
else:
self.north = world_grid[y-1][x]
# EAST
if ( x == grid_width-1 ):
self.east = world_grid[y][0]
else:
self.east = world_grid[y][x+1]
# SOUTH
if ( y == grid_height-1 ):
self.south = world_grid[0][x]
else:
self.south = world_grid[y+1][x]
# WEST
if ( x == 0 ):
self.west = world_grid[y][grid_width-1]
else:
self.west = world_grid[y][x-1]
def update(self):
# Deternime the next state.
# If I am state==2, and my neighbour is state==0 -> switch to state:=1
neighbours = [ self.north, self.east, self.south, self.west ]
for neighbour in neighbours:
if ( self.state == 2 and neighbour.state == 0 ):
self.next_state = 1
neighbour.next_state = 2 # TODO: re-write in terms of "my" state, don't change neighbour, it should change itself
def advance_state( self ):
""" Move to the target state """
self.state = self.next_state
def __str__( self ):
s = "Square at (%3d,%3d) state=%d" % ( self.position[0], self.position[1], self.state )
return s
def draw( self, surface ):
if self.state == 2:
colour = (255,0,0)
elif self.state == 1:
colour = (255,255,255)
elif self.state == 0:
colour = (0,0,0)
else:
colour = (0,255,255) # error, should not happen
x,y = self.position
pygame.draw.rect( surface, colour, (x*GRID_SIZE, y*GRID_SIZE, GRID_SIZE, GRID_SIZE ) )
###
### MAIN
###
pygame.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE )
pygame.display.set_caption("Squares")
# Define some Squares in a World
world_grid = []
for y in range( GRID_CELLS ):
world_grid.append( [] ) # empty row
for x in range( GRID_CELLS ):
position = ( x, y )
state = random.randint( 0, 2 )
world_grid[y].append( Square( position, state ) )
#print( "Added Square: " + str( world_grid[y][-1] ) )
# we can't find our neighbours until all cells are filled
for y in range( GRID_CELLS ):
for x in range( GRID_CELLS ):
world_grid[y][x].map_neighbours( world_grid, GRID_CELLS, GRID_CELLS )
# Main loop
clock = pygame.time.Clock()
next_update = pygame.time.get_ticks() + UPDATE_MILLIS # time in future to update states
running = True
while running:
time_now = pygame.time.get_ticks()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
running = False
# Update the state-transition algorithm every period
if ( time_now > next_update ):
next_update = time_now + UPDATE_MILLIS # reschedule next update
# Do the update
for y_row in world_grid:
for sq in y_row:
sq.update() # calculate all the next states
for y_row in world_grid:
for sq in y_row:
sq.advance_state() # now calc is done, switch to new state (if any)
# Draw and update the world_grid
for y_row in world_grid:
for sq in y_row:
sq.draw( window )
pygame.display.flip()
# Clamp FPS
clock.tick(60)
pygame.quit()