From bc738e27c72ac76556cff6c04e93ff197d260c46 Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Fri, 11 Sep 2020 21:09:03 +0200 Subject: [PATCH 1/2] Added flood fill in Node.js --- .../flood_fill/code/javascript/flood_fill.js | 101 ++++++++++++++++++ contents/flood_fill/flood_fill.md | 12 +++ 2 files changed, 113 insertions(+) create mode 100644 contents/flood_fill/code/javascript/flood_fill.js diff --git a/contents/flood_fill/code/javascript/flood_fill.js b/contents/flood_fill/code/javascript/flood_fill.js new file mode 100644 index 000000000..872b5e190 --- /dev/null +++ b/contents/flood_fill/code/javascript/flood_fill.js @@ -0,0 +1,101 @@ + +function isInBounds(canvas, x, y) { + return (x >= 0) && (x < canvas[0].length) && (y >= 0) && (y < canvas.length) +} + +function color(canvas, x, y, oldColor, newColor) { + if (isInBounds(canvas, x, y) && canvas[y][x] == oldColor) + canvas[y][x] = newColor +} + +function findNeighbors(canvas, x, y, oldColor) { + const allNeighbors = [ + [x, y - 1], // North + [x + 1, y], // East + [x, y + 1], // South + [x - 1, y] // West + ] + + return allNeighbors + .filter(loc => isInBounds(canvas, ...loc)) + .filter(loc => canvas[loc[1]][loc[0]] == oldColor) +} + +function stackFill(canvas, x, y, oldColor, newColor) { + const stack = [ + [x, y] + ] + + while (stack.length > 0) { + const currentLoc = stack.pop() + color(canvas, ...currentLoc, oldColor, newColor) + + for(const n of findNeighbors(canvas, ...currentLoc, oldColor)) + stack.push(n) + } +} + +function queueFill(canvas, x, y, oldColor, newColor) { + const queue = [ + [x, y] + ] + + while (queue.length > 0) { + const currentLoc = queue.shift() + color(canvas, ...currentLoc, oldColor, newColor) + + for (const n of findNeighbors(canvas, ...currentLoc, oldColor)) { + // Color neighbor pixel before enqueuing to prevent + // it from being colored multiple times + color(canvas, ...n, oldColor, newColor) + queue.push(n) + } + } +} + +function recursiveFill(canvas, x, y, oldColor, newColor) { + color(canvas, x, y, oldColor, newColor) + + for(const n of findNeighbors(canvas, x, y, oldColor)) + recursiveFill(canvas, ...n, oldColor, newColor) +} + +function copyGrid(canvas) { + return canvas.map(row => row.map(v => v)) +} + +function compareGrids(canvas1, canvas2) { + return canvas1.map((row, y) => { + return row.every((val, x) => canvas1[y][x] === canvas2[y][x]) + }).every(x => x) +} + +let orignal = [ + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0] +] + +let solutionGrid = [ + [1, 1, 1, 1, 1], + [1, 1, 1, 1, 1], + [1, 1, 1, 1, 1], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 0], +] + +let startLoc = [3, 1] + +let canvas = copyGrid(orignal) +recursiveFill(canvas, ...startLoc, 0, 1) +console.log(`Recursive Fill: ${compareGrids(canvas, solutionGrid)}`) + +canvas = copyGrid(orignal) +stackFill(canvas, ...startLoc, 0, 1) +console.log(`Stackfill: ${compareGrids(canvas, solutionGrid)}`) + +canvas = copyGrid(orignal) +queueFill(canvas, ...startLoc, 0, 1) +console.log(`Queuefill: ${compareGrids(canvas, solutionGrid)}`) diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 836f86897..83dbe49a0 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="js" %} +[import:11-22, lang:"javascript"](code/javascript/flood_fill.js) {% 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="js" %} +[import:56-61, lang:"javascript"](code/javascript/flood_fill.js) {% 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="js" %} +[import:6-9, lang:"javascript"](code/javascript/flood_fill.js) {% 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="js" %} +[import:24-36, lang:"javascript"](code/javascript/flood_fill.js) {% 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="js" %} +[import:38-54, lang:"javascript"](code/javascript/flood_fill.js) {% 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="js" %} +[import, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} From ae40c508f66dcba2c0ef6526deddc3d4d5e7cf4a Mon Sep 17 00:00:00 2001 From: Jonas Rinke Date: Wed, 14 Oct 2020 00:56:02 +0200 Subject: [PATCH 2/2] Removing redundant checks mentioned in #765 --- .../flood_fill/code/javascript/flood_fill.js | 21 +++++++------------ contents/flood_fill/flood_fill.md | 19 ++++------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/contents/flood_fill/code/javascript/flood_fill.js b/contents/flood_fill/code/javascript/flood_fill.js index 872b5e190..0fcb85e46 100644 --- a/contents/flood_fill/code/javascript/flood_fill.js +++ b/contents/flood_fill/code/javascript/flood_fill.js @@ -3,11 +3,6 @@ function isInBounds(canvas, x, y) { return (x >= 0) && (x < canvas[0].length) && (y >= 0) && (y < canvas.length) } -function color(canvas, x, y, oldColor, newColor) { - if (isInBounds(canvas, x, y) && canvas[y][x] == oldColor) - canvas[y][x] = newColor -} - function findNeighbors(canvas, x, y, oldColor) { const allNeighbors = [ [x, y - 1], // North @@ -27,10 +22,10 @@ function stackFill(canvas, x, y, oldColor, newColor) { ] while (stack.length > 0) { - const currentLoc = stack.pop() - color(canvas, ...currentLoc, oldColor, newColor) + const [x, y] = stack.pop() + canvas[y][x] = newColor - for(const n of findNeighbors(canvas, ...currentLoc, oldColor)) + for(const n of findNeighbors(canvas, x, y, oldColor)) stack.push(n) } } @@ -41,20 +36,20 @@ function queueFill(canvas, x, y, oldColor, newColor) { ] while (queue.length > 0) { - const currentLoc = queue.shift() - color(canvas, ...currentLoc, oldColor, newColor) + const [x, y] = queue.shift() + canvas[y][x] = newColor - for (const n of findNeighbors(canvas, ...currentLoc, oldColor)) { + for (const n of findNeighbors(canvas, x, y, oldColor)) { // Color neighbor pixel before enqueuing to prevent // it from being colored multiple times - color(canvas, ...n, oldColor, newColor) + canvas[n[1]][n[0]] = newColor queue.push(n) } } } function recursiveFill(canvas, x, y, oldColor, newColor) { - color(canvas, x, y, oldColor, newColor) + canvas[y][x] = newColor for(const n of findNeighbors(canvas, x, y, oldColor)) recursiveFill(canvas, ...n, oldColor, newColor) diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index 83dbe49a0..9b2f0903a 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -91,7 +91,7 @@ In code, this might look like this: {% sample lang="c" %} [import:34-52, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:11-22, lang:"javascript"](code/javascript/flood_fill.js) +[import:6-17, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} @@ -109,18 +109,7 @@ In code, it might look like this: {% sample lang="c" %} [import:180-195, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:56-61, lang:"javascript"](code/javascript/flood_fill.js) -{% endmethod %} - -All code snippets for this chapter rely on an exterior `color` function, defined as - -{% method %} -{% sample lang="jl" %} -[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="js" %} -[import:6-9, lang:"javascript"](code/javascript/flood_fill.js) +[import:51-56, lang:"javascript"](code/javascript/flood_fill.js) {% 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. @@ -133,7 +122,7 @@ Additionally, it is possible to do the same type of traversal by managing a stac {% sample lang="c" %} [import:85-108, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:24-36, lang:"javascript"](code/javascript/flood_fill.js) +[import:19-31, lang:"javascript"](code/javascript/flood_fill.js) {% 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: @@ -174,7 +163,7 @@ The code would look something like this: {% sample lang="c" %} [import:155-178, lang:"c"](code/c/flood_fill.c) {% sample lang="js" %} -[import:38-54, lang:"javascript"](code/javascript/flood_fill.js) +[import:33-49, lang:"javascript"](code/javascript/flood_fill.js) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally.