I have a problem with implementation of flood filling.
The task is to ask user to click on the white part of the image (indicating seed point), he want to fill with black. The operation should be done on the binary images. I'm using CImg library. I can't use recursive algorithm. I've came up with something but it is not working properly (the gap becomes black only in the seed point). I am not familiar with the queues at all, so maybe the problem is in their implementaion.
void floodfill(int x, int y, int c, int b, CImg <unsigned char>image)
{
//c-black
//b-white
CImg<unsigned char> kopia(image.width(),image.height());
for (int p=1; p<image.height()-1; p++)
{
for (int q=1; q<image.width()-1; q++)
{
kopia(p,q)=255; //setting kopia2 all white
}
}
queue <pair<int,int> > a;
int p;
if(image(x, y) == c)
{
cout<<"Already black"<<endl;
return;
}
else
{
a.push(make_pair(x, y));
while(!a.empty())
{
a.pop();
p=image(x+1, y);
if((p == b) && (x < image.width()))
{
a.push(make_pair(x+1, y));
kopia(x+1, y)=c;
image(x+1, y)=c;
}
p = image(x-1, y);
if((p == c) && (x > 0))
{
a.push(make_pair(x-1, y));
kopia(x-1, y)=c;
image(x-1, y)=c;
}
p=image(x, y+1);
if((p == b) && (y < image.height()))
{
a.push(make_pair(x, y+1));
kopia(x, y+1)=c;
image(x, y+1)=c;
}
p=image(x, y-1);
if((p == b) && (y > 0))
{
a.push(make_pair(x, y-1));
kopia(x, y-1)=c;
image(x, y-1)=c;
}
}
saving(kopia);
}
}
void hole (CImg <unsigned char>image)
{
CImgDisplay image_disp(image,"Click a point");
int c_x=0; //coordinates
int c_y=0;
while (!image_disp.is_closed())
{
image_disp.wait();
if (image_disp.button())
{
c_x=image_disp.mouse_x(); //reads coordinates indicated by user
c_y=image_disp.mouse_y();
}
}
floodfill(c_x, c_y,0,255,image);
}
1)
while(!a.empty())
{
x = a.front().first; //fixed as per ChristianRau's code
y = a.front().second; //fixed as per ChristianRau's code
a.pop();
You just popped the current x,y coordinates off the stack without looking at what they were.
2)
p = image(x-1, y);
if((p == c) && (x > 0))
Did you mean to check if it was white, like you did with the other directions?
3) The caller passes in black and white, what happens if part of the image is blue? Better would be to pass in the filling color (black), and wherever you have white, replace that with not-black.
Don't you realize that you are working with the same x and y all the time and that a.pop() doesn't return anything? std::queue::pop only pops the front of the queue, but doesn't return it. You have to query it beforehand using std::queue::front. So just add
x = a.front().first;
y = a.front().second;
right before a.pop() inside the while loop.
And by the way, you might also want to set image(x, y) (and maybe kopia(x, y)) to c at the beginning of the else block before pushing the initial pair, although it might also get set by its neighbours' iterations.
Also, there is a built-in function in CImg that does what you want : CImg::draw_fill().
Related
I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.
I'm trying to cycle through the RGB spectrum smoothly, but so far I've only been able to make code that either goes through 768 colors (256*3) smoothly or goes through 16777216 colors (256^3) with discrete jumps.
Here's the code that runs smoothly:
void loop() {
setColor(255,0,0);
setColor(255,255,0);
setColor(0,255,0);
setColor(0,255,255);
setColor(0,0,255);
setColor(255,0,255);
}
void setColor(int red, int green, int blue) {
while ( r != red ) {
if ( r < red ) r += 1;
if ( r > red ) r -= 1;
_setColor();
delay(10);
}
while ( g != green){
if ( g < green ) g += 1;
if ( g > green ) g -= 1;
_setColor();
delay(10);
}
while ( b != blue){
if ( b < blue ) b += 1;
if ( b > blue ) b -= 1;
_setColor();
delay(10);
}
}
void _setColor() {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
Here's the code that runs through every RGB value:
void loop() {
for (r = 0; r <= 255; r++) {
for (g = 0; g <= 255; g++) {
for (b = 0; b <= 255; ++) {
analogWrite(redPin, r);
analogWrite(greenPin, g);
analogWrite(bluePin, b);
}
}
}
The smooth code will never combine values in between the max/min of the colors, i.e. I never get anything like [128,64,72], only outputs like [255,64,0] or [0,0,72].
The discrete code runs through every blue value, then increases the green value, and starts back at 0 for blue, i.e. [0,0,254] -> [0,0,255] -> [0,1,0] -> [0,1,1] which creates discrete jumps.
I'm trying to get a smooth cycle which goes through every possible RGB value, is that even possible?
Sure you can visit every point in a 256^3 space continuously.
The easiest way is to start with a line, then turn a line into a plane, then turn a plane into a cube.
struct simple_generator {
int current = 0;
int min = 0;
int max = 255;
int direction = 1;
bool advance() // returns false iff we hit the end
{
if (current + direction > max || current+direction < min) {
direction = -direction;
return false;
}
current += direction;
return true;
}
};
now let's make a generator from this.
template<std::size_t N>
struct shape_generator {
simple_generator state[N];
int operator[](std::size_t i) const { return state[i].current; }
bool advance() {
for (std::size_t i = 0; i < N; ++i) {
if (state[i].advance())
return true;
}
return false;
}
};
now what this does it advances the first simple generator until it overflows (which causes the generator stand still and reverse directions). If it overflows, it "recursively" advances the next one.
If every generator overflows, it returns false. Otherwise it returns true.
This will generate a pretty boring curve, as it mostly looks like "blue goes to top, then back down, back to top, and back down". Only after many cycles does any green show up. And only after many cycles of green does any red show up.
A fancier one would use an approximation of a real space-filling curve, like the Hilbert curve. But that should work
Live example with a max of 5, because running it for 256^3 elements seems rude.
I have a while loop that runs independently in a second thread in my c++ program, however the loop ends without meeting the conditions I have set.
I have tried setting a counter to keep track of how many times the loop runs and it is different every time, ranging from eight to hundreds of times.
Does anyone know why this could be happening?
while (ghostPosition.X != playerPosition.X && ghostPosition.Y != playerPosition.Y)
{
randomDirection = getRandomNumber(1, 5);
x = ghostPosition.X;
y = ghostPosition.Y;
if (randomDirection == 1)
{
if (ghostPosition.X > 1)
{
COORD oldPosition = { x, y };
COORD moveTo = { --x, y };
updateGraphics(oldPosition, moveTo, ghost);
ghostPosition.X = ghostPosition.X--;
}
}
else if (randomDirection == 2)
{
if (ghostPosition.Y < 84)
{
COORD oldPosition = { x, y };
COORD moveTo = { x, ++y };
updateGraphics(oldPosition, moveTo, ghost);
ghostPosition.Y = ghostPosition.Y++;
}
}
else if (randomDirection == 3)
{
if (ghostPosition.X < 24)
{
COORD oldPosition = { x, y };
COORD moveTo = { ++x, y };
updateGraphics(oldPosition, moveTo, ghost);
ghostPosition.X = ghostPosition.X++;
}
}
else if (randomDirection == 4)
{
if (ghostPosition.Y > 1)
{
COORD oldPosition = { x, y };
COORD moveTo = { x, --y };
updateGraphics(oldPosition, moveTo, ghost);
ghostPosition.Y = ghostPosition.Y--;
}
}
}
functions called inside loop:
int Maze::getRandomNumber(int minimum, int maximum)
{
int output;
std::random_device randomDevice;
std::mt19937 mersenneTwister(randomDevice());
std::uniform_real_distribution<double> distribution(minimum, maximum);
return distribution(mersenneTwister);
}
The only other function called (updateGraphics) simply modifies the console's output based on the parameters passed. (I don't think that this function is the issue)
I think you want this loop to execute while the player and ghost positions are different. But that's not the condition you've defined. Your loop executes while the player and ghost X positions are different AND the player and ghost Y positions are different. If either are the same, then the loop will stop. I think you want OR.
Hard to tell without some debugging information. Why don't you print out the contents of ghostPosition and playerPosition just before the close of the while loop and post them back.
Also, running the risk of sounding pedantic, you can simply your loop logic by using a swtich statement rather than a multitude of if/else statements.
I am trying to implement a board game on C++ and its some features are below:
I have 4 sources named as Mine (M), Water (W), Food (F) and Medical Supplies (S)
The Sources will be distributed to the board randomly (which I completed)
User will enter two coordinates and if there is mine on these coordinates they will just blow up and destroy the cells around them depending on their place. For example if the mine is on somewhere in the middle it will destroy the 8 cells around it and if there is another mine around the one which is exploded it will make the other one explode, too.
And there are some exceptions for example if the coordinate is on the corner it will just blow up 3 cell around it.
Let's come to the real problem. When I try to implement it I saw that it is tons of codes actually and I need to make it recursive to give the ability to blow up other cells so for every single possilibility I need to check if the blown cell is a mine or not. Is there an efficient way to implement this or do I need to just write the whole code?
void explode_mines(int x,int y) {
if (x == 0 && y == 0) {
grid[0][0] = 'X';
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
grid[1][0] = 'X';
//...
grid[1][1] = 'X';
//...
}
//Is there any efficient way?
Pseudo code:
void ExploreCell(int x, int y)
{
if (x or y are out of bounds (less than zero/greater than max))
or (this cell is a mountain, because mountains don't explode))
return
else if this location is a mine
ExplodeMine(x, y) //This cell is a mine, so it blows up again
else
DestroyCell(x, y) //This cell is a valid, non-mine target
}
void ExplodeMine(int x, int y)
{
ExploreCell(x-1, y-1);
ExploreCell(x-1, y);
....
ExploreCell(x+1, y+1);
}
void DestroyCell(int x, int y)
{
//Take care of business
}
I think there's a typo in your code:
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
How would location (0,1) not be 'X" at this point?
It doesn't have to be recursive, but information theory does say that you have to make 8 checks. You can make it more readable, however. For general purposes, I've found the basic perimeter check to be maintainable. Here, I'll let "O" be a crater and ""M" be a mine.
grid[x][y] = ' '
for (row = x-1; row <= x+1; row++) {
for (col = x-1; col <= x+1; col++) {
if grid[row][col] == "M"
explode_mines(row, col)
}
}
Now, if you have to worry about the time spent for a huge chain reaction, then you can alter your algorithm to keep two lists:
Squares that need checking
Squares with mines to blow up
In this case, explode_mines looks more like this:
Mark x,y as a dead square
Add adjacent squares to the checking list; do *not* add a duplicate
... and you get a new routine check_for_mine that looks like this:
while check list is not empty {
while mine list is not empty {
explode the top mine on the list
}
take the top square from the check list and check it
}
You can play with the nesting, depending on what chain-reaction order you'd like. For breadth-first explosions, you check all squares on the check list, then explode all the mines on the mine list; repeat that until both lists are empty. For depth-first, you can simplify the loops a little: explode every mine as soon as you find it, which means that you don't need a mine list at all.
Hoping this helps [caution: not tested] ('d' for "destroied", 'b' for "bomb")
void destroy (int x, int y)
{
char oldVal;
if ( (x >= 0) && (x < maxX) && (y >= 0) && (y < maxY)
&& ('d' != (oldVal = grid[x][y])) ) // 'd' for destroyed
{
grid[x][y] = 'd'; // set "destroyed"
if ( 'b' == oldVal ) // if it was a bomb, destroy surrounding
{
destroy(x-1, y-1);
destroy(x-1, y);
destroy(x-1, y+1);
destroy(x, y-1);
destroy(x, y+1);
destroy(x+1, y-1);
destroy(x+1, y);
destroy(x+1, y+1);
}
}
}
If i try to Fill a 100x100 rectangle then i get an overflow.
A 50x50 works fine.
Is there a way to fix the overflow?
I also print out the Stack Number and sometimes the working rectangle Stack is higher then the big one (it crashes around 7000).
void draw(int x, int y)
{
if ((x >= 0 && x < 100) && (y >= 0 && y < 100))
{
canvas.set_pixel(x, y);
if (!canvas.get_pixel(x, y + 1))draw(x, y + 1);
if (!canvas.get_pixel(x, y-1))draw(x, y - 1);
if (!canvas.get_pixel(x - 1, y))draw(x - 1, y);
if (!canvas.get_pixel(x+1, y))draw(x + 1, y);
}
return;
}
The reason you get a stack overflow is that the recursion goes too deep.
How deep will it go? Well, with the algorithm the way you designed it - it will actually go to depth 100*100=10,000!
Let's look at in what order will the canvas be filled - assuming the canvas is empty and we start filling from the middle:
set the middle pixel
go to x,y+1
do that until you get to the edge
at the edge - move to x-1,0 (remember, we're at the top)
go down until the bottom
etc. etc.
The point is - you're going deeper and deeper until you fill the canvas, and then have a "chain" of recursion calls going all around the canvas and it's a waste :)
Benjamin is right that you can use a stack, but a stack basically does the exact same thing (just without the recursion), so the stack will get to depth 10,000 as well. Still a waste, and you run out of memory in some cases (for a bitmap canvas, each pixel takes 1 bit, but the stack will have 2 integers per pixel for x,y, and thus could take 64 times more memory than the canvas)
Instead - use a queue! Almost the same code:
void draw(int x, int y)
{
struct coordinate { int x, y; };
std::queue<coordinate> to_draw; // <- changed from stack to queue
to_draw.push({x, y});
while (!to_draw.empty())
{
auto top = to_draw.front(); // <- changed from top to front
to_draw.pop();
if ( (top.x >= 0 && top.x < 100)
&& (top.y >= 0 && top.y < 100)
&& !canvas.get_pixel(top.x, top.y))
{
canvas.set_pixel(top.x, top.y);
to_draw.push({top.x, top.y + 1});
to_draw.push({top.x, top.y - 1});
to_draw.push({top.x + 1, top.y});
to_draw.push({top.x - 1, top.y});
}
}
}
And now the memory needed will be <=4*100! In other words - by changing from stack to queue we changed the memory needed from N*N to 4*N.
Don't use recursion. Instead, use a stack to store the coordinates you want to draw. And iterate until the stack is empty.
void draw(int x, int y)
{
struct coordinate { int x, y; };
std::stack<coordinate> to_draw;
to_draw.push({x, y});
while (!to_draw.empty())
{
auto top = to_draw.top();
to_draw.pop();
if ( (top.x >= 0 && top.x < 100)
&& (top.y >= 0 && top.y < 100)
&& !canvas.get_pixel(top.x, top.y))
{
canvas.set_pixel(top.x, top.y);
to_draw.push({top.x, top.y + 1});
to_draw.push({top.x, top.y - 1});
to_draw.push({top.x + 1, top.y});
to_draw.push({top.x - 1, top.y});
}
}
}