C# - Recursive Function Issue - c#

Here's my function:
static Map AddFormation(Map _map, Tile tile, int x, int y, int length,
Random rand, Tile endTile = (Tile)Int32.MaxValue)
{
//so a call to AddFormation without the endTile will work, if I don't want a border.
if ((int)endTile == Int32.MaxValue) endTile = tile;
if (x >= 0 && x < _map.Data.GetLength(0) && y >= 0 && y < _map.Data.GetLength(1))
{
if (_map.Data[x, y].Tile != tile)
{
if (length > 0)
{
_map.Data[x, y].Tile = tile;
int newlength = length - 1;
AddFormation(_map, tile, x, y - 1, newlength, rand, endTile); // ^
AddFormation(_map, tile, x, y + 1, newlength, rand, endTile); // v
AddFormation(_map, tile, x - 1, y, newlength, rand, endTile); // <-
AddFormation(_map, tile, x + 1, y, newlength, rand, endTile); // ->
}
else
{
_map.Data[x, y].Tile = endTile;
}
}
}
return _map;
}
I have a Tile enum which is to make my life easier when working with the tiles.
I have a Cell class which contains a Tile enum called "Tile" and other info (unimportant to this)
The Map class contains a Cell[,] group called Data.
What I am trying to achieve is to create a block of the specific tile at a specific point, I will later incorporate Randomisation into this (so it wouldn't be just a diamond) but I took it out to see if that was the cause of my issue.
The problem is a call to this function always produces blocks taller than they are wide and I can't for the life of me see why..
I created a test function to see what happens if I use something like:
public static int[,] Add(int[,] grid, int x, int y, int length, int value)
{
if (x >= 0 && y >= 0 && x < grid.GetLength(0) && y < grid.GetLength(1))
{
if(grid[x,y] != value)
{
if(length > 0)
{
grid[x, y] = value;
Add(grid, x - 1, y, length - 1, value);
Add(grid, x + 1, y, length - 1, value);
Add(grid, x, y - 1, length - 1, value);
Add(grid, x, y + 1, length - 1, value);
}
}
}
return grid;
}
Which seems to suffer from the same problem if you go big enough (5 produces a perfect diamond, 6 produces a strange shape and something like 11 even stranger)

Ok, after spending a long time on this (I do like recursion), here is partway to the solution (it may be hard to explain):
The problem is that you are allowing the "path" to backtrack along the cells that have already been allocated as endTiles. If you take a look at your first method, you make the search point go down straight after it has searched up. You simply need to remove that.
This is the code I am using (notice that it calls AddFormationAlt twice, once for going up, once for going down):
class Program
{
static string left;
static string right;
static void Main(string[] args)
{
int size = 20;
int sizem = size*2 + 1;
Map m = new Map(new int[sizem,sizem]);
AddFormationAlt(m, 1, size, size, size-1, 2);
var l = left;
var r = right;
}
private class Map
{
public int[,] Data { get; set; }
public Map(int[,] data)
{
Data = data;
}
public string Print()
{
StringBuilder sb = new StringBuilder();
for (int x = 0; x < Data.GetLength(0); x++)
{
for (int y = 0; y < Data.GetLength(1); y++)
sb.Append(Data[y, x] == 0 ? " " : Data[y,x] == 1 ? "." : "#");
sb.AppendLine();
}
return sb.ToString();
}
}
static void AddFormationAlt(Map _map, int tile, int x, int y, int length, int endTile)
{
// You may need to change the cloning method when you change the tiles from ints
Map m1 = new Map((int[,])_map.Data.Clone());
Map m2 = new Map((int[,])_map.Data.Clone());
// Contains the left and right half of the Map you want, you need to join these together.
Map aleft = AddFormationAlt(m1, true, tile, x, y, length, endTile);
Map aright = AddFormationAlt(m2, false, tile, x, y + 1, length, endTile);
left = aleft.Print();
right = aright.Print();
}
static Map AddFormationAlt(Map _map, bool up, int tile, int x, int y, int length, int endTile)
{
if (x >= 0 && x < _map.Data.GetLength(0) && y >= 0 && y < _map.Data.GetLength(1))
{
if (_map.Data[y, x] != tile)
{
if (length > 0)
{
_map.Data[y, x] = tile;
int newlength = length - 1;
// Either go 'up' or 'down'
if(up)
AddFormationAlt(_map, true, tile, x, y - 1, newlength, endTile); // ^
else
AddFormationAlt(_map, false, tile, x, y + 1, newlength, endTile); // v
AddFormationAlt(_map, up, tile, x - 1, y, newlength, endTile); // <-
AddFormationAlt(_map, up, tile, x + 1, y, newlength, endTile); // ->
}
else
_map.Data[y, x] = endTile;
}
}
return _map;
}
}
I changed all your Data[x, y] to Data[y, x] because that's how I usually store them and then it worked xD.
In aleft and aright you have the left half and the right half of the diamond you want in separate Maps, you need to join them together somehow (shouldn't be too hard for a clever guy like you :). left and right show the textual representation of Maps (note the overlap in the centre):
left:
#
#.
#..
#...
#....
#.....
#......
#.......
#........
#.........
#..........
#...........
#............
#.............
#............
#...........
#..........
#.........
#........
#.......
#......
#.....
#....
#...
#..
#.
#
right:
#
.#
..#
...#
....#
.....#
......#
.......#
........#
.........#
..........#
...........#
............#
.............#
............#
...........#
..........#
.........#
........#
.......#
......#
.....#
....#
...#
..#
.#
#
You need to clean this up and change all the classes back to your own ones. I hope this helps!

When you say:
if(grid[x,y] != value)
You're telling it to only continue down this "leg" if you don't run into any blocks that have already been set to this value. The problem is that once you get a long enough length, the "leg" going out the top of the starting point "spirals around" to the left and right, and so when the recursion finally comes back to the point where it starts trying to go out the left or right, there is already a square there and you return immediately.
It looks like you want to take the if(length > 0) and put it after the if(grid[x,y] != value) block, rather than inside of it. That way, you only "set" the value if it hasn't already been set, but you will continue until you reach the appropriate length.
Of course, since "branches" (i.e. if statements) take longer than "assignments" (i.e. setting a value in an array), you might as well just remove the if(grid[x,y] != value) entirely, and risk setting spots to the same value multiple times, because it's cheaper than comparing the current value.
if (x >= 0 && y >= 0 && x < grid.GetLength(0) && y < grid.GetLength(1))
{
grid[x, y] = value;
if(length > 0)
{
Add(grid, x - 1, y, length - 1, value);
Add(grid, x + 1, y, length - 1, value);
Add(grid, x, y - 1, length - 1, value);
Add(grid, x, y + 1, length - 1, value);
}
}
return grid;

Don't you want something like
if(grid[x,y] != 0) // or whatever the initial value is
instead of
if(grid[x,y] != value)
Otherwise, when you grow out, it will grow back to the seed point.

Related

How to calculate the Snake Index in N dimensions?

I have written the following code in c# where it calculates the Snake index of a 2-dimensional point.
public static uint SnakeCurveIndex(uint bits, uint x, uint y )
{
uint index = 0;
//The dimension of the array
uint dim = (uint)Math.Pow( 2.0 , (double)bits);
if(y % (uint)2 == 0 )
{
index = x + y * dim;
}
else
{
index = (dim - 1 - x) + y * dim;
}
if (index >= dim*dim)
{
//Debug console
throw new Exception("The index is out of bounds");
}
return index;
}
The variable bits it responsible for the order of the curve. The following image represents the order of the curve for 1 to 3.
My question is who to extend this code for n-dimensional points? Do I need a multidimensional-array or some other technique?
Thank you for your time.

Reverse image searching

Is there any ways to reverse the image search? Instead of scanning from top-left to bottom-right, start from bottom-left to top-right ?
That's how I scan the image
for (int y = 0; y < matches.GetLength(0); y++)
{
for (int x = 0; x < matches.GetLength(1); x++)
{
double matchScore = matches[y, x, 0];
if (matchScore > threshold)
{
Console.WriteLine("There is a Match");
Console.WriteLine($"Coords: {x},{y}");
return new Point(x, y);
}
}
}
return new Point(-1, -1);
I just scan a screenshot and find matches on it using emgu.cv
Can I reverse the scan, starting from the bottom instead of top?
Let say, there is 2 matches at X100, Y100 and X350, Y350
it should scan from X350 -> X100, instead of X100 -> X350 and thus return X350, Y350
You can write:
for ( int y = matches.GetLength(0) - 1; y >= 0; y-- )
for ( int x = matches.GetLength(1) - 1; x >= 0; x-- )
if ( matches[y, x, 0] > threshold )
{
Console.WriteLine("There is a Match");
Console.WriteLine($"Coords: {x},{y}");
return new Point(x, y);
}
Because you return the first searched element found, it is possible that this loop and yours do not find the same result, if for example your finds a first element at the top, and this one at the bottom, so your returns the top element and this one returns the bottom element...
Choosing a loop direction depends of what you want to return first, from top to bottom or bottom to top, and from left to right or right to left.

Is it possible to write rule based iterators a 2d array of structs in C#(for nieghbours of a tile in a grid)?

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]);
}
}

