diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 8baea2e3d..37b6d127f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -53,3 +53,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her - James Goytia - Amaras - Jonathan Dönszelmann +- Ishaan Verma diff --git a/contents/flood_fill/code/python/flood_fill.py b/contents/flood_fill/code/python/flood_fill.py new file mode 100644 index 000000000..0d22599f5 --- /dev/null +++ b/contents/flood_fill/code/python/flood_fill.py @@ -0,0 +1,92 @@ +from collections import namedtuple +from queue import Queue +import numpy as np + +Point = namedtuple("Point", "x y") + +def inbounds(canvas_shape, p): + return min(p) >= 0 and p.x < canvas_shape[0] and p.y < canvas_shape[1] + +def color(canvas, p, new_val): + canvas[p] = new_val + +def find_neighbors(canvas, p, old_val, new_val): + # north, south, east, west neighbors + possible_neighbors = [ + Point(p.x, p.y+1), + Point(p.x+1, p.y), + Point(p.x-1, p.y), + Point(p.x, p.y-1) + ] + + # exclude the neighbors that go out of bounds and should not be colored + neighbors = [] + for possible_neighbor in possible_neighbors: + if inbounds(canvas.shape, possible_neighbor): + if canvas[possible_neighbor] == old_val: + neighbors.append(possible_neighbor) + return neighbors + +def stack_fill(canvas, p, old_val, new_val): + if old_val == new_val: + return + + stack = [p] + + while stack: + cur_loc = stack.pop() + color(canvas, cur_loc, new_val) + stack += find_neighbors(canvas, cur_loc, old_val, new_val) + +def queue_fill(canvas, p, old_val, new_val): + if old_val == new_val: + return + + q = Queue() + q.put(p) + + color(canvas, p, new_val) + + while not q.empty(): + cur_loc = q.get() + neighbors = find_neighbors(canvas, cur_loc, old_val, new_val) + + for neighbor in neighbors: + color(canvas, neighbor, new_val) + q.put(neighbor) + +def recursive_fill(canvas, p, old_val, new_val): + if old_val == new_val: + return + + color(canvas, p, new_val) + + neighbors = find_neighbors(canvas, p, old_val, new_val) + for neighbor in neighbors: + recursive_fill(canvas, neighbor, old_val, new_val) + +def main(): + grid = np.zeros((5, 5)) + grid[2,:] = 1 + + answer = np.zeros((5, 5)) + answer[:3,] = 1 + + c0 = grid.copy() + c1 = grid.copy() + c2 = grid.copy() + + start_loc = Point(0, 0) + + recursive_fill(c0, start_loc, 0, 1) + queue_fill(c1, start_loc, 0, 1) + stack_fill(c2, start_loc, 0, 1) + + assert (c0 == answer).all() + assert (c1 == answer).all() + assert (c2 == answer).all() + + print("Tests Passed") + +if __name__ == "__main__": + main() diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 836f86897..9cf00c577 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -90,6 +90,8 @@ In code, this might look like this: [import:37-55, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:34-52, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:13-28, lang="python"](code/python/flood_fill.py) {% endmethod %} @@ -106,6 +108,8 @@ In code, it might look like this: [import:106-118, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:180-195, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:58-66, lang="python"](code/python/flood_fill.py) {% endmethod %} All code snippets for this chapter rely on an exterior `color` function, defined as @@ -115,6 +119,8 @@ All code snippets for this chapter rely on an exterior `color` function, defined [import:23-35, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:28-32, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:10-11, lang="python"](code/python/flood_fill.py) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -126,6 +132,8 @@ Additionally, it is possible to do the same type of traversal by managing a stac [import:57-77, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:85-108, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:30-39, lang="python"](code/python/flood_fill.py) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -165,6 +173,8 @@ The code would look something like this: [import:80-104, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import:155-178, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:41-56, lang="python"](code/python/flood_fill.py) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -243,6 +253,8 @@ After, we will fill in the left-hand side of the array to be all ones by choosin [import, lang:"julia"](code/julia/flood_fill.jl) {% sample lang="c" %} [import, lang:"c"](code/c/flood_fill.c) +{% sample lang="py" %} +[import:, lang="python"](code/python/flood_fill.py) {% endmethod %}