Finding neighbors of a cell in a grid - python

Let's say I have a two dimensional grid of 10x10 cells. The top left cell has coordinates (0,0) and
the bottom right cell has coordinates (9,9).
The code below doesn't seem to function the way I want it to.
I can't figure out what I am doing wrong.
'''
X = 10
Y = 10
class Cell:
def __init__(self,x,y) -> None:
self.coordinates = (x,y)
self.neigbors = self.find_neighbors()
def find_neighbors(self):
x,y = self.coordinates
neighbors = [
(x+1,y),(x-1,y),(x,y+1),(x,y-1),(x+1,y+1),
(x+1,y-1),(x-1,y+1),(x-1,y-1)
]
for neighbor in neighbors:
if neighbor[0] < 0 or neighbor[1] < 0:
neighbors.remove(neighbor)
elif neighbor[0] >= X or neighbor[1] >= Y:
neighbors.remove(neighbor)
return neighbors
cell1 = Cell(0,0)
cell1.neigbors
# [(1, 0), (0, 1), (1, 1), (-1, 1)]
# shouldn't have (-1,1)
cell2 = Cell(9,9)
cell2.neigbors
# [(8, 9), (9, 8), (10, 8), (8, 8)]
# shouldn't have (10,8)
'''

Better not to remove items from a list while iterating over it (as already pointed out in the comments). Here's an idea where you mark the entries in the list of potential coordinates as "unwanted" then subsequently reconstruct the desired output:
MX = 10
MY = 10
def neighbours(x, y):
pn = [(x-1, y), (x+1, y), (x-1, y-1), (x, y-1),
(x+1, y-1), (x-1, y+1), (x, y+1), (x+1, y+1)]
for i, t in enumerate(pn):
if t[0] < 0 or t[1] < 0 or t[0] >= MX or t[1] >= MY:
pn[i] = None
return [c for c in pn if c is not None]
print(neighbours(5, 4))

Instead of the for-loop put this piece.
neighbors = [neighbor for neighbor in neighbors if validate_cell(neighbor)]
The function validate_cell(coordinate).
validate_cell(coordinate):
if coordinate[0] < 0 or coordinate[1] < 0:
return False
elif coordinate[0] >= X or coordinate[1] >= Y:
return False
else:
return True

Related

Game tree for Othello(Reversi) in pyhton

I have to write this function that generates a game tree for Othello (or Reversi), without using any library, that at the end gives me as an output a tuple (a,b,c) with:
a: the number of situations that result in a black win
b: the number of situations that result in a white win
c: the number of situations that result in a draw.
I have to work with these boards given as a txt file, as such:
. . W W
. . B B
W W W B
W B B W
The problem is I get a wrong output (different from the expected one). In the given exemple above, the output tuple should be (2, 16, 0), but I get (5, 7, 11). Below I will leave you the code, and I can't figure out what did I do wrong.
def generate_game_tree(filename: str):
# Load the board from the file
board = [line.split() for line in open(filename)]
# Initialize the game tree with the root node representing the current board state
game_tree = [(board, 0)]
black_count = 0
white_count = 0
draw_count = 0
# Generate the game tree by expanding the nodes in a breadth-first manner
i = 0
while i < len(game_tree):
node = game_tree[i]
board, _ = node
valid_moves = get_valid_moves(board)
for move in valid_moves:
new_board = make_move(board, move)
game_tree.append((new_board, i))
i += 1
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] == "W":
white_count += 1
if board[i][j] == "B":
black_count += 1
else:
draw_count += 1
return black_count, white_count, draw_count
def make_move(board, move):
flips = []
x, y = move
curr_color = board[x][y]
for dx, dy in [(1, 0), (1, 1), (0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1)]:
x, y = move
x += dx
y += dy
if not (0 <= x < len(board) and 0 <= y < len(board[0])):
continue
if board[x][y] != '.':
continue
while board[x][y] != curr_color:
flips.append((x, y))
x += dx
y += dy
if not (0 <= x < len(board) and 0 <= y < len(board[0])):
break
return flips
def get_valid_moves(board):
valid_moves = []
for i in range(len(board)):
for j in range(len(board[0])):
if board[i][j] != '.':
continue
flips = make_move(board, (i, j))
if flips:
valid_moves.append((i, j))
return valid_moves

