How can I run code when the contents of an Array change? - c#

Basically, I am creating a voxel-based game. I have an object Chunk with a 3 dimensional array of the object Voxel. In the array of Voxels, the Voxel currently has an isAir bool on it that signifies no voxel being there.
I am trying to add a property to Chunk which returns a 2 dimensional array of Voxel's that are on the surface of the chunk. Currently, this array will always have Voxel.isAir == false in the lower Y values and Voxel.isAir == true in the higher Y values, there is never a clear run on the "Y" axis of the array that is either all "air"s or all Voxel's, and there is never a "air" below a Voxel in the array.
To get only the "Surface" Voxel's, I have added this code to the Chunk:
public Voxel[,,] Voxels { get; set; }
private Voxel[,] _surfaceVoxels = null;
public Voxel[,] SurfaceVoxels
{
get
{
if (_surfaceVoxels == null)
{
_surfaceVoxels = new Voxel[this.Voxels.GetLength(0), this.Voxels.GetLength(2)];
for (var x = 0; x < this.Voxels.GetLength(0); x++)
{
for (var z = 0; z < this.Voxels.GetLength(2); z++)
{
for (var y = 0; y < this.Voxels.GetLength(1); y++)
{
Voxel v = this.Voxels[x, y, z];
var solidAbove = false;
var solidBelow = false;
if (y - 1 >= 0)
{
Voxel vBelow = this.Voxels[x, y - 1, z];
solidBelow = !vBelow.isAir;
}
if (y + 1 < this.Voxels.GetLength(1))
{
Voxel vAbove = this.Voxels[x, y + 1, z];
solidAbove = !vAbove.isAir;
}
if (!v.isAir && !solidAbove && solidBelow)
{
_surfaceVoxels[x, z] = v;
}
}
}
}
}
return _surfaceVoxels;
}
}
Calculating the surface voxels this way is computationally expensive, and i cannot see a faster way of doing this. Because of this I effectively "cache" the surface array, which is fine until the underlying 3 dimensional array is changed, as then the surface voxels will obviously need to be recalculated.
Is there some way on the get of public Voxel[,,] it only returns a clone of the array and not the array itself, and on the set set the entire array and set _surfaceVoxels to NULL, or even better is there some inexpensive way to execute code when values in an array are changed?
Is there some a more efficient way to accomplish what I have explained here that I have overlooked?

Related

C# having a list what's the best way to print a board

Having a list of 25 integers. What'd be the better approach to make a 5x5 board?
List<int> x = Enumerable.Repeat(0, 25).ToList();
Here's a list that's filled with 0. In the image below I had represented this list was B(a).
I did this image to show you what's the problem I'm facing to. Notice that for example B(b)[6] has a value of 7.
In a classic Matrix 5x5 the value of 6 would be 19. I'm not pretty sure how to face this problem, I think that for the game it'd better be treated like a list but with the purpose of showing the board it's a must to be like that.
How could I print this kind of board?
I would argue that the first step would be to convert the array to some representation of 2D data in some way, and then have a print method using the 2D data as input. One way to do this would be to create a wrapper around the array with methods suitable to indexing into the 1D array. For example:
public class My2DArray<T>
public int Width { get; }
public int Height { get; }
public T[] Data { get; }
public My2DArray(int width, T[] data)
{
Width = width;
Height = data.Length / width; // might want to check there is no remainder here
Data = data;
}
public T this[int x, int y]
{
get => Data[y * Width + x];
set => Data[y * Width + x] = value;
}
public string ToString(string valueSeparator , string rowSeparator)
{
var sb = new StringBuilder();
for (int y = 0; y < Height; y++)
{
var row = y * Width;
for (int x = 0; x < Width; x++)
{
sb.Append(Data[row + x])
.Append(valueSeparator);
}
sb.Append(rowSeparator);
}
return sb.ToString();
}
}

For loop that evaluates all values at once?

