In this note we provide the source code for a basic flood fill algorithm. Suppose you have a rectangular grid of cells representing, say, the floor of a dungeon in a video game. Some of the cells are empty, and some of the cells are walls (meaning nothing can go over or on them).
We assume that for each cell it has, at most, four possible neighbors: the cells above and below it, and to the left and right of it. We won’t won’t count diagonal neighbors. However, they are not hard to add if that’s what you want, e.g. add a four extra if-statements in the main loop of the flood fill code below.
The flood fill problem is this: given a starting cell, “mark” all cells that can be reached from the start by a series of single-step up, down, left, or right moves. One way to think about this is that we start pouring water at the starting cell, and want to figure all the other cells the water flows to (assuming it can’t go through walls).
Many interesting problems can be solved by flood fill, or variations of it. For example, in a painting program, you usually get a “flood fill” tool that will make all the pixels of an enclosed region the same color. Or, suppose you want to know if there is a path from some starting point to some ending point in a maze. You could answer this by doing a flood fill at the beginning point, and then, after its done, check to see if the ending point has been marked.
The source code below implements basic flood fill that will mark all reachable cells in the maze. It has no intelligence at all: it keeps going until all reachable cells are marked.
Here are a few examples of related problems that can be solved with variations of flood fill, or ideas related to it:
#include "error.h"
#include <iostream>
#include <vector>
#include <cstdlib>
#include <time.h>
using namespace std;
enum class Cell : char {
Empty = '.',
Wall = 'M',
Blob = '^'
}; // enum class Cell
char to_char(Cell t) { return static_cast<char>(t); }
class Map {
private:
int rows;
int cols;
vector<vector<Cell>> map;
public:
Map(int r, int c, Cell init_val = Cell::Empty)
: rows(r), cols(c)
{
map = vector<vector<Cell>>(rows);
for(int i = 0; i < rows; ++i) {
map[i] = vector<Cell>(cols, init_val);
}
// add some random walls
for(int i = 0; i < rows; ++i) {
for(int j = 0; j < cols; ++j) {
if (rand() % 3 == 0) {
map[i][j] = Cell::Wall;
}
}
}
// ensure upper-left corner is empty
map[0][0] = Cell::Empty;
}
int num_rows() const { return rows; }
int num_cols() const { return cols; }
Cell get_cell(int r, int c) const {
if (!is_valid(r, c)) error("invalid cell coordinates");
return map[r][c];
}
void set_path(int r, int c) {
if (!is_valid(r, c)) error("invalid cell coordinates");
if (map[r][c] == Cell::Empty) {
map[r][c] = Cell::Blob;
}
}
bool is_valid(int r, int c) const {
return r >= 0 && r < num_rows() && c >= 0 && c < num_cols();
}
void print() const {
for(const vector<Cell>& row : map) {
for(const Cell& c : row) {
cout << to_char(c) << " ";
}
cout << "\n";
}
}
}; // class Map
struct Location {
int row;
int col;
};
// flood fill
int flood_fill(Map& map, int row = 0, int col = 0) {
vector<Location> stack;
// assumes start (col, row) is empty; (0, 0) by default
stack.push_back(Location{row, col});
int count = 0;
while (!stack.empty()) {
Location loc = stack.back();
stack.pop_back();
int r = loc.row;
int c = loc.col;
map.set_path(r, c);
count++;
// add empty cells to the stack
//
// note that the only difference in these if-statements are the cell
// coordinates
if (map.is_valid(r - 1, c) && map.get_cell(r - 1, c) == Cell::Empty) {
stack.push_back(Location{r - 1, c});
}
if (map.is_valid(r + 1, c) && map.get_cell(r + 1, c) == Cell::Empty) {
stack.push_back(Location{r + 1, c});
}
if (map.is_valid(r, c - 1) && map.get_cell(r, c - 1) == Cell::Empty) {
stack.push_back(Location{r, c - 1});
}
if (map.is_valid(r, c + 1) && map.get_cell(r, c + 1) == Cell::Empty) {
stack.push_back(Location{r, c + 1});
}
}
return count;
}
int main() {
srand(time(NULL));
Map map(10, 10);
map.print();
cout << "\n";
int count = flood_fill(map);
map.print();
cout << "# of blobs = " << count << "\n";
}