views:

289

answers:

2
void FloodFill(int layer, int x, int y, int target, int replacement)
{
    if (x < 0) return;
    if (y < 0) return;
    if (x >= _mapWidth) return;
    if (y >= _mapHeight) return;

    if (_mapLayers[layer, x, y] != target) return;

    _mapLayers[layer, x, y] = replacement;

    FloodFill(layer, x - 1, y, target, replacement);
    FloodFill(layer, x + 1, y, target, replacement);
    FloodFill(layer, x, y - 1, target, replacement);
    FloodFill(layer, x, y + 1, target, replacement);

    return;
}

This is my code so far, but when it reaches the end of the map it causes a Stack Overflow, does anybody know how to solve the problem (probably a tricky condition)?

+4  A: 

Notice that this call path exists:

(x, y) -> (x+1, y) -> (x+1-1, y) -> (x+1-1+1, y) -> ...

This is an infinite recursion, so you have stack overflow. Your checks can't deal with this. You have to perform one extra check:

if (_mapLayers[layer, x, y] == replacement) return;

Even if you have included the extra check above, note that the maximum recursion depth is (_mapWidth * _mapHeight), which can be very deep even for a small bitmap (e.g. 100 x 100).

KennyTM
maximum recursion depth is actually _mapWidth * _mapHeight. 200 is not very deep (typo I guess) :). 100*100 should not cause overflows either (unless there's already a hell of a lot of other stuff in memory), but something like 10,000*10,000 would. But the extra check is probably what the OP needs, +1.
MAK
-1, this answer is wrong in more than one way. "Linear search" does not conform to neighbourhood of the points. The test _mapLayers[layer, x, y] == replacement is not needed when there is a test target!=replacement before 'FloodFill` is called first. And the maximum recursion depth will not be more that _mapWidth*_mapHeight/2 (for some kind of spiral image).
Doc Brown
@DocBrown: 'if (_mapLayers[layer, x, y] != target) return;' does not handle the (admittedly unlikely) case where target==replacement. Yes, (_mapWidth * _mapHeight) is not the exact depth, but the depth will still be O(_mapWidth * _mapHeight), so the point raised is still valid. The only thing really wrong about the answer is the part about linear search.
MAK
@MAK: I did not wrote 'if (_mapLayers[layer, x, y] != target)' handles the admitted case, I wrote 'one should add the missing test for 'target==replacement' (but not inside the recursive routine, the test should be done by the caller of 'FloodFill). And in the current answer there is no big O. And for target!=replacement, the given call path will not be executed as shown, since the recursion will stop before. IMHO, the answer is only partially correct, and a little bit misleading.
Doc Brown
+1  A: 

First of all, you should make sure that target!=replacement (can be done once before the inital call of 'FloodFill'). Then, your algo may work, as long as _mapWidth and _mapHeight are not extraordinary large (it depends heavily on the content of your _mapLayers array). If this is a problem, you should try a non-recursive algorithm. Create a

class Point
{ 
    public int x, y;
    public Point(int newX, int newY)
    {
         x=newX;
         y=newY;
    }
}

and a

 List<Point> pointList;

Put the initial point into this list and run some kind of loop until pointList is empty: take one point out of the list, process it like above and instead of the original recursive call above put the four neighbours again into the list.

EDIT: here is the complete example, did not test it, though:

    void FloodFill2(int layer, int xStart, int yStart, int target, int replacement)
    {
        if(target==replacement)
            return;
        List<Point> pointList = new List<Point>();

        pointList.Add(new Point(xStart,yStart));
        while(pointList.Count>0)
        {
            Point p = pointList[pointList.Count-1];
            pointList.RemoveAt(pointList.Count-1);
            if (p.x < 0) continue;
            if (p.y < 0) continue;
            if (p.x >= _mapWidth) continue;
            if (p.y >= _mapHeight) continue;
            if (_mapLayers[layer, p.x, p.y] != target) continue;
            _mapLayers[layer, p.x, p.y] = replacement;

            pointList.Add(new Point(p.x - 1, p.y));
            pointList.Add(new Point(p.x + 1, p.y));
            pointList.Add(new Point(p.x, p.y - 1));
            pointList.Add(new Point(p.x, p.y + 1));
        }
    }

EDIT2: In fact, here is a suggestion to optimize the routine: avoid inserting to the list if inserting gets pointless, so:

            if(p.x>=0) 
                 pointList.Add(new Point(p.x - 1, p.y));
            if(p.x<_mapWidth-1) 
                 pointList.Add(new Point(p.x + 1, p.y));
            if(p.y>=0) 
                 pointList.Add(new Point(p.x, p.y - 1));
            if(p.y<_mapHeight-1) 
                 pointList.Add(new Point(p.x, p.y + 1));
Doc Brown