Reputation: 5522
I'm working on an AI for the aisandbox.com competition. I'm making the AI for fun, not to enter the competition (just in case I would be breaking rules by asking here if I was).
I can get a 2d map of the area by using self.level.blockHeights
, and I want to find some places where my bots can hide by finding corners in the map and having them defend there.
So my question is, given a 2d array of numbers where 0 is free space and 1 is a wall, what's the best way of finding corners?
P.s. I'm using Python
EDIT: here's an example of a map (the walls are black) and the corners to be found are in red:
Upvotes: 1
Views: 1951
Reputation: 23322
The answers so far are extremely slow (although their complexity is linear) - particularly in python, since they involve extensive looping. Also, they're not very robust to noise (though I understand that's not required here)
A nice trick to solving this problem (if you're willing to use scipy and numpy) is to apply a gaussian filter with a small kernel to the image and subtract the original image from it (taking care not to underflow). Since the corners "bleed" more into the background, in the resulting image the corners will be the pixels with highest intensity.
Here's an actual example:
import numpy
import scipy.ndimage
import scipy.misc
image= scipy.ndimage.imread('corners.png').astype(float)
image= numpy.mean( image, axis=2) #squash color channels
filtered= scipy.ndimage.filters.gaussian_filter(image,1)
subtracted= (256+image)-filtered #take care not to underflow
maximum, minimum= numpy.max(subtracted), numpy.min(subtracted)
magic= 0.48 #will depend on source image. Fool around and see what works
threshold= (maximum+minimum)/2+(maximum-minimum)*magic
thresholded= subtracted>=threshold
print list(zip(*numpy.where(thresholded)))
outputs [(190, 206), (207, 314)]
corners.png:
filtered:
thresolded:
Upvotes: 1
Reputation: 15538
From your example, one could define corners as free cells with two adjacent cells occupied by walls, the two occupied cells in a configuration North/East, North/West, South/East and South/West.
Therefore finding corners is just a matter of scanning your 2D map and look at the adjacent cell of every free cell.
I am assuming that you don't want free cells with 3 surrounding walls (like in the end of a corridor).
Assuming that you have a function isWall(x,y)
:
def isCorner(x, y) :
eastHasWall = isWall(x+1, y)
westHasWall = isWall(x-1, y)
northHasWall = isWall(x, y-1)
southHasWall = isWall(x, y+1)
wallArray = [eastHasWall, westHasWall, northHasWall, southHasWall]
wallCount = wallArray.count(true)
if wallCount == 2 :
return (eastHasWall and northHasWall) or (eastHasWall and southHasWall) or (westHasWall and northHasWall) or (westHasWall and southHasWall)
return false # other wall count are not corners
I haven't tested the code, but it should compile.
One should also be careful with the boundaries of the map. Only you can say if they are considered as walls or as free space in your settings.
Upvotes: 0
Reputation: 2250
According to your example grid, it would seem a corner can be defined by 0
that is surrounded by at least 2 1
.
You could begin by writing this definition in a dumb implementation at first (like I did below intentionally), and then maybe improve it by thinking about performance, for example.
2
represents a corner in this implementation
Example Python implementation:
g = [[1,1,1,0],
[1,0,0,0],
[1,0,0,0],
[1,0,1,0],
[1,1,1,1]]
width = len(g[0])
height = len(g)
for i in range(height):
for j in range(width):
if g[i][j] != 0:
continue
around = [(i-1,j),(i+1,j),(i,j-1),(i,j+1)]
walls = 0
for (x,y) in around:
if x < 0 or x >= height or y < 0 or y >= width:
#Outside, count as wall
walls += 1
elif g[x][y] == 1:
walls += 1
if walls in [2,3]: # 4 would be inaccessible
g[i][j] = 2
Output:
[1, 1, 1, 2]
[1, 2, 0, 0]
[1, 0, 0, 0]
[1, 2, 1, 2]
[1, 1, 1, 1]
Upvotes: 1
Reputation: 31813
pseudo code
function isCorner(x, y) {
wallsFound = 0;
if (withinBounds(x + 1, y) && grid[x + 1][y] == 1)
wallsFound++
if (withinBounds(x - 1, y) && grid[x - 1][y] == 1)
wallsFound++
if (withinBounds(x, y + 1) && grid[x][y + 1] == 1)
wallsFound++
if (withinBounds(x, y - 1) && grid[x][y - 1] == 1)
wallsFound++
return wallsFound == 2 || wallsFound == 3;
}
function withinBounds(x, y) {
return x >= 0
&& x < rows.len
&& y >= 0
&& y < cols.len
}
for i = 0 to rows.len
for j = 0 to cols.len
if isCorner(x, y)
// do something
You could change &&
to ||
in isCorner if you want the edge of the map to count as a wall.
Upvotes: 0