How to split image to objects ("islands") using OpenCV

I have an image. It is made of 3 white "islands" on black background. I want to split this image into thouse islands. Is there a function in opencv or in numpy that does something like that? I have my implementation of that function. It works with 2d bool numpy arrays:
def get_island(img, x, y):
island = numpy.zeros_like(img, dtype=bool)
neibourghood = [(0, -1), (1, 0), (0, 1), (-1, 0)]
island[x, y] = True
img[x, y] = False
dots = [(x, y),]
while len(dots) > 0:
dots2 = dots
dots = []
for x, y in dots2:
for xs, ys in neibourghood:
x2, y2 = x + xs, y + ys
if 0 <= x2 < img.shape[0] and 0 <= y2 < img.shape[1]:
if img[x2, y2]:
img[x2, y2] = False
island[x2, y2] = True
dots.append((x2, y2),)
return island
def get_islands(img:numpy.ndarray) -> list: # <- that function
img = numpy.copy(img)
islands = []
while 1:
xa, ya = numpy.where(img)
if xa.shape[0] == 0: break
x, y = xa[0], ya[0]
islands.append(get_island(img, x, y))
return islands
Result will be list([img1, img2, img3])
But it is slow. I want to find a faster way to do that.
Sorry for my English.
There is cv2.connectedComponents function in opencv. It does what I need.
def get_islands(img):
n, labels = cv2.connectedComponents(img.astype('uint8'))
islands = [labels == i for i in range(1, n)]
return islands

sympy piecewise:i want to use array loop

sympy piecewise:i want use array loop
OK
from sympy import *
var('x')
myP1 = (0, x < -1)
myP2 = (x, x <= 1)
myP3 = (1, x >= 5)
myPw1= Piecewise( myP1,myP2,myP3 )
myPw2= Piecewise(*(myP1,myP2,myP3))
print("#",myPw1,myPw1.subs(x,6))
print("#",myPw2,myPw2.subs(x,6))
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5)) 1
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5)) 1
i want use array. (i want use Sum or use Append)
i want use array loop
TypeError
from sympy import *
var('x myP')
myP[1] = (0, x < -1)
myP[2] = (x, x <= 1)
myP[3] = (1, x >= 5)
myPw = Piecewise(*(myP[1] , myP[2] , myP[3]))
print("#",myPw,myPw.subs(x,6))
# myP[1] = (0, x < -1)
# TypeError: 'Symbol' object does not support item assignment
(2022-03-11)
i try tuple
from sympy import *
var('x')
myP=[]
myP=myP+list([(0, x < -1)])
myP=myP+list([(x, x <= 1)])
myP=myP+list([(1, x >= 5)])
myPw=Piecewise(*myP)
print("#",myPw)
myP=[]
myP=list([(0, x < -1)])+list([(x, x <= 1)])+list([(1, x >= 5)])
myPw=Piecewise(*myP)
print("#",myPw)
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5))
# Piecewise((0, x < -1), (x, x <= 1), (1, x >= 5))
You created symbols x and myP and then you tried to treat myP like a list (or something indexable) by writing myP[1] = .... Instead, create a list and assign/append values:
>>> myP = []
>>> myP.append((0, x < -1))
>>> myP.append((x, x <= 1))
>>> myP.append((1, x >= 5))
>>> myPw = Piecewise(*myP)

Keeping the path of BFS search