So i have a cellular automaton, where i can place pixels on an image and they just move down one pixel each "tick". Now the problem is since the for loop is like this:
for(int x = 0; x < 100; x++){
for(int y = 0; y < 100; y++){
//Check if nothing below (x,y) pixel and move it down if so
}
}
Then the pixels get teleported to the bottom because they get moved down every iteration of the y loop. I solved it by making the y loop go from 100 down to 0 instead of 0 to 100, so its iterating upwards but it wont work if i want to make my pixels move upwards in certain situations.
Maybe a double loop where it makes a list of which pixels to move and where in the first one and actually do it in the second but that seems quite performance heavy and im sure there is a better solution
PS: if you have a better title for the question, let me know
You need two copies of the cells. In pseudo code:
int[] currentCells = new int[...];
int[] nextCells = new int[...];
Initialize(currentCells);
while (true) {
Draw(currentCells);
Calculate next state by using currentCells as source and store result into nextCells;
// exchange (this copies only references and is fast).
var temp = currentCells;
currentCells = nextCells;
nextCells = temp;
}
Note that we loop through each cell of the destination (nextCells) to get a new value for it. Throughout this process we never look at the cells in nextCells, because these could be moved ones already. Our source is strictly currentCells which now represents the previous (frozen) state.
// Calculate next state.
for(int x = 0; x < 100; x++){
for(int y = 0; y < 100; y++){
if(currentCells[x, y] == 0 && y > 0) { // Nothing here
// Take value from above
nextCells[x, y] = currentCells[x, y - 1];
} else {
// Just copy
nextCells[x, y] = currentCells[x, y];
}
}
}
In Conway's Game of Life, for instance, you calculate the state of a cell by analyzing the values of the surrounding cells. This means that neither working upwards nor downwards will work. By having 2 buffers, you always have a source buffer that is not changed during the calculation of the next state.
Would something like this work, assuming you've got what you want to do inside the inner for loop correct?
static void MovePixels(bool moveUp)
{
for (int x = 0; x < 100; x++)
{
if (moveUp)
{
for (int y = 0; y < 100; y++)
{
}
}
else
{
for (int y = 100; y > 0; y--)
{
}
}
}
}

Tilebased boardgame dicenumber restriction (Incremental algorithms)