How to make Flood fill algorithm return different Lists of separate groups in the grid?

So i have this flood fill method :
public int groupedCells(int y, int x, int value)
{
//base state
if (!gSetup.isValidLocation(y, x) ||
gSetup.getCell(y, x).value != value ||
gSetup.getCell(y, x).cMAtch.isInMatchPool)
{
return 0;
}
// keep track of matched cells
gSetup.getCell(y, x).cMAtch.isInMatchPool = true;
// collect
int up = groupedCells(y + 1, x, value); // up
int left = groupedCells(y, x - 1, value); // left
int right = groupedCells(y, x + 1, value); // right
int down = groupedCells(y - 1, x, value); // down
// sum
int total = up + left + right + down + 1;
return total;
}
basically i have this grid :
and currently if i click on a yellow triangle, the method above will tell me how many of them are next to each other.
What i want is a solution that will run all over the grid, and check if a group has :
less than 3-triangles then do (A)
if it has 3-triangles then do (B)
if it has 4-triangles then do (C)
etc ...
Am not sure about this, but i thought the best way is to have a list for every group, and then check those conditions over the lists.
I tried it here but the list keep re-initializing itself, so it always either have 0 cells or 1 :
public List<CellSetup> listedCells(int y, int x, int value)
{
List<CellSetup> cList = new List<CellSetup>();
//base state
if (!gSetup.isValidLocation(y, x) ||
gSetup.getCell(y, x).value != value ||
gSetup.getCell(y, x).cMAtch.isInMatchPool)
{
return cList;
}
// keep track of matched cells
gSetup.getCell(y, x).cMAtch.isInMatchPool = true;
cList.Add(gSetup.getCell(y, x));
// collect
List<CellSetup> up = listedCells(y + 1, x, value); // up
List<CellSetup> left = listedCells(y, x - 1, value); // left
List<CellSetup> right = listedCells(y, x + 1, value); // right
List<CellSetup> down = listedCells(y - 1, x, value); // down
return cList;
}
The result of that method when its called here :
for (int y = 0; y < gSetup.cell2DArray.GetLength(0); y++)
{
for (int x = 0; x < gSetup.cell2DArray.GetLength(1); x++)
{
print(listedCells(y, x, 1).Count);
}
}
is printing "1" three times and "0" 22 times.
NOTE : this is for a game, made in unity.
Thank you
You are calling listedCells once for each cell in the grid, so you are going to get 25 lists.
You should use your value and IsInMatchPool fields to skip cells that don't have a triangle, or have already been grouped.
So the algorithm is:
for every cell:
if cell has a triangle and cell is not already marked:
create new list by flood-filling from cell (marking cells as you fill)
At the end of that process, you have a list of all groups of connected cells.
Also, note that your listedCells method doesn't add up, down, etc to cList. You probably want to do that.

