Returns a set of the (x, y) points of a filled in area.

    `points` is an iterable of (x, y) tuples of an arbitrary shape.

    `startx` and `starty` mark the starting point (likely inside the
    arbitrary shape) to begin filling from.

    >>> drawPoints(polygon(5, 5, 4, 5))
    ,,,O,,,
    ,,O,O,,
    ,O,,,O,
    O,,,,,O
    O,,,,,O
    O,,,,,O
    ,O,,,O,
    ,OOOOO,
    >>> pentagonOutline = list(polygon(5, 5, 4, 5))
    >>> floodFill(pentagonOutline, 5, 5)
    {(7, 3), (4, 7), (4, 8), (5, 6), (6, 6), (7, 7), (6, 2), (5, 1), (3, 7), (2, 5), (8, 5), (5, 8), (6, 7), (3, 3), (5, 5), (7, 6), (4, 4), (6, 3), (3, 6), (3, 4), (8, 6), (6, 4), (5, 4), (2, 6), (4, 5), (5, 2), (7, 5), (4, 2), (6, 5), (5, 3), (3, 5), (6, 8), (4, 6), (5, 7), (3, 8), (7, 4), (4, 3), (7, 8), (2, 4), (8, 4)}
    >>> drawPoints(floodFill(pentagonOutline, 5, 5))
    ,,,O,,,
    ,,OOO,,
    ,OOOOO,
    OOOOOOO
    OOOOOOO
    OOOOOOO
    ,OOOOO,
    ,OOOOO,

def floodFill(points, startx, starty):
    """
    Returns a set of the (x, y) points of a filled in area.

    `points` is an iterable of (x, y) tuples of an arbitrary shape.

    `startx` and `starty` mark the starting point (likely inside the
    arbitrary shape) to begin filling from.

    >>> drawPoints(polygon(5, 5, 4, 5))
    ,,,O,,,
    ,,O,O,,
    ,O,,,O,
    O,,,,,O
    O,,,,,O
    O,,,,,O
    ,O,,,O,
    ,OOOOO,
    >>> pentagonOutline = list(polygon(5, 5, 4, 5))
    >>> floodFill(pentagonOutline, 5, 5)
    {(7, 3), (4, 7), (4, 8), (5, 6), (6, 6), (7, 7), (6, 2), (5, 1), (3, 7), (2, 5), (8, 5), (5, 8), (6, 7), (3, 3), (5, 5), (7, 6), (4, 4), (6, 3), (3, 6), (3, 4), (8, 6), (6, 4), (5, 4), (2, 6), (4, 5), (5, 2), (7, 5), (4, 2), (6, 5), (5, 3), (3, 5), (6, 8), (4, 6), (5, 7), (3, 8), (7, 4), (4, 3), (7, 8), (2, 4), (8, 4)}
    >>> drawPoints(floodFill(pentagonOutline, 5, 5))
    ,,,O,,,
    ,,OOO,,
    ,OOOOO,
    OOOOOOO
    OOOOOOO
    OOOOOOO
    ,OOOOO,
    ,OOOOO,
    """

    # Note: We're not going to use recursion here because 1) recursion is
    # overrated 2) on a large enough shape it would cause a stackoverflow
    # 3) flood fill doesn't strictly need recursion because it doesn't require
    # a stack and 4) recursion is overrated.

    allPoints = set(points) # Use a set because the look ups will be faster.

    # Find the min/max x/y values to get the "boundaries" of this shape, to
    # prevent an infinite loop.
    minx = miny = maxx = maxy = None
    for bpx, bpy in points:
        if minx is None:
            # This is the first point, so set all the min/max to it.
            minx = maxx = bpx
            miny = maxy = bpy
            continue
        if bpx < minx:
            minx = bpx
        if bpx > maxx:
            maxx = bpx
        if bpy < miny:
            miny = bpy
        if bpy > maxy:
            maxy = bpy

    pointsToProcess = [(startx, starty)]
    while pointsToProcess:
        x, y = pointsToProcess.pop()

        # Process point to right left of x, y.
        if x + 1 < maxx and (x + 1, y) not in allPoints:
            pointsToProcess.append((x + 1, y))
            allPoints.add((x + 1, y))
        # Process point to the left of x, y.
        if x - 1 > minx and (x - 1, y) not in allPoints:
            pointsToProcess.append((x - 1, y))
            allPoints.add((x - 1, y))
        # Process point below x, y.
        if y + 1 < maxy and (x, y + 1) not in allPoints:
            pointsToProcess.append((x, y + 1))
            allPoints.add((x, y + 1))
        # Process point above x, y.
        if y - 1 > miny and (x, y - 1) not in allPoints:
            pointsToProcess.append((x, y - 1))
            allPoints.add((x, y - 1))
    return allPoints