Skip to content

Commit

Permalink
Added flood fill in Node.js
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonas Rinke committed Sep 11, 2020
1 parent a4dbc2a commit bc738e2
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 0 deletions.
101 changes: 101 additions & 0 deletions contents/flood_fill/code/javascript/flood_fill.js
Original file line number Diff line number Diff line change
@@ -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)}`)
12 changes: 12 additions & 0 deletions contents/flood_fill/flood_fill.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 %}


Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 %}


Expand Down

0 comments on commit bc738e2

Please sign in to comment.