Generate random

My code as of now is as below:

``````width = 200

class G:
``````

Welcome @millionish!

Iâ€™ll address the collision detection first â€“

This is kinda hacky, but itâ€™ll work. Iâ€™m using the `get()` function to detect if the adjacent cells are background-coloured (therefore, empty). Iâ€™ve drawn some points (after the checks) to indicate the coordinates Iâ€™m sampling.

``````    ...
if get(x+10, y+30) == -2960686:
print('cell below is empty')

if get(x+10, y+30) == -2960686:
print('cell to the right is empty')

if get(x+10, y+30) == -2960686:
print('cell to the left is empty')

stroke(5)
point(x+10, y+30)
point(x+30, y+10)
point(x-10, y+10)

moveDown(1)
``````

`-2960686` is the color code for `background(210)`. You can find this out by sampling any background pixel using `get()`.

To be honest, Iâ€™d approach this task differently â€“ instead, use a 2D-list to store the block locations, to check for collisions, etc. You can render the tiles using this list. Conceptually, something like:

``````cells = [
[ 0, 0, 0, 0 ],
[ 0, 0, 0, 0 ],
[ 0, 0, 0, 0 ],
[ G, 0, R, 0 ],
[ 0, B, R, 0 ],
]
``````

So, for example, I can determine whether G (`cells[3,0]`) can move another row down by checking if `cells[4,0]` is a zero.

If you prefer your current approach, thatâ€™s cool. In that case, itâ€™s probably a good to start with a class for each falling box:

``````class Box:

def __init__(self):
self.fill = (225, 0, 0)
self.x = 0
self.y = 0
...
``````

You can use this to spawn new Box instances, in random positions, using random fills, and so on.

2 Likes

If I may, @millionish can also make use of Processing built-in functions like:

• `constrain()` to keep the `x` and `y` coordinates within the boundaries of the canvas
• `createShape()` to display the grid permanently and avoid computing it at each frame
• `frameCount` to manage the descent rate of the moving rectangle

I would also second @tabreturnâ€™s suggestion regarding the collision detection part. In this case, looking for the state of the cell below the currently visited cell is probably the best way to go.

Example with 1D list:

``````W, H = 200, 400    # dimensions of canvas
nC, nR = 10, 20    # number of cols and rows
S = W/nC           # step / size of unit cell
x, y = 80, -S      # coordinates of starting cell

disabled = False   # keyboard is NOT disabled
filled = [i for i in xrange(163,180) if random(1)>.6] + range(173, 200)  # randomly fill the bottom of the grid

def setup():
size(W, H)
fill(225,0,0)
updateGrid()   # function that creates a grid showing both 'filled' and 'empty' cells

def draw():
global y, disabled

shape(grid)                # display the grid
rect(x,y,S,S)              # draw the moving rectangle (red)

t = frameCount%20 == 0     # time/tempo of descent

