Declare variable in if statement and then using it later in code - c#

I am programming a console "game" and I need to declare a "hp" of character in IF statements which depends on level of this character.
if ((char_level > 0) && (char_level < 4))
{
char_hp = 100;
}
if ((level > 4) && (level < 6))
{
char_hp = 120;
}
if ((level > 6) && (level < 8))
{
char_hp = 150;
}
if ((level > 8) && (level < 10))
{
char_hp = 180;
}
Then I need to use it later in code in a fight. After a successful fight character gets a new level and after that, program will get back to check these IF statements and if level is bigger than 4, character's hp will be increased to 120. But declaration of char_hp in IF statements does not change the value of hp in general and when the next fight comes after reaching level 4, character's hp is still like at the end of previous battle was. I am new in C# programming and I have tried everything but I can't solve it, if it is possible.
The same problem is with the "hp" of enemy that is randomly generated...then I need to use it in that fight
if((level>0) && (level<4))
{
random_enemy_hp = RND.Next(89, 111);
goto enemy;
}
if((level>4) && (level<6))
{
random_enemy_hp = RND.Next(109, 141);
goto enemy;
}
if ((level > 6) && (level < 8))
{
random_enemy_hp = RND.Next(149, 184);
goto enemy;
}
if ((level > 8) && (level < 10))
{
random_enemy_hp = RND.Next(189, 221);
goto enemy;
}
EDIT: I meant "saving values to variables" in IF statements, so I can use them later in code. This is how my code starts, then there are "Console.WriteLine()"-s, principe of a fight and statements shown above.
string name;
int char_hp = 100;
int level = 1;
int random_enemy_hp;
Random RND = new Random();

You're completely on the wrong track. You should be doing something like:
int[] charLevelHp = { 100, 100, 100, 100,
120, 120, 120,
150, 150,
180, 180 };
int charLevel = 1;
int charHp = charLevelHp[charLevel];

I can't help but notice that you're comparing with char_level for your first couple if-statement, but you're comparing to level for your subsequent if-statements
if ((char_level > 0) && (char_level < 4))
{
char_hp = 100;
}
if ((level > 4) && (level < 6))
{
char_hp = 120;
}
I think you might have intended to use char_level for all of the conditions.
if ((char_level > 0) && (char_level < 4))
{
char_hp = 100;
}
if ((char_level > 4) && (char_level< 6))
{
char_hp = 120;
}
If that's the issue, it would be consistent with the kinds of errors you're seeing.

Related

Method keep adding the same thing to the list [duplicate]