Given a maze, start coordinate, and end coordinate,
maze = [
[BLACK, BLACK, WHITE, WHITE],
[BLACK, WHITE, WHITE, WHITE],
[WHITE, WHITE, BLACK, WHITE],
[WHITE, WHITE, BLACK, WHITE],
]
s = Coordinate(3, 0)
e = Coordinate(0, 3)
I am trying to find the path from start to end using BFS.
Finding the path is straightforward, but I am struggling with keeping the path to the destination.
What I've tried is
directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
queue = collections.deque()
queue.append(s)
path = []
while queue:
curr = queue.popleft()
if curr == e:
path.append(curr)
return path
path.append(curr)
maze[curr.x][curr.y] = BLACK
for x, y in directions:
new_x, new_y = curr.x + x, curr.y + y
if new_x < 0 or new_y < 0 or new_x >= len(maze) or new_y >= len(maze[0]) or maze[new_x][new_y] == BLACK:
continue
queue.append(Coordinate(new_x, new_y))
Something like this, but the result prints out all the nodes that I've visited, instead of the final path. Any tips on keeping the right path and removing the node that does not belong to the final path?
Instead of maintaining a path list, you can maintain an edge_to dictionary which keeps track of which previous vertex led to visiting a certain vertex. Whenever you add something to your queue, you can can update edge_to. A modified version of your function using this approach is as follows:
Coordinate = collections.namedtuple('Coordinate', ['x', 'y'])
def find_path(s, e):
directions = [(1, 0), (0, 1), (-1, 0), (0, -1)]
queue = collections.deque()
queue.append(s)
edge_to = {s: None}
while queue:
curr = queue.popleft()
if curr == e:
return path(edge_to, curr)
maze[curr.x][curr.y] = BLACK
for x, y in directions:
new_x, new_y = curr.x + x, curr.y + y
if new_x < 0 or new_y < 0 or new_x >= len(maze) or new_y >= len(maze[0]) or maze[new_x][new_y] == BLACK:
continue
c = Coordinate(new_x, new_y)
edge_to[c] = curr
queue.append(c)
Notice the call to path(...) when you find your end vertex. That function just builds a list from the edge_to dictionary:
def path(edge_to, end):
curr = end
res = []
while curr != None:
res.append(curr)
curr = edge_to[curr]
return list(reversed(res))
For your given maze, and start and end coordinates, we get the following output:
s = Coordinate(3, 0)
e = Coordinate(0, 3)
print(find_path(s, e))
Output
[Coordinate(x=3, y=0), Coordinate(x=2, y=0), Coordinate(x=2, y=1), Coordinate(x=1, y=1), Coordinate(x=1, y=2), Coordinate(x=0, y=2), Coordinate(x=0, y=3)]
for x, y in directions:
new_x, new_y = curr.x + x, curr.y + y
if new_x < 0 or new_y < 0 or new_x >= len(maze) or new_y >= len(maze[0]) or maze[new_x][new_y] == BLACK:
continue
queue.append(Coordinate(new_x, new_y))
your queue.append(Coordinate(new_x, new_y)) is running everytime your for loops iterates.
We want this condition to only happen when our if condition runs, so lets try something like this:
for x, y in directions:
new_x, new_y = curr.x + x, curr.y + y
if new_x < 0 or new_y < 0 or new_x >= len(maze) or new_y >= len(maze[0]) or maze[new_x][new_y] == BLACK:
queue.append(Coordinate(new_x, new_y))
continue
When our if-condition is met, append it, and then continue. Let me know if this helps.

Determining neighbours of cell two dimensional list