Pathfinding - Index out of Bounds

Good day everyone, I'm writing my own pathfinding script, first had it on paper then started coding and let me tell you, it's much harder in practice than in theory. So, I've ran into a problem, which I of course can't solve.
The problem presents itself in the following images:1) In this shot, the waypoint is set to (6,6) and returns no errors.
2) Note the 2 points in the upper right corner, one shows no direction the other shows up. Error is the node that points upwards. In this shot, the waypoint is moved to (7,5) at which point it starts throwing errors starting from the last index. The more I move the waypoint closer to the bottom-right corner, the more points at X=13 down the Y axis throws exceptions.
Relevant code:
for (int x = 0; x < map.sizeX; x++)
{
for (int y = 0; y < map.sizeY; y++)
{
if (!(x == pVal.x && y == pVal.y) && map.grid[x, y].passable )
{
float dot = 1;
var heading = (grid[x, y].position - t.position).normalized;
heading.y = 0;
foreach (Vector3 direction in map.GetDirections())
{
var dot2 = Vector3.Dot(heading, direction.normalized);
if (dot > dot2 )
{
if (map.grid[x + (int)direction.x, y + (int)direction.y].passable)
{ // Error thrown when it reaches this if-statement \\
grid[x, y].direction = direction;
dot = dot2;
}
}
}
}
}
}
This Index out of Bounds error is only thrown when I add the check to see if the point towards the direction is passable or not. Another thing to note is that I use direction.y where the directions are actually stored in x and z. For some reason if I use the z instead of y, it stops working completely.
When in doubt, try walking through a test case to see what goes wrong.
Let's say we're in the second image, x = 13, y = 12
if (!(x == pVal.x && y == pVal.y) && map.grid[x, y].passable )
(13, 12) is not our target point, and is passable, so we pass this test and proceed to the next line...
float dot = 1;
var heading = (grid[x, y].position - t.position).normalized;
heading.y = 0;
heading ends up being something like (0.659, 0, 0.753) here, though if you have some y offsets it might be shorter since you normalize it before zeroing the y.
foreach (Vector3 direction in map.GetDirections())
I don't know what order your directions are stored in, so I'll just guess here:
{(0, 0, 1), (1, 0, 1), (1, 0, 0), (1, 0, -1)...}
Starting with (0, 0, 1) then...
var dot2 = Vector3.Dot(heading, direction.normalized);
if (dot > dot2 )
dot is still 1, and dot2 is 0.753 so we pass this test, check that the cell above is passable (even though that points wildly away from the direction we want to go! More on that soon), set dot = dot2 and try the next direction:
(1, 0, 1) normalizes to (0.707, 0, 0.707). dot is 0.753 and dot2 is 0.466 + 0.532 = 0.998 so we fail the dot > dot2 test and skip this one.
Here's the killer: (1, 0, 0)
dot is still 0.753 and dot2 is 0.659, so we pass the dot > dot2 test, and proceed to check the cell in that direction:
if (map.grid[x + (int)direction.x, y + (int)direction.y].passable)
{ // Error thrown when it reaches this if-statement \\
No kidding an error is thrown! We were already at x = 13 (ie. map.sizeX - 1) and we added 1, so we're off the edge of the board!
So, this error is easy to detect just by walking through the problem case.
Possible fixes (from most to least hacky):
Do bounds checking whenever you try to access an adjacent cell, and skip it if it would lead off the map.
Add a border of unused grid cells around your map, so checking one cell off the edge never poses a problem.
Consider switching to a more conventional, well-studied pathfinding algorithm, like Breadth-first search (all moves cost the same) or Djikstra's algorithm (distinct move costs) if you want to populate the entire grid with pathing information, or A* if you want the shortest point-to-point path.
Make a simple function that checks if you are within the bounds of the map.grid 2D array before doing if (map.grid[x + (int)direction.x, y + (int)direction.y].passable).
Check if map.grid[x + (int)direction.x is less than map.sizeX-1
then check if map.grid[ y + (int)direction.y] is less than map.sizeY-1.
If both conditions are met, go ahead with the if (map.grid[x + (int)direction.x, y + (int)direction.y].passable).
Here is a simple function to simplify that:
bool isWithinBound(Vector3 direction, int sizeX, int sizeY, int x, int y)
{
return ((x + (int)direction.x < sizeX - 1) && (y + (int)direction.y < sizeY - 1));
}
Now you can just do:
for (int x = 0; x < map.sizeX; x++)
{
for (int y = 0; y < map.sizeY; y++)
{
if (!(x == pVal.x && y == pVal.y) && map.grid[x, y].passable)
{
float dot = 1;
var heading = (grid[x, y].position - t.position).normalized;
heading.y = 0;
foreach (Vector3 direction in map.GetDirections())
{
var dot2 = Vector3.Dot(heading, direction.normalized);
if (dot > dot2)
{
//Check if we are within bounds
if (isWithinBound(direction, map.sizeX, map.sizeY, x, y))
{
if (map.grid[x + (int)direction.x, y + (int)direction.y].passable)
{ // Error thrown when it reaches this if-statement \\
grid[x, y].direction = direction;
dot = dot2;
}
}
}
}
}
}
}

Categories