This question already has answers here:
Random number generator only generating one random number
(15 answers)
Closed 1 year ago.
I 'm creating a Text Base arena rpg where each day new monsters are add to the list, but only one monster get add to the list repeatly with the same stats which for some reason keeps increasing each day.
What i need are that diferent objects with diferent values are created than add to a list, the second part works well.
This method calls the creator.
public static List<Monster> MonsterOfTheDay()
{
int count = 0;
List<Monster> MonstersListOfTheDay = new List<Monster>();
while(count <= 5)
{
MonstersListOfTheDay.Add(Creator());
count++;
}
return MonstersListOfTheDay;
}
This are the creator
public static Monster Creator()
{
Random random = new Random();
Monster monsterChoosen = monsterListPrefab.Find(m => m.Id == random.Next(0, monsterListPrefab.Count -1));
monsterChoosen.Level = random.Next(monsterChoosen.Level, monsterChoosen.Level + 3);
//1Offensive, 2Defensive, 3Balance
monsterChoosen.Type = (Types)typeList.GetValue(random.Next(1, typeList.Length));
Console.WriteLine("Estou Aqui");
int atributes = monsterChoosen.Level * 3;
int spend = 0;
Console.WriteLine("Estou Aqui");
while(spend != atributes)
{
int chance = random.Next(0, 100);
if(monsterChoosen.Type == Types.Offensive)
{
if(chance >= 0 && chance <= 60)
{
monsterChoosen.Str++;
spend++;
}
if(chance >= 61 && chance <= 70)
{
monsterChoosen.Int++;
spend++;
}
if(chance >= 71 && chance <= 85)
{
monsterChoosen.Agi++;
spend++;
}
if(chance >= 86 && chance <= 100)
{
monsterChoosen.Vig++;
spend++;
}
}
else if(monsterChoosen.Type == Types.Defensive)
{
if(chance >= 0 && chance <= 60)
{
monsterChoosen.Vig++;
spend++;
}
if(chance >= 61 && chance <= 70)
{
monsterChoosen.Str++;
spend++;
}
if(chance >= 71 && chance <= 85)
{
monsterChoosen.Int++;
spend++;
}
if(chance >= 86 && chance <= 100)
{
monsterChoosen.Agi++;
spend++;
}
}
else if(monsterChoosen.Type == Types.Balance)
{
if(chance >= 0 && chance <= 25)
{
monsterChoosen.Str++;
spend++;
}
if(chance >= 26 && chance <= 50)
{
monsterChoosen.Int++;
spend++;
}
if(chance >= 51 && chance <= 75)
{
monsterChoosen.Agi++;
spend++;
}
if(chance >= 76 && chance <= 100)
{
monsterChoosen.Vig++;
spend++;
}
}
else if(monsterChoosen.Type == Types.Prefab)
{
spend++;
}
else
{
Console.WriteLine("Error");
}
}
return monsterChoosen;
}
I chance the initial 3 lines of the Creator method, now its working properly, the reason is, as Phillipp said in the game developer stack, the old code keep the information of the monsterChoosen variable and won't generate a new one when called again, keeping the same result and only increasing its stats and level, now with the new line of the variable, monsterChoosen can update every time its called.
Random random = new Random();
int randId = random.Next(2);
Monster monsterChoosen = new Monster(monsterListPrefab.Find(m => m.Id == randId));

How to make picturebox move along edges of screen without clipping?

In a sample car game, I want to be able to make the car move within the screen, and I want it to be "bound" by the edges. However, it keeps clipping and goes beyond the screen.
I made the picturebox square and used the dimensions of the picture of the car, which is centered in the picturebox) to base the model of the code. The picturebox is 100x100 and the picture of the car (when facing sideways) is roughly 50x100. For example, if the car is going sideways against the edges of the screen, I would make the Y location of the picturebox -25. However, I feel that this is too inconvenient, because I may need to change the picture of the car.
Point pNewLoc = carLocation;
int iX = Width - pbCar.Width;
int iY = Height - pbCar.Height;
if (pNewLoc.X <= 0 || pNewLoc.X >= iX)
{
if (pNewLoc.X <= 0)
{
if (iDirection == CarDirection.UP || iDirection == CarDirection.DOWN)
{
pNewLoc.X = -25;
}
else
{
pNewLoc.X = 0;
}
}
if (pNewLoc.X >= iX)
{
if (iDirection == CarDirection.UP || iDirection == CarDirection.DOWN)
{
pNewLoc.X = iX + 25;
}
else
{
pNewLoc.X = iX;
}
}
}
if (pNewLoc.Y <= 0 || pNewLoc.Y >= iY)
{
if (pNewLoc.Y <= 0)
{
if (iDirection == CarDirection.LEFT || iDirection == CarDirection.RIGHT)
{
pNewLoc.Y = -25;
}
else
{
pNewLoc.Y = 0;
}
}
if (pNewLoc.Y >= iY)
{
if (iDirection == CarDirection.LEFT || iDirection == CarDirection.RIGHT)
{
pNewLoc.Y = iY + 25;
}
else
{
pNewLoc.Y = iY;
}
}
}
carLocation = pNewLoc;
This code works just fine, but I feel it's too long and is too inconvenient.
If the code is working, then, it can definetly be summarized:
int verticalDirection = Convert.ToInt32(iDirection == CarDirection.UP || iDirection == CarDirection.DOWN);
int horizontalDirection = Convert.ToInt32(iDirection == CarDirection.LEFT || iDirection == CarDirection.RIGHT);
if (pNewLoc.X <= 0)
{
pNewLoc.X = -25 * verticalDirection;
}
else
{
pNewLoc.X = iX + 25 * verticalDirection;
}
if (pNewLoc.Y <= 0)
{
pNewLoc.Y = -25 * horizontalDirection;
}
else
{
pNewLoc.Y = iY + 25 * horizontalDirection;
}
If you change the size of the picture, then you don't have to use a constant like 25 but declare a variable which is dependent on the size of the car picture.

