Flood Fill / Seed Fill
by Jeremy Easoz · in Torque Game Builder · 06/16/2008 (10:49 am) · 15 replies
I have a tilelayer (10x15) that is filled with random images.
To keep it simple they we will be red, white, and blue.
I'm trying to recrusively walk the tilelayer and any tiles that are
near each other and match color, clear the tile.
I can randomly fill a tile layer.
I can walk 1 square at a time and clear a given color.
But now i'm trying to do a 4 way walk and I either get a crash,
or my x and y gets trashed.
Example.
Yes, I know this isnt a flood fill but im just trying to get something working at this point.
I also know this will loop forever and it does.
It also trashes the program right when i exec from the console.
Another Attempt
Misses bottom left corner.
To keep it simple they we will be red, white, and blue.
I'm trying to recrusively walk the tilelayer and any tiles that are
near each other and match color, clear the tile.
I can randomly fill a tile layer.
I can walk 1 square at a time and clear a given color.
But now i'm trying to do a 4 way walk and I either get a crash,
or my x and y gets trashed.
Example.
function matchEnemyStones(%x, %y, %stone, %tap)
{
%base = enemyStoneArea.getTileCustomData(%x, %y);
echo("%base = ", %base);
%north = enemyStoneArea.getTileCustomData(%x, %y + 1);
echo("%north = ", %north);
%south = enemyStoneArea.getTileCustomData(%x, %y - 1);
echo("%south = ", %south);
%east = enemyStoneArea.getTileCustomData(%x - 1, %y);
echo("%east = ", %east);
%west = enemyStoneArea.getTileCustomData(%x + 1, %y);
echo("%west = ", %west);
matchEnemyStones(%x, %y, %stone, %tap);
matchEnemyStones(%x, %y - 1, %stone, %tap);
matchEnemyStones(%x, %y + 1, %stone, %tap);
matchEnemyStones(%x + 1, %y, %stone, %tap);
matchEnemyStones(%x -1, %y, %stone, %tap);
}Yes, I know this isnt a flood fill but im just trying to get something working at this point.
I also know this will loop forever and it does.
It also trashes the program right when i exec from the console.
Another Attempt
function matchEnemyStones(%x, %y, %stone, %tap)
{
if(%x == 9)
{
%x--;
if(%x == -1)
{
%x = 9;
%y--;
}
%base = enemyStoneArea.getTileCustomData(%x, %y);
echo("%base = ", %base);
%north = enemyStoneArea.getTileCustomData(%x, %y + 1);
echo("%north = ", %north);
%south = enemyStoneArea.getTileCustomData(%x, %y - 1);
echo("%south = ", %south);
%east = enemyStoneArea.getTileCustomData(%x - 1, %y);
echo("%east = ", %east);
%west = enemyStoneArea.getTileCustomData(%x + 1, %y);
echo("%west = ", %west);
matchEnemyStones(%x, %y, %stone, %tap);
matchEnemyStones(%x, %y - 1, %stone, %tap);
matchEnemyStones(%x, %y + 1, %stone, %tap);
matchEnemyStones(%x + 1, %y, %stone, %tap);
matchEnemyStones(%x -1, %y, %stone, %tap);
}Crashes the program on execution.Misses bottom left corner.
About the author
#2
06/16/2008 (1:01 pm)
Argh...
#3
Spits out the image map,
north,south,east,west of the current (base) position.
06/16/2008 (2:05 pm)
4 Way EchoSpits out the image map,
north,south,east,west of the current (base) position.
function echoEnemyStones(%x, %y)
{
for(%i = 0; %i < 150; %i++)
{
%base = enemyStoneArea.getTileCustomData(%x, %y);
%north = enemyStoneArea.getTileCustomData(%x, %y - 1);
%south = enemyStoneArea.getTileCustomData(%x, %y + 1);
%east = enemyStoneArea.getTileCustomData(%x + 1, %y);
%west = enemyStoneArea.getTileCustomData(%x - 1, %y);
echo("%base = ", %base);
echo("%north = ", %north);
echo("%south = ", %south);
echo("%east = ", %east);
echo("%west = ", %west);
%x--;
if(%x == -1)
{
%x = 9;
%y--;
}
}
#4
matchEnemyStones(%x, %y, %stone, %tap){
matchEnemyStones(%x, %y, %stone, %tap);
}
you need to have a change state and a fail condition to make it work.
If I am understanding you correctly you want to fill the whole screen starting from a point right? And you want to do this recursively? Do something like this. this is psudo code mind you:
function isTileColored(%tile)
{
//true = colored
//false = not colored
return %tile.colored;
}
matchEnemyStones(%x, %y, %stone);
{
if(%stone.killed)//if this has already been colored exit.
return;
%stone.killStone();
matchEnemyStones(%x, %y - 1, %stone);
matchEnemyStones(%x, %y + 1, %stone);
matchEnemyStones(%x + 1, %y, %stone);
matchEnemyStones(%x -1, %y, %stone);
}
function stone::killStone(%this)
{
%stone.killed = true;//color stone
//this is the code you write to put a picture here that looks like an empty tile. You can start with a message like:
echo("killed stone:",%this);
}
PS. What is %tap for ( I removed it because it wasn't being used?)
PPS. do you want the effect of gradually filling a color, is that why you want to do it recursively? If so you will need to put a delay in each step otherwise it will instantly fill in. Also you want it to stop filling if it hits a barrier of another color right? This should handle that.
PPPS. I don't remember if the default initialization would equate to false.. I think it would.
Since I'm not at home I can't test this for you but it's the general idea
06/16/2008 (2:25 pm)
Huh, ok so your recursively checking around a tile, and then calling each tile around your initial tile. Actually it looks like you call each one around the tile including the one you just called (which is redundant). Maybe I don't have all the code and am reading this out of context, but I don't see why you would recall the same thing you just called in the first place "matchEnemyStones(%x, %y, %stone, %tap). That alone would be an infinate loop since it's basically:matchEnemyStones(%x, %y, %stone, %tap){
matchEnemyStones(%x, %y, %stone, %tap);
}
you need to have a change state and a fail condition to make it work.
If I am understanding you correctly you want to fill the whole screen starting from a point right? And you want to do this recursively? Do something like this. this is psudo code mind you:
function isTileColored(%tile)
{
//true = colored
//false = not colored
return %tile.colored;
}
matchEnemyStones(%x, %y, %stone);
{
if(%stone.killed)//if this has already been colored exit.
return;
%stone.killStone();
matchEnemyStones(%x, %y - 1, %stone);
matchEnemyStones(%x, %y + 1, %stone);
matchEnemyStones(%x + 1, %y, %stone);
matchEnemyStones(%x -1, %y, %stone);
}
function stone::killStone(%this)
{
%stone.killed = true;//color stone
//this is the code you write to put a picture here that looks like an empty tile. You can start with a message like:
echo("killed stone:",%this);
}
PS. What is %tap for ( I removed it because it wasn't being used?)
PPS. do you want the effect of gradually filling a color, is that why you want to do it recursively? If so you will need to put a delay in each step otherwise it will instantly fill in. Also you want it to stop filling if it hits a barrier of another color right? This should handle that.
PPPS. I don't remember if the default initialization would equate to false.. I think it would.
Since I'm not at home I can't test this for you but it's the general idea
#5
It helps sometimes haha :)
I have a 10x15 tile ayer that I have randomly filled with different color images.
I want to walk the grid, and colors that are next to each other and match in color,
will be deleted.
For instance, the bottom left corner.
3 blocks match in an L shape.
Thats a 3 block combo. (delete tiles)
06/16/2008 (2:36 pm)
I'm just posting / talking outloud.It helps sometimes haha :)
I have a 10x15 tile ayer that I have randomly filled with different color images.I want to walk the grid, and colors that are next to each other and match in color,
will be deleted.
For instance, the bottom left corner.
3 blocks match in an L shape.
Thats a 3 block combo. (delete tiles)
#6
matchEnemyStones(%x, %y, %stone, %tap){
matchEnemyStones(%x, %y, %stone, %tap);
}
you need to have a change state and a fail condition to make it work.
If I am understanding you correctly you want to fill the whole screen starting from a point right? And you want to do this recursively? Do something like this. this is psudo code mind you:
function isTileColored(%tile)
{
//true = colored
//false = not colored
return %tile.colored;
}
matchEnemyStones(%x, %y, %stone);
{
if(%stone.killed)//if this has already been colored exit.
return;
%stone.killStone();
matchEnemyStones(%x, %y - 1, %stone);
matchEnemyStones(%x, %y + 1, %stone);
matchEnemyStones(%x + 1, %y, %stone);
matchEnemyStones(%x -1, %y, %stone);
}
function stone::killStone(%this)
{
%stone.killed = true;//color stone
//this is the code you write to put a picture here that looks like an empty tile. You can start with a message like:
echo("killed stone:",%this);
}
PS. What is %tap for ( I removed it because it wasn't being used?)
PPS. do you want the effect of gradually filling a color, is that why you want to do it recursively? If so you will need to put a delay in each step otherwise it will instantly fill in. Also you want it to stop filling if it hits a barrier of another color right? This should handle that.
PPPS. I don't remember if the default initialization would equate to false.. I think it would.
Since I'm not at home I can't test this for you but it's the general idea
06/16/2008 (4:04 pm)
Huh, ok so your recursively checking around a tile, and then calling each tile around your initial tile. Actually it looks like you call each one around the tile including the one you just called (which is redundant). Maybe I don't have all the code and am reading this out of context, but I don't see why you would recall the same thing you just called in the first place "matchEnemyStones(%x, %y, %stone, %tap). That alone would be an infinate loop since it's basically:matchEnemyStones(%x, %y, %stone, %tap){
matchEnemyStones(%x, %y, %stone, %tap);
}
you need to have a change state and a fail condition to make it work.
If I am understanding you correctly you want to fill the whole screen starting from a point right? And you want to do this recursively? Do something like this. this is psudo code mind you:
function isTileColored(%tile)
{
//true = colored
//false = not colored
return %tile.colored;
}
matchEnemyStones(%x, %y, %stone);
{
if(%stone.killed)//if this has already been colored exit.
return;
%stone.killStone();
matchEnemyStones(%x, %y - 1, %stone);
matchEnemyStones(%x, %y + 1, %stone);
matchEnemyStones(%x + 1, %y, %stone);
matchEnemyStones(%x -1, %y, %stone);
}
function stone::killStone(%this)
{
%stone.killed = true;//color stone
//this is the code you write to put a picture here that looks like an empty tile. You can start with a message like:
echo("killed stone:",%this);
}
PS. What is %tap for ( I removed it because it wasn't being used?)
PPS. do you want the effect of gradually filling a color, is that why you want to do it recursively? If so you will need to put a delay in each step otherwise it will instantly fill in. Also you want it to stop filling if it hits a barrier of another color right? This should handle that.
PPPS. I don't remember if the default initialization would equate to false.. I think it would.
Since I'm not at home I can't test this for you but it's the general idea
#7
theres a colored stone %stone and a colored spell tap %tap.
red stones and red taps can match
06/16/2008 (4:09 pm)
%stone and %tap are basically the samething.theres a colored stone %stone and a colored spell tap %tap.
red stones and red taps can match
#8
//assumptions
//this assumes you store the color of your stone in %stone.color
//it also assumes you have the color of the initial stone picked as %colorPicked
//it also assumes you use a "blank" color that isn't used again in your palate. otherwise you will need to add an additional part in the color matching.. or you will get another stack overflow.
matchEnemyStones(%x, %y, %stone,%colorPicked);
{
if(!%stone.matches(%colorPicked))//if this doesn't match your initial color exit.
return;
matchEnemyStones(%x, %y - 1, %stone, %colorPicked);
matchEnemyStones(%x, %y + 1, %stone, %colorPicked);
matchEnemyStones(%x + 1, %y, %stone, %colorPicked);
matchEnemyStones(%x -1, %y, %stone, %colorPicked);
}
function stone::matches(%this, %colorPicked)
{
if(%this.color == %colorPicked)
{
%this.color="grey"; // use whatever color you use as empty
return true;
}
else
return false;
}
06/16/2008 (4:21 pm)
Ah I suppose I missed the condition. My version would delete it all. You would need to pass in an initial color and use that as your condition. It would need to be like a Match function.//assumptions
//this assumes you store the color of your stone in %stone.color
//it also assumes you have the color of the initial stone picked as %colorPicked
//it also assumes you use a "blank" color that isn't used again in your palate. otherwise you will need to add an additional part in the color matching.. or you will get another stack overflow.
matchEnemyStones(%x, %y, %stone,%colorPicked);
{
if(!%stone.matches(%colorPicked))//if this doesn't match your initial color exit.
return;
matchEnemyStones(%x, %y - 1, %stone, %colorPicked);
matchEnemyStones(%x, %y + 1, %stone, %colorPicked);
matchEnemyStones(%x + 1, %y, %stone, %colorPicked);
matchEnemyStones(%x -1, %y, %stone, %colorPicked);
}
function stone::matches(%this, %colorPicked)
{
if(%this.color == %colorPicked)
{
%this.color="grey"; // use whatever color you use as empty
return true;
}
else
return false;
}
#9
//assumptions
//this assumes you store the color of your stone in %stone.color
//it also assumes you have the color of the initial stone picked as %colorPicked
//it also assumes you use a "blank" color that isn't used again in your palate. otherwise you will need to add an additional part in the color matching.. or you will get another stack overflow.
matchEnemyStones(%x, %y, %stone,%colorPicked);
{
if(!%stone.matches(%colorPicked))//if this doesn't match your initial color exit.
return;
matchEnemyStones(%x, %y - 1, %stone, %colorPicked);
matchEnemyStones(%x, %y + 1, %stone, %colorPicked);
matchEnemyStones(%x + 1, %y, %stone, %colorPicked);
matchEnemyStones(%x -1, %y, %stone, %colorPicked);
}
function stone::matches(%this, %colorPicked)
{
if(%this.color == %colorPicked)
{
%this.color="grey"; // use whatever color you use as empty
return true;
}
else
return false;
}
06/16/2008 (5:29 pm)
Ah I suppose I missed the condition. My version would delete it all. You would need to pass in an initial color and use that as your condition. It would need to be like a Match function.//assumptions
//this assumes you store the color of your stone in %stone.color
//it also assumes you have the color of the initial stone picked as %colorPicked
//it also assumes you use a "blank" color that isn't used again in your palate. otherwise you will need to add an additional part in the color matching.. or you will get another stack overflow.
matchEnemyStones(%x, %y, %stone,%colorPicked);
{
if(!%stone.matches(%colorPicked))//if this doesn't match your initial color exit.
return;
matchEnemyStones(%x, %y - 1, %stone, %colorPicked);
matchEnemyStones(%x, %y + 1, %stone, %colorPicked);
matchEnemyStones(%x + 1, %y, %stone, %colorPicked);
matchEnemyStones(%x -1, %y, %stone, %colorPicked);
}
function stone::matches(%this, %colorPicked)
{
if(%this.color == %colorPicked)
{
%this.color="grey"; // use whatever color you use as empty
return true;
}
else
return false;
}
#10
06/16/2008 (6:06 pm)
So you want to find all adjacent tiles with matching colors anywhere in your tileLayer, and clear them?function clearMatchedTiles()
{
for ( %i = 0; %i < %tileCountX; %i++ )
{
for ( %j = 0; %j < %tileCountY; %j++ )
{
%color; // get tile color at i,j
checkForMatch( %i, %j, %color );
}
}
}
function checkForMatch( %x, %y, %color )
{
// check for a matching color anywhere adjacent to x,y
for ( %i = %x - 1; %i <= %x + 1; %i++ )
{
for( %j = %y - 1; %j <= %y + 1; %j++ )
{
// DO NOT CALL GETCUSTOMDATA OUTSIDE OF THE LAYER SIZE
if ( %i < 0 || %i > %layersizex ||
%j < 0 || %j > %layersizey )
continue;
// if color at i,j equals %color
// do something!
}
}
}
#11
It doesn't crash like my code but it locks up the rendering process and I have to
force close it :/
06/16/2008 (8:02 pm)
Can't tell if that code works or not.It doesn't crash like my code but it locks up the rendering process and I have to
force close it :/
#12
The crash you are getting is most likely due to calling %layer.getCustomData(%x,%y) with an x/y outside of the layer's bounds. I'm pretty sure doing that causes a crash.
06/16/2008 (8:10 pm)
That was not complete code. For instance, I never defined %tileCountX in the first for loop, so it might be an infinite loop if you past it in exactly like that.The crash you are getting is most likely due to calling %layer.getCustomData(%x,%y) with an x/y outside of the layer's bounds. I'm pretty sure doing that causes a crash.
#13
06/16/2008 (8:44 pm)
Actually it just returns " "
#14
06/17/2008 (9:19 am)
Oops, well maybe it use to '-) If you use this code as a base and insert your calls to getTileCustomData and etcetera it should work and not cause a callstack overflow or infinite loop.
#15
The base was 9,14 so east and south were outside the grid.
getTileCustomData returned "" for south and east. So no big deal there.
Could add up to lots of extra function calls on the stack though thats for sure.
06/17/2008 (10:17 am)
In some code I wrote, I started the check at the bottom right corner. 9,14The base was 9,14 so east and south were outside the grid.
getTileCustomData returned "" for south and east. So no big deal there.
Could add up to lots of extra function calls on the stack though thats for sure.
Associate Orion Elenzil
Real Life Plus
your code is surely "crashing" because of stack overflow.
every one of your calls to matchEnemyStones() creates five more calls to matchEnemyStones(),
so soon you've got 1000's of calls to matchEnemyStones() on the stack.