id = (x//S)+(y//S)*nC      # convert coordinates to 1D index of currently visited cell (red)
if id+nC not in filled:    # if no blue cell below:    (note: 'id+nC' = index of cell below)
if t and y < H-S:      #   if time to move down and empty cell below is within canvas:
y+=S               #     -> increment 'y' position

else:                      # else:
disabled = True        #   -> disable keyboard
if t:                  #   when time to move:
filled.append(id)  #     -> put id of current cell (red) in 'filled' array list
updateGrid()       #     -> update the grid accordingly
y = -S             #     -> reset 'y' position
disabled = False   #     -> re-enable keyboard

def keyPressed():
global x, y

if not disabled:
k = keyCode
if   k == 39: x+=S       # move RIGHT
elif k == 37: x-=S       # move LEFT
elif k == 40: y+=S       # move DOWN

x = constrain(x, 0, W-S) # make sure that x and y coordinates stay within the boundaries of canvas
y = constrain(y, 0, H-S)

def updateGrid():
global grid

grid = createShape(GROUP)
for i in xrange(nC*nR):
cell = createShape(RECT, (i%nC)*S, (i//nC)*S, S, S)
cell.setFill(color(0, 180, 255) if i in filled else 255)
``````

3 Likes

Hey Thank you so much for your help and efforts!!!

Also, I wish to remove those random blue filled cells at bottom and start doing that when first square falls down. It should basically retain its colours and not turn blue. How do I do that?

All the code I posted is carefully annotated. If you look at line 7 I have written `# randomly fill the bottom of the grid`. If you donâ€™t want anything at start (an unfilled grid), just delete the content of the `filled` list.

You could pick a random color at each start and save it in a list when the square is being stopped. This way, the `updateGrid()` function knows what color to assign to each grid cell for the next run.

To change the stroke width and color of the grid cells just add:

``````cell.setStrokeWeight( a float )
cell.setStroke( an int )
``````

inside the `updateGrid()` function

Full sketch (annotated)

``````W, H = 200, 400         # dimensions of canvas
nC, nR = 10, 20         # number of cols and rows
S = W/nC                # step / size of unit cell
x, y = 80, -S           # coordinates of starting cell

disabled = False        # keyboard is NOT disabled
filled = []             # list storing the cells that have been stopped
colors = [255] * nC*nR  # list storing the colors of each grid cell
curcol = color(random(80,255),random(80,255),random(80,255)) # current color (picked randomly)

def setup():
size(W, H)
updateGrid()   # function that creates a grid showing both 'filled' and 'empty' cells

def draw():
global y, curcol, disabled

shape(grid)                                                          # display the grid
fill(curcol)
rect(x,y,S,S)                                                        # draw the moving rectangle (red)

t = frameCount%20 == 0                                               # time/tempo of descent

id = (x//S)+(y//S)*nC                                                # convert coordinates to 1D index of currently visited cell (red)
if id+nC not in filled and id+nC<nC*nR:                              # if no blue cell below:    (note: 'id+nC' = index of cell below)
if t and y < H-S:                                                #   if time to move down and empty cell below is within canvas:
y+=S                                                         #     -> increment 'y' position

else:                                                                # else:
disabled = True                                                  #   -> disable keyboard
if t:                                                            #   when time to move:
filled.append(id)                                            #     -> put id of current cell (red) in 'filled' array list
colors[id] = curcol                                          #     -> store current color to 'colors' list
curcol = color(random(80,255),random(80,255),random(80,255)) #     -> pick a new color at random
updateGrid()                                                 #     -> update the grid accordingly
y = -S                                                       #     -> reset 'y' position
disabled = False                                             #     -> re-enable keyboard

def keyPressed():
global x, y

if not disabled:
k = keyCode
if   k == 39: x+=S       # move RIGHT
elif k == 37: x-=S       # move LEFT
elif k == 40: y+=S       # move DOWN

x = constrain(x, 0, W-S) # make sure that x and y coordinates stay within the boundaries of canvas
y = constrain(y, 0, H-S)

def updateGrid():
global grid

grid = createShape(GROUP)
for i in xrange(nC*nR):
cell = createShape(RECT, (i%nC)*S, (i//nC)*S, S, S)
cell.setFill(colors[i])   # fill with corresponding color
cell.setStrokeWeight(1)   # stroke width
cell.setStroke(0)         # stroke color
``````
2 Likes

Thanks so much!!! So suppose I want to restart the game after I click the mouse anywhere inside the window, how do I use mousePressed() function?
Iâ€™ve looked at many different topics in the forum and tried to get the code to work, but it wonâ€™t restart.

The mousePressed() works similarly to `keyPressed()`. Thereâ€™s no conditional statement required to detect where youâ€™re clicking (like on a button, or something) because youâ€™re happy using the entire display window â€“

``````    ...

def keyPressed():
...

def mousePressed():
global colors, filled, grid, x, y
# code to reset the game variables
# ...

...
``````
2 Likes

thanks I actually figured it out myself was pretty simple

Can you post your attempts (the code) for the 2 problems youâ€™re mentioning:

• cells removal
• void filling

This way we can better help you understand what is not functioning or missing in your script.

Now that you have asked your question here, please try not to open another thread for it. There are just a handful of people looking at the â€śPythonâ€ť tag so do not expect to have an answer within the hour.

If you really are in a hurry, feel free to look at the OpenProcessing website where plenty of examples for tetris game are available for inspiration.

Back to your question. Every time the moving square â€ścollidesâ€ť with another colored square below, you could:

• get the stack of adjacent squares sharing the same color
• if that stack exists:
• look for a specific pattern inside (4 consecutive block either horizontally or vertically)
• if that pattern is found:
• remove it
• shift down the cells above (if any)
• if no stack:
• â€śleaveâ€ť the square where it is + store its color at this location
• restart from top

Annotated example:

``````W, H = 200, 400    # dimensions of canvas
nC, nR = 10, 20    # number of cols and rows
S = W/nC           # step / size of unit cell
x, y = 80, -S      # coordinates of starting cell

disabled = False   # keyboard is NOT disabled
filled = set()     # set storing the id of cells that have been stopped

colors = [235] * nC*nR            # list storing the colors of each grid cell (all white-ish at start)
palette = (color(40,38,31),       # color palette
color(95,181,179),
color(251,177,44),
color(252,169,191))
curcol = palette[int(random(4))]  # current color picked randomly

patterns = (set(range(1,4)), set(range(nC, nC*4, nC)))  # a pattern = a set of distances from cell 0 (ex: horizontal block -> 0 to 1, to 2 and to 3, vertical block -> 0 to 10, to 20 and to 30)
dir = (-1, 1, -nC, nC)

def setup():
size(W, H)
stroke(255)
updateGrid()   # function that creates a grid showing both 'filled' and 'empty' cells

def draw():
global y, curcol, filled, disabled

shape(grid)     # display the grid
fill(curcol)    # draw the moving rectangle (red)
rect(x,y,S,S)   #

t = frameCount%20 == 0   # time/tempo of descent

id = (x//S)+(y//S)*nC                     # convert coordinates of currently visited cell to 1D index

if id+nC not in filled and id+nC<nC*nR:   # if NO occupied cell below:    (note: 'id+nC' = index of cell below)
if t and y < H-S:                     #   if time to move down and empty cell below is within canvas:
y+=S                              #     -> increment 'y' position

else:                                     # else (there IS an occupied cell below!):
disabled = True                       #   -> disable keyboard
if t:                                 #   when time to move:

s = stack(id,[])                                                    # look for cells with similar colors around (a 'stack')
to_remove = set()
if len(s)>1:                                                        # if stack of similar cells (same color) is found:
for n, i1 in enumerate(s):                                      # -> check if patterns can be found in it
d = set(abs(i2-i1) for i2 in s[n:] if i1!=i2)
for p in patterns:
if p.issubset(d):                                       # if so:
to_remove.update([i1] + map(lambda x: x+i1, p))     # -> store id of their cells in 'to_remove'

if to_remove:                                                       # if patterns have been found:
filled -= to_remove                                             # -> remove the corresponding cells from 'filled'
for i in to_remove: colors[i] = 235                             # -> make them appear white again

to_shift = set()                                                # check if there are cells above the removed cells
for i in filled:
if i<min(to_remove) and i%nC in [k%nC for k in to_remove]:

if to_shift:                                                    # if so, shift them down*
shifted = [i+nC for i in to_shift]
filled -= to_shift                                          # -> *(remove their ids form 'filled')
filled.update(shifted)                                      # -> *(replace with new shifted/incremented ids)

temp = [colors[i] for i in to_shift]                        # let's not forget to shift the corresponding 'colors' as well
for i, n in enumerate(shifted):
colors[n] = temp[i]                                     # cells below inherit the color from the cell above
for i in to_shift.difference(shifted):
colors[i] = 235                                         # cell on top becomes white

else:                                 # if NO pattern to remove:
filled.add(id)                    #  -> put id of current cell in 'filled' array list
colors[id] = curcol               #  -> store current color to 'colors' list

# IN ANY CASE, at the end DO NOT FORGET TO
curcol = palette[int(random(4))]      # -pick a new color at random
updateGrid()                          # -update the grid
y = -S                                # -reset 'y' position
disabled = False                      # -re-enable keyboard

def keyPressed():
global x, y

if not disabled:
k = keyCode
if   k == 39: x+=S       # move RIGHT
elif k == 37: x-=S       # move LEFT
elif k == 40: y+=S       # move DOWN

x = constrain(x, 0, W-S) # make sure that x and y coordinates stay within the boundaries of canvas
y = constrain(y, 0, H-S)

def updateGrid():
global grid

grid = createShape(GROUP)
for i in xrange(nC*nR):
cell = createShape(RECT, (i%nC)*S, (i//nC)*S, S, S)
cell.setFill(colors[i])   # fill with corresponding color
cell.setStrokeWeight(1)   # stroke width
cell.setStroke(255)       # stroke color

def stack(id, v):
v.append(id)

for d in dir:
nid = id+d
if 0<=nid<nC*nR and nid not in v:
if colors[nid] == curcol:
stack(nid, v)

return sorted(v)
``````
2 Likes