How can I check so that if any elements relate to the if the foreach would set it instead of later going to the else?

How can I check so that if any elements relate to the if the foreach would set it instead of later going to the else?
for (int i = 0; i < 3; i++)
{
Grass grass = new Grass(32 + 32 * i, 32 * 12);
GrassList.Add(grass);
}
foreach (Grass grass in GrassList)
{
if (grass.Rect.X - 32 <= Rect.X && grass.Rect.X + 32 >= Rect.X && grass.Rect.Y - 65 <= Rect.Y)
{
FallSpeed = 0;
isTouchingGround = true;
}
else
{
FallSpeed = 3;
isTouchingGround = false;
}
}
You can use the LINQ expression with if condition to achieve what you want also
FallSpeed = 3;
isTouchingGround = false;
if(GrassList.Any(
grass => grass.Rect.X - 32 <= Rect.X &&
grass.Rect.X + 32 >= Rect.X &&
grass.Rect.Y - 65 <= Rect.Y))
{
FallSpeed = 0;
isTouchingGround = true;
}
The best option is to short-circuit your evaluation. As soon as you know that you are touching the ground, break out of the loop. Your else branch is basically your default case.
FallSpeed = 3;
isTouchingGround = false;
foreach (Grass grass in GrassList)
{
if (grass.Rect.X - 32 <= Rect.X && grass.Rect.X + 32 >= Rect.X && grass.Rect.Y - 65 <= Rect.Y)
{
FallSpeed = 0;
isTouchingGround = true;
break;
}
}

C# nested if-else optimization

