I'm trying to find a faster implementation of a flood fill algorithm for a program I'm making using C# in Unity 2020.
This is my current method, which in my program takes about 400ms to run on a 1000 x 1000 map. Instead of a target colour to replace, I am using a height map (called noiseMap in this code snippet) and all values above a threshold should be considered inside the flooded area.
public void Flood()
{
landMasses.Clear();
globalSet.Clear();
HashSet<Vector2Int> samples = new HashSet<Vector2Int>();
for (int x = 0; x < mapGen.mapSize; x += mapGen.scanStride)
{
for (int y = 0; y < mapGen.mapSize; y += mapGen.scanStride)
{
samples.Add(new Vector2Int(x, y));
}
}
float[,] noiseMap = mapGen.noiseMap;
int mapSize = mapGen.mapSize;
float threshold = mapGen.threshold;
foreach (var sample in samples)
{
CalculateSets(sample, noiseMap, mapSize, threshold);
}
}
public bool Inside(Vector2Int point)
{
return Inside(point.x, point.y);
}
public bool Inside(int x, int y)
{
if (x < mapGen.mapSize && x >= 0 && y < mapGen.mapSize && y >= 0)
{
return mapGen.noiseMap[x, y] > mapGen.threshold;
}
return false;
}
public void CalculateSets(Vector2Int sample, float[,] noiseMap, int mapSize, float threshold)
{
if (globalSet.Contains(sample) || noiseMap[sample.x, sample.y] < threshold)
{
return;
}
HashSet<Vector2Int> set = new HashSet<Vector2Int>();
Queue<Vector2Int> queue = new Queue<Vector2Int>();
queue.Enqueue(sample);
while (queue.Count > 0)
{
Vector2Int n = queue.Dequeue();
if (set.Contains(n))
{
continue;
}
if(Inside(n))
{
set.Add(n);
globalSet.Add(n);
queue.Enqueue(new Vector2Int(n.x, n.y - 1));
queue.Enqueue(new Vector2Int(n.x, n.y + 1));
queue.Enqueue(new Vector2Int(n.x - 1, n.y));
queue.Enqueue(new Vector2Int(n.x + 1, n.y));
}
}
landMasses.Add(landMasses.Count.ToString(), set);
}
I've looked around at places like Wikipedia and other online forums for an implementation of the scan line flood fill, but every implementation I find has very little documentation to go along with it, or has no definitions of what their variable names represent. Regardless of this, I have tried to decipher these other implementations and have had 0 luck.
For example, on the Floodfill Wikipedia Page, there are a few different methods along with pseudocode to go along with it - but I cannot find definitions for what most of the variables mean in the later methods which are supposedly faster. Perhaps it's simple, but as someone overall new to computing algorithms I am struggling to figure it out.
So at the end of all this, I am essentially just looking for a faster way to implement something like a floodfill algorithm than what I currently have. It doesn't need to exactly fit into my program of course, even just a general C# implementation or more clarified pseudocode example with comments will be a great help.
Thank you for reading!!
Related
Here is the code I used, where 'gridCount' is precalculated as the number of elements in the 2D grid that has 1 as the element. Adjacent vertices(vertical and horizontal) are the only ones that can be traversed from each vertex.
I need the actual path also to be logged, so simply checking if a path exists is not an option. I can check it in my code, but don't know how to check in the dynamic programming solution
Answers I am looking for:
Check the time complexity of the code. This code gives correct solution but if there is no path, the complexity raises exponentially. Worst case complexity should be less than N*N!(right?). 4X4 grid calls this recursive function around a 1000 times.
Find a dynamic programming solution for the below code. I have provided links below, that I referred to but I couldn't understand the basic principle of it.
GeekForGeeks . Sample Code is below :
bool HamiltonianPath(int[,] grid, (int,int) curPlace, int gridCount)
{
if(gridCount == 0)//Total number of elements in the grid that has 1
{
return true;
}
(int, int) tempPlace;
int x;
int y;
for(int i =-1; i <= 1; i+=2)
{
x = curPlace.Item1 + i;
y = curPlace.Item2 + i;
if (x < mRow && x >= 0 && grid[x, curPlace.Item2] != 0)
{
tempPlace = (x, curPlace.Item2);
grid[tempPlace.Item1, tempPlace.Item2] = 0;
gridCount--;
if (HamiltonianPath(grid, tempPlace,gridCount))
{
return true;
}
gridCount++;
grid[tempPlace.Item1, tempPlace.Item2] = 1;
}
if (y < mCol && y >= 0 && grid[curPlace.Item1, y] != 0)
{
tempPlace = (curPlace.Item1, y);
grid[tempPlace.Item1, tempPlace.Item2] = 0;
gridCount--;
if (HamiltonianPath(grid, tempPlace,gridCount))
{
return true;
}
gridCount++;
grid[tempPlace.Item1, tempPlace.Item2] = 1;
}
}
return false;
}
I am making a precipitation map for an experiment with Perlin noise, where I also create biomes.
I am using temperature and precipitation to determine a biome of a certain pixel, I have the program for the precipitation, but the multiple For loops in the program is making Unity3d become unresponsive for a long period of time.
Does anyone know how to make this faster? I have looked around on the internet, but I couldn't find an answer
Here is my code:
public float[,] PrecipMap (float[,] noise,int mapWidth,int mapHeight)
{
float[,] precipMap = new float[mapWidth, mapHeight];//array that it to be used for precipitation
float[,] waterTiles = WaterTiles(mapHeight, mapWidth, noise);//array with all values that are water
for (int y = 0; y < mapHeight; y++)
{
for(int x = 0; x < mapWidth; x++)
{
float[] distance = new float[count];//distance between pixel and water tile
for(int wy = 0; wy < mapHeight; wy++)
{
for(int wx = 0; wx < mapWidth; wx++)
{
if (waterTiles[x, y] == 1) { // if the selected tile in water tiles has water
for(int i = 0; i < count; i++)// distance makes an array of all possible distances.
{
distance[i] = Mathf.Sqrt(((x + -wx) * (x + -wx)) + ((y +-wy) * (y +-wy)));// finds distance between pixel and water tile
}
}
}
Array.Sort(distance); /// sorts distance from least to greatest
precipMap[x, y] = distance[count-1];//enters in distance
}
}
}
return precipMap;
}
If anyone could help, I would be thankful. I am very grateful for any help/criticism.
As was already commented your loops run quite often and by default everything in unity runs on the main thread. So before Unity can render the next frame, you method has to finish.
Additionally you run Array.Sort which is quite expensive and allocate new arrays for each iteration which also keeps your GC busy!
Then I don't see what your distance array is good for. The variable i is Neve used in yoir calculation so you just fill all 1500 entries with the same value, sort it and read out the last one ... They will all be equal anyway so this is completely redundant!
Also you check redundantly
if (waterTiles[x, y] == 1)
which would be enough to check once as soon as you have x and y and is unnecessarily done further below within the nested loops - loops that you could have skipped completely if the condition is false anyway.
You could actually move your entire method to a thread and wait for the result using async await (also see Using async-await in Unity3D). Unfortunately I'm no async-await expert but this should do it
public async void CalculatePrecipMap(float[,] noise, int mapWidth, int mapHeight, Action<float[,]> callback)
{
var result = await PrecipMap(noise, mapWidth, mapHeight);
callback?.Invoke(result);
}
private async Task<float[,]> PrecipMap (float[,] noise,int mapWidth,int mapHeight)
{
//array that it to be used for precipitation
float[,] precipMap = new float[mapWidth, mapHeight];
//array with all values that are water
float[,] waterTiles = WaterTiles(mapHeight, mapWidth, noise);
for (var y = 0; y < mapHeight; y++)
{
for(var x = 0; x < mapWidth; x++)
{
// Skip as soon as possible
// Completely unnecessary to run your loop at all if this is already false
if (waterTiles[x, y] != 1) continue;
// if the selected tile in water tiles has water
for(var wy = 0; wy < mapHeight; wy++)
{
for(var wx = 0; wx < mapWidth; wx++)
{
// i is nowhere used in below calculation!
// I don't see any reason to use an distance array here at all !!!
precipMap[x, y] = Mathf.Sqrt(((x + -wx) * (x + -wx)) + ((y +-wy) * (y +-wy)));
}
}
}
}
return precipMap;
}
You would then use it using a lambda expression like
CalculatePrecipMap(someNoise, someWidth, someHeight, result =>
{
// Do something with the result
}
or using a method like
CalculatePrecipMap(someNoise, someWidth, someHeight, OnResultReady);
private void OnResultReady (float[,] result)
{
// Do something with the result
}
Note: Typed on smartphone but I hope the idea gets clear
I'm using C# and I used a 2d array of structs for a grid of tiles.This is not about how to find 8 neighboring tiles from a tile in the grid. I understand that in c# you can have a series of yield returns make a ienumerable. Like:
public IEnumerable<int> fakeList()
{
yield return 1;
yield return 2;
}
And call it with a foreach loop. Now, in my grid class want to have an easy way to access neighbours in grid.array[x,y] and modify it. But since it is a struct, I can't write an iterator like:
public IEnumerable<int> neighbours(int x, int y)
{
if((x+1) >=0 && y >=0 && .....)//check if node above is inside grid
yield return grid.array[x+1,y];
//rinse and repeat 7 more times for each direction
}
Instead, every time I need the neighbors, I need to copy paste the 8if conditions and check that I'm using the correct x+direction,y+direction to find valid indices. Basically, a huge pain.
I could work around by:
Not using structs and making my life easier. And getting rid of possible premature optimization. BUT I'm going to running this code every frame in my game. Possibly multiple times. So I'd like to keep the structs if possible.
Write iterator for indices instead. Ex:
Is the 2nd approach valid? Or does it generate garbage? I don't know how yield return works in detail.
public struct GridTile
{
public int x;
public int z;
public GridTile(int x, int z)
{
this.x = x;
this.z = z;
}
}
public IEnumerable<int> neighbours(int x, int y)
{
if ((x + 1) >= 0 && y >= 0 && .....)//check if right node is inside
yield return new Gridtile(x + 1, y);
//rinse and repeat 7 more times for each direction
}
If you know the coordinates of a 2D array entry, then the neighbors can be retrieved using loops:
var twoD = new int[10,10];
var someX = 5;
var someY = 5;
List<int> neighbors = new List<int>();
for(int nx = -1; nx <= 1; nx++){
for(int ny = -1; ny <= 1; ny++){
int iX = someX + nX;
int iY = someY + nY;
if(iX > 0 && iX < twoD.GetLength(0) && iY > 0 && iY < twoD.GetLength(1))
neighbors.Add(twoD[iX,iY]);
}
}
been trying to implement a Q deep learning algorithm, having an issue though, its not working, after 100 000 game plays and using 1000 iterations to train each step (although i have tried lower numbers for both) it's still not learning. Network and game are in the linked image, http://imgur.com/a/hATfB here is what happens in each training step:
double maxQval;
double[] inputvec;
int MaxQ = GetRandDir(state, out maxQval, out inputvec);//input vec is board
double[] QtarVec = new double[] { 0, 0, 0, 0 };
double r = GetR((int)state[0], (int)state[1]); // GetR is reward
QtarVec[MaxQ] = Qtar(r, maxQval); // backprop vector of 0's except Qtar replaces a value
associator.Train(50, new double[][] { inputvec }, new double[][] { QtarVec });
Training data pair for backprop is (input i linked in image,QTarget = r + gamma * MaxQ) , MaxQ is max network output layer activation or a random one (epsilon greedy). r is reward obtained from each move, -10 for obstacle and 10 for goal. (althogh I have tried just 10 for goal and 0 for everything else. Here is training code.
public void Train(int nTrails)
{
double[] state = new double[] { 1, 1 }; // inital position
int its = 0;
for (int i = 0; i < nTrails; i++)
{
while (((state[0] < 4) && (state[1] < 4))&&((state[0] * 100 >0) && (state[1] * 100 >0)) && (state[0] != 3 && state[1] != 3))//while on board and not at goal postion
{
double temp = r.NextDouble();
int next = -1;
lines.Add(new Vector2((float)(state[0] * 100), (float)(state[1] * 100)));
if (temp < epsilon)
{
next = TrainRandIt(state); // move random direction, backprop
}
else
{
next = TrainMaxIt(state); // move in max activation direction, backprop
}
if (next == 0) .//updating postion
{
state[0]++;
}
else if (next == 1)
{
state[0]--;
}
else if (next == 2)
{
state[1]++;
}
else if (next == 3)
{
state[1]--;
}
}
}
state[0] = 1;
state[1] = 1; // resetting game
}
Any Help appreciated.
Judging from the linked image you provided, it is just like a maze game where you have inputs for the player's position and the output as the direction the player should move to (up, down, left or right).
Here is a machine learning engine which is able to solve exactly that and more - the Ryskamp Learning Machine (RLM). The RLM has a different approach compared to the typical machine learning engines that you may have tried so far so I suggest you go to the link I've provided to learn more about it and what makes it different.
It is written in C# and we have an example of the Maze game just like the one you are trying out which you can browse through our Github page or even try it yourself by cloning/downloading the source code together with the examples apps provided.
For documentation, you may refer to the Documentations files provided or even through the github wiki.
The RLM is also available via Nuget.
I'm trying to write an implementation of the parking lot test for random number generators. Here are the sources that I'm getting my information about the test from: Intel math library documentation and Page 4 of this paper along with the phi function for probability density listed here.
I wrote an implementation of the test in C#. It uses a 100x100 grid whose values are initially set to null. I then use the random number generator to generate random integers for x and y. If that index of the grid and it's neighbors are empty, that index gets set to 1. Otherwise, nothing happens because there was a "crash".
I ran it using C# System.Random generator. I don't believe the results are correct because I always get very near 3079 points parked, which is about 500 short of the average I'm supposed to get. It's also yields a p-value of 2.21829146215425E-90.
My code is below. Does anyone have any experience with this or can anyone see something that I might be doing incorrectly in my implementation? Any help would be greatly appreciated.
private void RunParkingLotTest()
{
points = new int?[100,100];
int parked = 0;
for (int i = 0; i < 12000; i++)
{
int x = random.Next(100);
int y = random.Next(100);
if (IsSafeToPark(x, y))
{
points[x, y] = 1;
parked++;
}
}
Console.WriteLine("Parked: " + parked + "\nP value: " + PhiFunction((parked-3523)/21.9));
}
private bool IsSafeToPark(int x, int y)
{
return PointIsEmpty(x, y)
&& LeftOfPointIsEmpty(x, y)
&& RightOfPointIsEmpty(x, y)
&& BelowPointIsEmpty(x, y)
&& AbovePointIsEmpty(x, y);
}
private bool AbovePointIsEmpty(int x, int y)
{
if (y == 99)
{
return true;
}
else
return points[x, y + 1] == null;
}
private bool BelowPointIsEmpty(int x, int y)
{
if (y == 0)
{
return true;
}
else
return points[x, y - 1] == null;
}
private bool RightOfPointIsEmpty(int x, int y)
{
if (x == 99)
{
return true;
}
else
return points[x + 1, y] == null;
}
private bool LeftOfPointIsEmpty(int x, int y)
{
if (x == 0)
{
return true;
}
else
return points[x - 1, y] == null;
}
private bool PointIsEmpty(int x, int y)
{
return points[x, y] == null;
}
private double PhiFunction(double x)
{
//ϕ(x) = (2π)−½e−x2/2
return ((1 / Math.Sqrt(2 * Math.PI)) * Math.Exp(-(Math.Pow(x, 2)) / 2));
}
edit - The problems with my original implementation were
I was plotting squares instead of disks
I only plotted points at integer values. I should have used decimal values instead.
As a result of the above two, I needed to change my distance check
Thanks to Chris Sinclair and mine z for help in figuring this out. The final code is posted below.
I'm going to take a stab at this, and admittedly, I have not attempted any such test, so forgive me if I'm way off. In general though, the .NET Random implementation is pretty good and I've never had issues with it, so I wouldn't suspect that initially especially since you're properly reusing the same instance instead of creating new ones.
Reading from the parking.pdf, and from the Intel documentation, it seems that they're using discs, and compute the distance from their centre points. Your implementation is using squares (array of 1 distance between spots) and thus ignoring diagonals.
From the pdf:
If disks were being used, the distance between the particles r =
p(x(i) − z)2 + (y(i) − z)2 would need to be less than or equal to one.
Does it matter whether one uses disks or squares? An indication of the
importance of which geometric figure is parked can be obtained by
comparing the area occupied by a square of side 1.0 to the area of a
disk of diameter 1.0. The ratio of the areas, disk to square, is π/4.
Therefore, it would be anticipated that more disks could be placed in
a box than squares in the same number of tries.
And the Intel doc:
The test assumes a next random point (x, y) successfully ”parked”, if
it is far enough from every previous successfully ”parked” point. The
sufficient distance between the points (x1, y1) and (x2, y2) is
min(|x1 - x2|,|y1 - y2|) > 1.
I'm guessing that the π/4 disk to square ratio and the differences between how many discs can fit vs squares might be why you're seeing a different number. (although right now I fail to see a directly relationship between 3523 and 3070 and π/4. 3523 * π/4 = 2767, which is close, but I'm sure if there's a relationship it's slightly more complex than just simple multiplication.)
Not a great answer, but my best guess.
EDIT: Interestingly enough, I did a quick implementation using discs with 1 unit diameter and getting results around 4000 parked. So maybe there's a bit more to this than my untrained self can grasp (or maybe .NET's Random doesn't pass the test?) Anyway, here's my disc implementation:
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
//make sure it's "inside" the box
if (pointToPark.X < 0.5 || pointToPark.X > 99.5
|| pointToPark.Y < 0.5 || pointToPark.Y > 99.5)
return false;
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
Using my likely too simple application of the π/4 ratio yields about 3142. A bit closer, but it seems very incorrect.
EDIT: As #mike z pointed out, my test using directly distance is incorrect. According to the parameters of the test, which I forgot about, just checks that the X and Y distance are greater than 1. Changing my Distance check to:
Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y))
Yields a much closer result around 3450, which is pretty close. If I take out my "//make sure it's "inside" the box" check, averaged over 10 tries gets 3531!
So my final, "working" code is:
public struct Point
{
public double X,Y;
public Point(double x, double y)
{
this.X = x;
this.Y = y;
}
}
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
}
EDIT: I ran the test 100 times twice, and averaged the results to 3521.29 and 3526.74 respectively. Not sure if this means there still something slightly more to this, but perhaps this is just indicative of rounding or floating point precision differences between .NET and Fortran.