'How to move an Item inside a list of lists?

I have the following list of lists representing a matrix stored in space:

[' ', '1', '1', ' ']
[' ', '1', ' ', ' ']
[' ', '1', ' ', ' ']
[' ', ' ', ' ', ' ']

The number 1s represent an upside down L (like a gamma, "Γ"). How can I make this "object" move to the right, left, up and down as if it was a block? I move it with "asdw" keys

I tried doing this to make a RIGHT direction movement (btw it doesnt make the movement correctly), but i dont think its the best way and dont really think I can escalate it to other movements:

x = input("Movement: ")

if x == 'd':
    for i in range(4):
        for j in range(4):
            if space[i][j] == '1':
                try:
                    if space[i][j+1] == ' ':
                        space[i][j] = ' '
                        space[i][j+1] = '1'
                    if space[i][j+1] == '1' and space[i][j+2] == ' ':
                        space[i][j] = ' '
                        space[i][j+2] = '1'
                except IndexError:
                    pass

Is there any other method I could try? Or correct the one Im using? Thanks in advance



Solution 1:[1]

A list of lists is clunky for representing a matrix. Instead you can use an actual matrix-like type, the NumPy ndarray. NumPy includes a roll() function that can be used to translate a shape, so you just need to supply the directions.

Firstly, I'll use 0 and 1 for better readability.

import numpy as np

space = np.array([
    [0, 1, 1, 0],
    [0, 1, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 0, 0],
])

Then we can roll it right:

>>> np.roll(space, 1, 1)
[[0 0 1 1]
 [0 0 1 0]
 [0 0 1 0]
 [0 0 0 0]]

To do other directions, you just need to supply the right values, and you can wrap that up in a function like this:

def move_shape_in_array(array, direction):
    directions = {
        "up": (-1, 0),
        "left": (-1, 1),
        "down": (1, 0),
        "right": (1, 1),
    }
    shift, axis = directions[direction]
    return np.roll(array, shift=shift, axis=axis)
>>> move_shape_in_array(space, "right")
[[0 0 1 1]
 [0 0 1 0]
 [0 0 1 0]
 [0 0 0 0]]

But this doesn't guard against the shape going off the edge. Instead it wraps around:

>>> move_shape_in_array(space, "up")
[[0 1 0 0]
 [0 1 0 0]
 [0 0 0 0]
 [0 1 1 0]]

To actually guard against it, check if there are any 1s on the edge before moving:

edge_idx = 0 if shift == -1 else -1
edge = array[edge_idx] if axis == 0 else array[:, edge_idx]

if edge.any():
    return array

(Insert this between the shift, axis = and return lines in the function above.)

Solution 2:[2]

One way this can be done is by treating it as a matrix and applying matrix transformations. This will not prevent the figure from going off screen, but move it will and much more efficiently than what you're doing now.

For simplicity I'll convert your matrix to 1s and 0s instead, because it's simpler to work with:

import numpy as np

data = np.array([
          [0, 1, 1, 0],
          [0, 1, 0, 0],
          [0, 1, 0, 0],
          [0, 0, 0, 0],
       ])

To apply right and left translations to your overall matrix you need to perform a matrix multiplication with right and left arrays, respectively:

right = np.array([
    [0, 1, 0, 0],
    [0, 0, 1, 0],
    [0, 0, 0, 1],
    [0, 0, 0, 0],
])

left = np.array([
    [0, 0, 0, 0],
    [1, 0, 0, 0],
    [0, 1, 0, 0],
    [0, 0, 1, 0],
])

For example,

print(data, '\n')
d = np.matmul(data, right)
print(d, '\n')
d = np.matmul(d, right)
print(d)

Out:
[[0 1 1 0]
 [0 1 0 0]
 [0 1 0 0]
 [0 0 0 0]] 

[[0 0 1 1]
 [0 0 1 0]
 [0 0 1 0]
 [0 0 0 0]] 

[[0 0 0 1]
 [0 0 0 1]
 [0 0 0 1]
 [0 0 0 0]]

Obviously, the problem here is that once it hits the edge, it won't stop and the values will be lost.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2