I have a list of lists, something like
[[1, 2, 3,],[4, 5, 6,],[7, 8, 9]].
Represented graphically as:
1 2 3
4 5 6
7 8 9
I'm looking for an elegant approach to check the value of neighbours of a cell, horizontally, vertically and diagonally. For instance, the neighbours of [0][2] are [0][1], [1][1] and [1][2] or the numbers 2, 5, 6.
Now I realise, I could just do a bruteforce attack checking every value a la:
[i-1][j]
[i][j-1]
[i-1][j-1]
[i+1][j]
[i][j+1]
[i+1][j+1]
[i+1][j-1]
[i-1][j+1]
But thats easy, and I figured I can learn more by seeing some more elegant approaches.
# Size of "board"
X = 10
Y = 10
neighbors = lambda x, y : [(x2, y2) for x2 in range(x-1, x+2)
for y2 in range(y-1, y+2)
if (-1 < x <= X and
-1 < y <= Y and
(x != x2 or y != y2) and
(0 <= x2 <= X) and
(0 <= y2 <= Y))]
>>> print(neighbors(5, 5))
[(4, 4), (4, 5), (4, 6), (5, 4), (5, 6), (6, 4), (6, 5), (6, 6)]
I don't know if this is considered clean, but this one-liner gives you all neighbors by iterating over them and discarding any edge cases.
Assuming you have a square matrix:
from itertools import product
size = 3
def neighbours(cell):
for c in product(*(range(n-1, n+2) for n in cell)):
if c != cell and all(0 <= n < size for n in c):
yield c
Using itertools.product and thanks to Python's yield expression and star operator, the function is pretty dry but still readable enough.
Given a matrix size of 3, you can then (if needed) collect the neighbours in a list:
>>> list(neighbours((2,2)))
[(1, 1), (1, 2), (2, 1)]
What the function does can be visualized as follows:
mb...
from itertools import product, starmap
x, y = (8, 13)
cells = starmap(lambda a,b: (x+a, y+b), product((0,-1,+1), (0,-1,+1)))
// [(8, 12), (8, 14), (7, 13), (7, 12), (7, 14), (9, 13), (9, 12), (9, 14)]
print(list(cells)[1:])
for x_ in range(max(0,x-1),min(height,x+2)):
for y_ in range(max(0,y-1),min(width,y+2)):
if (x,y)==(x_,y_): continue
# do stuff with the neighbours
>>> a=[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> width=height=3
>>> x,y=0,2
>>> for x_ in range(max(0,x-1),min(height,x+2)):
... for y_ in range(max(0,y-1),min(width,y+2)):
... if (x,y)==(x_,y_): continue
... print a[x_][y_]
...
2
5
6
If someone is curious about alternative way to pick direct (non-diagonal) neighbors, here you go:
neighbors = [(x+a[0], y+a[1]) for a in
[(-1,0), (1,0), (0,-1), (0,1)]
if ( (0 <= x+a[0] < w) and (0 <= y+a[1] < h))]
There's no cleaner way to do this. If you really want you could create a function:
def top(matrix, x, y):
try:
return matrix[x][y - 1];
except IndexError:
return None
Here is your list:
(x - 1, y - 1) (x, y - 1) (x + 1, y - 1)
(x - 1, y) (x, y) (x + 1, y)
(x - 1, y + 1) (x, y + 1) (x + 1, y + 1)
So the horizontal neighbors of (x, y) are (x +/- 1, y).
The vertical neighbors are (x, y +/- 1).
Diagonal neighbors are (x +/- 1, y +/- 1).
These rules apply for an infinite matrix.
To make sure the neighbors fit into a finite matrix, if the initial (x, y) is at the edge, just apply one more restriction to the coordinates of neighbors - the matrix size.
>>> import itertools
>>> def sl(lst, i, j):
il, iu = max(0, i-1), min(len(lst)-1, i+1)
jl, ju = max(0, j-1), min(len(lst[0])-1, j+1)
return (il, iu), (jl, ju)
>>> lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> tup = 0, 2
>>> [lst[i][j] for i, j in itertools.product(*sl(lst, *tup)) if (i, j) != tup]
[2, 5, 6]
I don't know how elegant it seems to you, but it seems to work w/o any hard-coding.
This generates all indices:
def neighboring( array ):
nn,mm = len(array), len(array[0])
offset = (0,-1,1) # 0 first so the current cell is the first in the gen
indices = ( (i,j) for i in range(nn) for j in range(mm) )
for i,j in indices:
all_neigh = ( (i+x,j+y) for x in offset for y in offset )
valid = ( (i,j) for i,j in all_neigh if (0<=i<nn) and (0<=j<mm) ) # -1 is a valid index in normal lists, but not here so throw it out
yield valid.next(), valid ## first is the current cell, next are the neightbors
for (x,y), neigh in neighboring( l ):
print l[x][y], [l[x][y] for x,y in neigh]
If lambdas daunt you here you are .But lambdas make your code look clean.#johniek_comp has a very clean solution TBH
k,l=(2,3)
x = (0,-1,+1)
y = (0,-1,+1)
cell_u = ((k+a,l+b) for a in x for b in y)
print(list(cell_u))
Inspired by one of the previous answers.
You can use min() and max() functions to shorten the calculations:
width = 3
height = 3
[(x2, y2) for x2 in range(max(0, x-1), min(width, x+2))
for y2 in range(max(0, y-1), min(height, y+2))
if (x2, y2) != (x, y)]
Thank you to #JS_is_bad for a great hint about the neighbors. Here is the running code for this problem:
def findNeighbours(l,elem):
#This try is for escaping from unbound error that happens
#when we try to iterate through indices that are not in array
try:
#Iterate through each item of multidimensional array using enumerate
for row,i in enumerate(l):
try:
#Identifying the column index of the givem element
column=i.index(elem)
except ValueError:
continue
x,y=row,column
# hn=list(((x,y+1),(x,y-1))) #horizontal neighbours=(x,y+/-1)
# vn=list(((x+1,y),(x-1,y))) #vertical neighbours=(x+/-1,y)
# dn=list(((x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1))) #diagonal neighbours=(x+/-1,y+/-1)
#Creating a list with values that are actual neighbors for the extracted index of array
neighbours=[(x,y+1),(x,y-1),(x+1,y),(x-1,y),(x+1,y+1),(x-1,y-1),(x+1,y-1),(x-1,y+1)]
#Creating a universe of indices from given array
index_list=[(i,j) for i in range(len(l)) for j in range(len(l[i]))]
#Looping through index_list and nested loop for neighbours but filter for matched ones
# and extract the value of respective index
return_values=[l[index[0]][index[1]] for index in index_list for neighbour in neighbours if index==neighbour]
return return_values,neighbours
except UnboundLocalError:
return []
Inspired by johniek's answer here is my solution which also checks for boundaries.
def get_neighbours(node, grid_map):
row_index, col_index = node
height, width = len(grid_map), len(grid_map[0])
cells = list(starmap(lambda a, b: (row_index + a, col_index + b), product((0, -1, +1), (0, -1, +1))))
cells.pop(0) # do not include original node
cells = list(filter(lambda cell: cell[0] in range(height) and cell[1] in range(width), cells))
return cells
def numCells(grid):
x=len(grid)
y=len(grid[0])
c=0
for i in range(x):
for j in range(y):
value_=grid[i][j]
f=1
for i2 in range(max(0,i-1),min(x,i+2)):
for j2 in range(max(0,j-1),min(y,j+2)):
if (i2,j2) != (i,j) and value_<=grid[i2][j2]:
flag=0
break
if flag ==0:
break
else:
c+=1
return c
def getNeighbors(matrix: list, point: tuple):
neighbors = []
m = len(matrix)
n = len(matrix[0])
x, y = point
for i in range (x -1, x +2): #prev row to next row
for j in range(y - 1, y +2): #prev column to next col
if (0 <= i < m) and (0 <= j < n):
neighbors.append((i,j))
return neighbors
maybe you are checking a sudoku box. If the box is n x n and current cell is (x,y) start checking:
startingRow = x / n * n;
startingCol = y/ n * n

Categories