I have a class obj, which has three properties: firstValue, secondValue, thirdValue, all of which range from 0 to 255.
I have a List containing objects of class obj and must divide them into 32 different regions according to the values of firstValue, secondValue and thirdValue. I have been successful using a nested if-else statement like this:
if (obj.firstValue < 15 )
{
if(obj.secondValue <200)
{
if(obj.thirdValue <125)
maincolor[0]++;
else
maincolor[1]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[2]++;
else
maincolor[3]++;
}
}
else if (obj.firstValue < 41)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[4]++;
else
maincolor[5]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[6]++;
else
maincolor[7]++;
}
}
else if (obj.firstValue < 90)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[8]++;
else
maincolor[9]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[10]++;
else
maincolor[11]++;
}
}
else if (obj.firstValue < 128)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[12]++;
else
maincolor[13]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[14]++;
else
maincolor[15]++;
}
}
else if (obj.firstValue < 166)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[16]++;
else
maincolor[17]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[18]++;
else
maincolor[19]++;
}
}
else if (obj.firstValue < 196)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[20]++;
else
maincolor[21]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[22]++;
else
maincolor[23]++;
}
}
else if (obj.firstValue < 205)
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[24]++;
else
maincolor[25]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[26]++;
else
maincolor[27]++;
}
}
else
{
if (obj.secondValue < 200)
{
if (obj.thirdValue < 125)
maincolor[28]++;
else
maincolor[29]++;
}
else
{
if (obj.thirdValue < 125)
maincolor[30]++;
else
maincolor[31]++;
}
}
I use maincolor[i] to record the maximum number of the region.
The above method works, but I would like to know if there is any way to make it more readable and less of a performance cost?
Untested, but you get the drift.
EDIT: I've reversed the algorithm to allow early bailout.
int[] firstCutoffs = new int[] { 15, 41, 90, 128, 166, 196, 205 };
int index;
for (int n = 0; obj.firstValue > firstCutoffs[n] && n < firstCutoffs.Length; n++)
index += 4;
if (obj.secondValue >= 200 )
index += 2;
if (obj.thirdValue >= 125 )
index ++;
maincolor[index]++;
When you have three nested if conditions, you can be almost certain that you're doing something wrong.
C# is an object oriented language, so you have to think object!
For instance:
class ColorRange
{
public Range RedRange { get; set; }
public Range GreenRange { get; set; }
public Range BlueRange { get; set; }
}
class Range
{
public int Minimum { get; set; }
public int Maximum { get; set; }
public bool IsInRange(int value)
{
return value >= this.Minimum && value < this.Maximum;
}
}
Then make a GetColorRange method somewhere:
public ColorRange GetColorRange(int red, int green, int blue)
{
foreach (var colorRange in this.Ranges)
{
if (colorRange.RedRange.IsInRange(red)
&& colorRange.GreenRange.IsInRange(green)
&& colorRange.BlueRange.IsInRange(blue))
{
return colorRange;
}
}
return null;
/*
Or with Linq:
return this.Ranges.FirstOrDefault(colorRange =>
colorRange.RedRange.IsInRange(red)
&& colorRange.GreenRange.IsInRange(green)
&& colorRange.BlueRange.IsInRange(blue));
*/
}
Usage:
var colorRange = GetColorRange(20, 175, 200);
// increment the count of this color range in your array
Of course, you're not supposed to use this code 'as is'. It's just to show you how you could redesign your algorithm.
This answer is almost similar to most of the answers here. I just want to stress the use of the break once you found a matching value here:
int[] limitList = new int[] { 15, 41, 90, 128, 166, 196, 205 };
int index = 0;
foreach(int val in limitList)
{
if (obj.firstValue < val)
break; //break on first encounter
index += 4;
}
if (obj.secondValue >= 200)
index+=2;
if (obj.thirdValue >=125)
index++;
maincolor[index]++;
To make the code more readable you could use a 3-dimension array to store the main color category.
int[,,] mainColorCategories = new int [8,2,2];
(note, there are 8 categories for first value, 2 for second and third)
Populate this accordingly with the indices into the main colour array. Then to implement your code you'd implement three functions to determine the indices into this array. These functions need to perform the "if-else-if" evaluations you perform in your code snippet.
int firstValueIndex = getFirstValueIndex(obj.firstValue);
int secondValueIndex = getSecondValueIndex(obj.secondValue);
int thirdValueIndex = getThirdValueIndex(obj.thirdValue);
Then you can increment the correct main color array
int mainColorCat = mainColorCategories[firstValueIndex,secondValueIndex,thirdValueIndex];
maincolor[mainColorCat]++;
I liked this question, I have tried this with a little of LinQ
Dictionary<int,int> firstValue = new Dictionary<int,int>();
firstValue.Add(15,0);
firstValue.Add(41,4);
firstValue.Add(90,8);
firstValue.Add(128,12);
firstValue.Add(166,16);
firstValue.Add(196,20);
firstValue.Add(205,24);
firstValue.Add(256,28);
int mainIndex = 0;
KeyValuePair<int,int> firstIndex = firstValue.FirstOrDefault(x => obj.firstValue < x.Key);
mainIndex = firstIndex.Value;
mainIndex += (obj.secondValue < 200 ? 0 : 2);
mainIndex += (obj.thirdValue < 125 ? 0 : 1);
maincolor[mainIndex]++;
First, I have stored all of your test condition values for the firstValue in a Dictionary with the proper base index to the maincolor, then is simply a math operation to add the remainder values to the index. The advantage is the clear indication of your limits in the Dictionary add methods.
I thought I'd throw this in to the pot for your consideration now that you've accepted an answer.
int index = 0;
if (obj.firstValue < 15)
index = 0;
else if (obj.firstValue < 41)
index = 4;
else if (obj.firstValue < 90)
index = 8;
else if (obj.firstValue < 128)
index = 12;
else if (obj.firstValue < 166)
index = 16;
else if (obj.firstValue < 196)
index = 20;
else if (obj.firstValue < 205)
index = 24;
else
index = 28;
if (obj.secondValue >= 200)
index += 2;
if (obj.thirdValue >= 125)
index++;
maincolor[index]++;
Its much more easier on the eye compared to your original posted coded and has the same
performance.
I was interested to see what the performance difference was between your original code vs my code vs the other answers posted and its become clear to me that using a loop will hurt your performance. I commented #GazTheDestroyer answer that it wouldn't be any faster (See Loop Unwinding > en.wikipedia.org/wiki/Loop_unwinding).
So I wrote a little program to compare the different answers and found that generally the loop type answers are much slower eg #mbm answer. The caveat here is that the performance hit becomes noticeable only when you have a large number of objects to iterate through so in my app I tested with 1000000 items (objects with 1st, 2nd, and 3rd properties).
Just to give you an idea of results for 1000000 items:
Your original code and my example code above executes in about 120 milliseconds
Both #mbm and #Steve answers (using loops) execute in about 650 and 750 milliseconds (respectively). Much, much slower!
I've uploaded the code for the program to github > https://github.com/mouters/SO12295374_SpeedTest so feel free to download and test.
You can try to gain some readability by using LINQ:
// be allObjects an IEnumerable<obj>
maincolor[0] = allObjects.Count(o => o.firstValue < 15 && o.secondValue < 200 && o.thirdValue < 125);