Developing a 2D tile-based "boardgame" I'm struggling with the restriction I have to make, when a player rolls the dice(Move 5 tiles if you land a 5 etc.)
I'm trying to use the following logic:
Start on starting point
Check the position to the sides, above and below
Check if the neighbour tiles are walkable, if they are, change them to reachable
Go to neighbour tile, repeat
I've been looking on A* and D* pathing, but it's a new subject to me and they seem more focused on getting from point A to B, not "reach" which is what I need.
How do I do this through code?
I created a 2D array from an array which was holding my tile(I need a normal array of the tilemap for another purpose):
for(int i = 0; i < 27; i++)
{
for(int j = 0; j < 33; j++)
{
tileMap[i, j] = goTile[i * 33 + j];
}
}
I now use the tileMap as my positiong factor, e.g. my players current position is tileMap[2,4].
I then tried to develop a function:
void pathFinding(Vector2 playerPosition, int diceNumber)
{
GameObject currentPos = tileMap[(int)playerPosition.x, (int)playerPosition.y];
for (int i = 0; i < diceNumber; i++) {
if (tileMap[(int)playerPosition.x + 1, (int)playerPosition.y].tag == "walkableGrid")
{
tileMap[(int)playerPosition.x + 1, (int)playerPosition.y].gameObject.tag = "reachable";
playerPosition.x++;
}
if (tileMap[(int)playerPosition.x - 1, (int)playerPosition.y].tag == "walkableGrid")
{
playerPosition.x--;
}
if (tileMap[(int)playerPosition.x, (int)playerPosition.y + 1].tag == "walkableGrid")
{
playerPosition.y++;
}
if (tileMap[(int)playerPosition.x, (int)playerPosition.y - 1].tag == "walkableGrid")
{
playerPosition.y--;
}
}
}
But as finishing this (if it even would work), would require MANY lines of code, and I believe there's a swifter method using a nested for loop maybe?
//I have now edited the code to better reflect your real data
public void ShowMoves(Vector2 playerPosition, int diceNumber, bool[] blocks)
{
int x = (int)playerPosition.x;
int y = (int)playerPosition.y;
if(tileMap.GetUpperBound(0) < x + 1)
{
if(tileMap[x + 1, y].tag == "walkableGrid" && blocks[0])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x + 1, y), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), false, y != tileMap.GetUpperBound(1), y != 0 });
}
}
if(x - 1 >= 0)
{
if(tileMap[x - 1, y].tag == "walkableGrid" && blocks[1])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x - 1, y), diceNumber - 1, new bool[] { false, x != 0, y != tileMap.GetUpperBound(1), y != 0 });
}
}
if(tileMap.GetUpperBound(1) < y + 1)
{
if(tileMap[x, y + 1].tag == "walkableGrid" && blocks[2])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x, y + 1), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), x != 0, y != tileMap.GetUpperBound(1), false });
}
}
if(y - 1 >= 0)
{
if(tileMap[x, y - 1].tag == "walkableGrid" && blocks[3])
{
/*Light up the tile*/
if(diceNumber > 0)
ShowMoves(new Vector2(x, y - 1), diceNumber - 1, new bool[] { x != tileMap.GetUpperBound(0), x != 0, false, y != 0 });
}
}
}
This code might not compile, but it's an example to help you along - it will loop until there are no available moves, and exhaust every option. It will also not go back on itself due to the blocks boolean array. The input format would be the position they're at two ints, one for x and one for y, the tiles available, the number of moves left in their roll, and the block available from the beginning (always new bool[] {true, true, true, true}
Be careful, there may be errors in my code, I wrote it in SO and have no clue how well it runs, if it runs at all or anything. Even if it does not, it should be a good starting point for you to create your logic and code it all
EDIT: Code has been changed so that it better fits how your code looks and the data types it uses
To avoid always calling the method by inputting a blocks variable of new bool[] {true, true, true, true}; you can make it an optional operator by making this the method parameters
public void ShowMoves(Vector2 playerPosition, int diceNumber, bool[] blocks = new bool[] {true, true, true, true})

Optimizing an adjacency matrix creation from a 2D array of nodes

I am attempting to create an adjacency matrix from a 2D array of nodes. The adjacency matrix will be passed to a program that will cluster the nodes either through
Spectral clustering algorithm
Kmeans clustering algorithm
**Node class **
public class Node{
public int _id;
public bool _isWalkable;
public int _positionX;
public int _positionY;
public Vector3 _worldPosition;
}
Grid Class
public class Grid : MonoBehaviour
{
void CreateGrid()
{
grid = new Node[_gridSizeX, _gridSizeY];
Vector3 worldBottomLeft = transform.position -
Vector3.right * worldSize.x / 2 - Vector3.forward * worldSize.y / 2;
//set the grid
int id = 0;
for (int x = 0; x < _gridSizeX; x++)
{
for (int y = 0; y < _gridSizeY; y++)
{
Vector3 worldPosition = worldBottomLeft + Vector3.right *
(x * _nodeDiameter + _nodeRadius) +
Vector3.forward * (y * _nodeDiameter + _nodeRadius);
//check to see if current position is walkable
bool isWalkable =
!Physics.CheckSphere(worldPosition, _nodeRadius, UnwalkableMask);
grid[x, y] = new Node(isWalkable, worldPosition, x, y);
grid[x, y].Id = id ++;
}
}
totalNodes = id;
}
}
Nodes are stored inside a 2D array called grid and represent a walkable path for a character to move on. I have succesfully implemented an A* algorithm with a euclidean distance heuristic. What I would like to do is cluster these nodes using the aforementioned clustering algorithms, but first I need to create an adjacency algorithm for them. This is the best pseudocode I could come up with
int[][] _adjacencyMatrix = new int[gridSizeX*gridSizeY][gridSizeX*gridSizeY];
for(int x = 0; x < gridSize;x< XgridSize; i++)
{
for(int y = 0; y < gridSize;y< YgridSize; i++)
{
if( !Grid[x][y]._isWalkable)
continue;
Node n = Grid[x][y];
List<Node> neighbors = GetNeighbors(n);
for(int k; k<neighbors.Count(); k++)
{
_adjacencyMatrix[n._id][neighbors[k]._id]=1;
}
}
}
public List<Node> GetNeighbours(Node n)
{
//where is this node in the grid?
List<Node> neighbours = new List<Node>();
//this will search in a 3X3 block
for (int x = -1; x <= 1; x++)
{
for (int y = -1; y <= 1; y++)
{
if (x == 0 && y == 0)
continue; //we're at the current node
int checkX = n._positionX + x;
int checkY = n._positionY + y;
if (checkX >= 0 && checkX < _gridSizeX && checkY >= 0
&& checkY < _gridSizeY)
{
if(grid[checkX, checkY]._isWalkable)
neighbours.Add(grid[checkX, checkY]);
else
continue;
}
}
}
return neighbours;
}
My main concern
My main concern with this is the total complexity of the above algorithm. It feels like it's going to be heavy and I have a total of (75^2 = 5625) nodes in a adjacency matrix that will be 5625X5625 in size! There must be a better way to find the neighbors than this, is there?
The matrix is symmetric, so you only need to save half of it, see (How to store a symmetric matrix?) for an example. The matrix values are binary, so saving them as booleans or in a bit vector will cut down memory by a factor of 4 or 32, respectively.
Alternatively, since the check for two adjacent nodes takes constant time (abs(n1.x - n2.x) <= 1 && abs(n1.y - n1.y) <= 1 && grid[n1.x, n2.x].isWalkable() && grid[n2.x, n2.y]), you could just pass the clustering algorithm a function which checks for adjacency on-the-fly.
5k by 5k is not very large. 100 MB is something you can keep in memory. If you want to avoid this cost, do not use algorithms based on distance matrixes!
However, since your similarity appears to be
d(x,y) = 1 if adjacent and both nodes walkable else 0
your results will degenerate. If you are lucky, you get something like connected components (which you could have gotten much easier).
Pairwise shortest paths would be more useful, but also more expensive to build. Maybe consider solving this first, though. Having a full adjacency matrix is a good starting point I guess.
k-means cannot work with pairwise distances at all. It needs distances point-to-mean only, for arbitrary means.
I suggest to look at graph algorithms, and spend some more time understanding your objective, before trying to squeeze the data into clustering algorithms that may be solving a different problem.

How to create Linked Array List in C#

I need to create an Array with Linked list capacities.
Basically, I need a static index based list (like array), but with the possibility to get next and previous field (and easily loop back and forward through list, like with linked list).
Note: Array is 2 dimensional. I use a custom class as array values. So I can set previous and next property for each instance.
Is there a built in C# collection for this? If not, any suggestions on how to create a very simple version of this? (I already have a version of this, consisting of 2 methods. One that loops forward to set the previous field, and one to loop backwards that set the next field, but it's still to messy).
Thanks in advance
EDIT:
The problem is my use of 2dimensional array. If loop through my array:
for (byte x = 0; x < Grid.GetLength(0); x++)
{
for (byte y = 0; y < Grid.GetLength(1); y++) /
{
//At certain point, I need to get the previous field. I can do:
if (y != 0)
{
y -= 2; //-2 because I will y++ in for. Already getting messy
}
else
{
//What if y == 0? Then I can't do y--. I should get max y and do x-- to get previous element:
y = (byte)(Grid.GetLength(1) - 1); //to get max value y
x--;
}
}
}
There is a built-in LinkedList<T> class.
But from your description why wouldn't an array work? It's static, and index-based, and you can easily get the next and previous element by incrementing / decrementing the index. It's hard to see exactly what you need from your code, but I'd like to point out that you can easily enumerate over a multi-dimensional array with:
var arry = new int[2,3];
foreach(var item in arry)
{
...
}
So you might be able to combine this with a Stack<T> structure (push items on the stack and pop them off to get the previous).
Alternatively, you can turn the array into a LinkedList directly.
var list = new LinkedList(arry.Cast<int>()); // flattens array
Or to preserve the indexes from the original array and still loop through the values as a linked list use:
var list = new LinkedList(arry.Cast<int>.Select((item, i) => new
{
Item = item,
Index1 = i % arry.GetLength(1),
Index2 = i / arry.GetLength(0)
}));
var node = list.First;
while(node.Next != null)
{
Console.WriteLine("Value # {1}, {2}: {0}", node.Value.Item, node.Value.Index1, node.Value.Index2);
// on some condition move to previous node
if (...)
{
node = node.Previous;
}
else
{
node = node.Next;
}
}
No, you don't. Instead of abandoning traditional arrays in lieu of "smart linked node arrays" which is what it seems like you're heading towards, try just adding a couple variables in your loop body:
byte x_len = Grid.GetLength(0);
byte y_len = Grid.GetLength(1);
byte prev_x, next_x, prev_y, next_y;
for (byte x = 0; x < x_len; ++x)
{
prev_x = x == 0? x_len - 1 : x - 1;
next_x = x == x_len - 1? 0 : x + 1;
for (byte y = 0; y < y_len; ++y)
{
prev_y = y == 0? y_len - 1 : y - 1;
next_y = y == y_len - 1? 0 : y + 1;
// here, you have access to the next and previous
// in both directions, satisfying your requirements
// without confusing your loop variables.
}
}

Categories

Resources