Different Methods of Performing FloodFill

OK everyone I have several different methods of performing a FloodFill. All of them cause problems. I will list the 3 methods and explain what happens with each one. If anyone could give me some pointers that would be great. I have seen some similar posts but none of them have been for C#, java, or VB.net (the only languages I know).
The givens for this are that I have a class called PixelData which stores a Color in a CellColor member variable. I have an array that is 50x50 of PixelData objects in size called "pixels". I also have a constant called CANVAS_SIZE which is 50 in this case. Here are the three methods I have tried using.
This one is recursive. It is EXTREMELY prone to stack overflows. I have tried settings a timer that enabled a CanFill member after this method is complete. This still does not prevent the overflows:
private void FloodFill(Point node, Color targetColor, Color replaceColor)
{
//perform bounds checking X
if ((node.X >= CANVAS_SIZE) || (node.X < 0))
return; //outside of bounds
//perform bounds checking Y
if ((node.Y >= CANVAS_SIZE) || (node.Y < 0))
return; //ouside of bounds
//check to see if the node is the target color
if (pixels[node.X, node.Y].CellColor != targetColor)
return; //return and do nothing
else
{
pixels[node.X, node.Y].CellColor = replaceColor;
//recurse
//try to fill one step to the right
FloodFill(new Point(node.X + 1, node.Y), targetColor, replaceColor);
//try to fill one step to the left
FloodFill(new Point(node.X - 1, node.Y), targetColor, replaceColor);
//try to fill one step to the north
FloodFill(new Point(node.X, node.Y - 1), targetColor, replaceColor);
//try to fill one step to the south
FloodFill(new Point(node.X, node.Y + 1), targetColor, replaceColor);
//exit method
return;
}
}
Next I have a method that uses a Queue based fill. This method causes OutOfMemory Exceptions at runtime and is EXTREMELY slow when filling the entire canvas. If just filling a small portion of the canvas, it is somewhat effective:
private void QueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> points = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
points.Enqueue(node);
while (points.Count > 0)
{
Point n = points.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
pixels[n.X, n.Y].CellColor = replaceColor;
if (n.X != 0)
{
if (pixels[n.X - 1, n.Y].CellColor == targetColor)
points.Enqueue(new Point(n.X - 1, n.Y));
}
if (n.X != CANVAS_SIZE - 1)
{
if (pixels[n.X + 1, n.Y].CellColor == targetColor)
points.Enqueue(new Point(n.X + 1, n.Y));
}
if (n.Y != 0)
{
if (pixels[n.X, n.Y - 1].CellColor == targetColor)
points.Enqueue(new Point(n.X, n.Y - 1));
}
if (n.Y != CANVAS_SIZE - 1)
{
if (pixels[n.X, n.Y + 1].CellColor == targetColor)
points.Enqueue(new Point(n.X, n.Y + 1));
}
}
DrawCanvas();
return;
}
The final method that I have tried also uses a queue based floodfill. This method is MUCH faster than the previous queue based floodfill but also eventually causes OutOfMemory exceptions at runtime. Again, I have tried setting a FillDelay timer that would prevent the user from rapidly clicking but this still doesn't stop the exceptions from occurring. Another bug with this one is that it has a hard time properly filling small areas. I see no point in fixing this until I can get it to not crash.
private void RevisedQueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> q = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
q.Enqueue(node);
while (q.Count > 0)
{
Point n = q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
Point e = n;
Point w = n;
while ((w.X != 0) && (pixels[w.X, w.Y].CellColor == targetColor))
{
pixels[w.X, w.Y].CellColor = replaceColor;
w = new Point(w.X - 1, w.Y);
}
while ((e.X != CANVAS_SIZE - 1) && (pixels[e.X, e.Y].CellColor == targetColor))
{
pixels[e.X, e.Y].CellColor = replaceColor;
e = new Point(e.X + 1, e.Y);
}
for (int i = w.X; i <= e.X; i++)
{
Point x = new Point(i, e.Y);
if (e.Y + 1 != CANVAS_SIZE - 1)
{
if (pixels[x.X, x.Y + 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y + 1));
}
if (e.Y - 1 != -1)
{
if (pixels[x.X, x.Y - 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y - 1));
}
}
}
}
}
Thanks for everyone's help! All of these methods are based on pseudo code on wikipedia.
EDIT:
I selected the RevisedQueueFloodFill and modified as suggested so that no variables are declared within the loops. An OutOfMemory is still generated. Even with a filldelay timer.
private void RevisedQueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
Queue<Point> q = new Queue<Point>();
if (pixels[node.X, node.Y].CellColor != targetColor)
return;
q.Enqueue(node);
Point n, e, w, x;
while (q.Count > 0)
{
n = q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
e = n;
w = n;
while ((w.X != 0) && (pixels[w.X, w.Y].CellColor == targetColor))
{
pixels[w.X, w.Y].CellColor = replaceColor;
w = new Point(w.X - 1, w.Y);
}
while ((e.X != CANVAS_SIZE - 1) && (pixels[e.X, e.Y].CellColor == targetColor))
{
pixels[e.X, e.Y].CellColor = replaceColor;
e = new Point(e.X + 1, e.Y);
}
for (int i = w.X; i <= e.X; i++)
{
x = new Point(i, e.Y);
if (e.Y + 1 != CANVAS_SIZE - 1)
{
if (pixels[x.X, x.Y + 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y + 1));
}
if (e.Y - 1 != -1)
{
if (pixels[x.X, x.Y - 1].CellColor == targetColor)
q.Enqueue(new Point(x.X, x.Y - 1));
}
}
}
}
}
Ok a couple of things:
C# has a recursive limit (determined by stack size) of a few thousand in depth.
This means you can't go DEEPER in recursion downward without causing a stack overflow. As soon as a method returns its pointer is popped off the stack. Your problem is not the same as an OutOfMemoryException. The stack holds pointers not actual memory and as such is not meant to hold thousands of pointers.
Garbage collection is what's causing your out of memory exception. You need to stop declaring variables inside of your loops. The garbage collector sees these as "still in scope" and will not free up the memory space until the loop completes all iterations. But if you use the same memory address, it will just overwrite it each time and hardly use any memory.
To be clear:
for (int i = w.X; i <= e.X; i++)
{
Point x = new Point(i, e.Y);
}
Should be like this:
Point x;
for(int i = w.X; i<= e.X; i++)
{
x = new Point(i, e.Y);
}
This will reuse the memory address like you would want it to.
Hope that helps!
I have no idea if this will work, but my own suspicion is that a lot more memory is being used than necessary due to all the 'new' operators, and perhaps due to the intensive nature of the algorithm, the garbage collector didn't have a chance to kick in?
I've rewritten the algorithm so that all the Point variables just get reused rather, but I've not currently got a way of testing this.
I've also taken the liberty of altering the first few lines of code, but this is because of a pet peeve of mine that most flood-fill algorithms you find out there need the user to specify the target colour, when in fact you can simply get the target colour automatically from the point given in the argument.
Anyway, have a try at using this, or otherwise just laugh at it:
private void RevisedQueueFloodFill(Point node, Color replaceColor)
{
Color targetColor = pixels[node.X, node.Y].CellColor;
if (targetColor == replaceColor) return;
Queue<Point> q = new Queue<Point>();
q.Enqueue(node);
Point n, t, u;
while (q.Count > 0)
{
n = q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
t = n;
while ((t.X > 0) && (pixels[t.X, t.Y].CellColor == targetColor))
{
pixels[t.X, t.Y].CellColor = replaceColor;
t.X--;
}
int XMin = t.X + 1;
t = n;
t.X++;
while ((t.X < CANVAS_SIZE - 1) &&
(pixels[t.X, t.Y].CellColor == targetColor))
{
pixels[t.X, t.Y].CellColor = replaceColor;
t.X++;
}
int XMax = t.X - 1;
t = n;
t.Y++;
u = n;
u.Y--;
for (int i = XMin; i <= XMax; i++)
{
t.X = i;
u.X = i;
if ((t.Y < CANVAS_SIZE - 1) &&
(pixels[t.X, t.Y].CellColor == targetColor)) q.Enqueue(t);
if ((u.Y >= 0) &&
(pixels[u.X, u.Y].CellColor == targetColor)) q.Enqueue(u);
}
}
}
}
In the third method, you should check the pixels to the immediate west and east of the current point. Instead of checking pixels[w.X, w.Y] you should be checking pixels[w.X - 1, w.Y] and instead of pixels[e.X, e.Y] you should have pixels[e.X + 1, e.Y]. Here is my take on your third method:
private void RevisedQueueFloodFill(Point node, Color targetColor, Color replaceColor)
{
if (pixels[node.X, node.Y].CellColor != targetColor) return;
Queue<Point> Q = new Queue<Point>();
Q.Enqueue(node);
while (Q.Count != 0)
{
Point n = Q.Dequeue();
if (pixels[n.X, n.Y].CellColor == targetColor)
{
int y = n.Y;
int w = n.X;
int e = n.X;
while (w > 0 && pixels[w - 1, y].CellColor == targetColor) w--;
while (e < CANVAS_SIZE - 1 && pixels[e + 1, y].CellColor == targetColor) e++;
for (int x = w; x <= e; x++)
{
pixels[x, y].CellColor = replaceColor;
if (y > 0 && pixels[x, y - 1].CellColor == targetColor)
{
Q.Enqueue(new Point(x, y - 1));
}
if (y < CANVAS_SIZE - 1 && pixels[x, y + 1].CellColor == targetColor)
{
Q.Enqueue(new Point(x, y + 1));
}
}
}
}
}
The issue here with the basic algorithm is that you queue multiple visits to a point and do a breadth-first search. This means that you create several copies of the same point during each pass. This accumulates exponentially, since each point is allowed to spread (queue more points), even if it's not the target color (already been replaced.)
Set the color at the same time that you Enqueue them (rather than on Dequeue), so that you never end up adding them to the Queue twice.

Categories