C# Shifting items in an array down and skip spaces - c#

I am trying to work out a method by which I can shift items in an array of objects, but keep some items in the array exactly where they are, having all items above dropping bellow the fixed items. Think in terms of a Gem Matching game, where gems can be matched. Here are the rules:
Board can be of any X,Y size
Board is filled with Gems (that can break and therefore be destroyed) and Rocks that cannot break (and therefore cannot be destroyed)
Rocks are stationary in their position and cannot move.
When user matches gems, they get destroyed and all the remaining gems drop down to the next available space.
If gems that are matched are below a Rock, then gems above the rock should skip the rock position and fill the bottom most open position.
Rocks can be stacked n deep, vertically or horizontally.
My code works, but only if there is one rock. Please note, this is written in C# for Unity, but the solution is not Unity specific (or even c#). If there are two Rocks stacked on top each other, like this:
[Gem_1]
[Rock_1]
[Rock_2]
[Gem_2]
[Gem_3]
[Gem_4]
And Gems 2,3 and 4 are destroyed, then the gems should all fall and look like this:
[Gem_7]
[Rock_1]
[Rock_2]
[Gem_6]
[Gem_5]
[Gem_1]
But, with my code, when they fall, they look like this:
[Gem_7]
[Rock_1]
[Rock_2]
[Gem_1]
[Gem_6]
[Gem_5]
Here is my Updated code:
private gem_script NextGem(Vector2Int currentGem)
{
for (int y = 0; y < board_height; y++ )
{
if(allGems[currentGem.x, y] != null && isGem(layoutStore[currentGem.x, y]) && y > 0){
return allGems[currentGem.x, y].GetComponent<gem_script>();
}
}
return null;
}
private IEnumerator ScrollNewGems()
{
for (int x = 0; x < board_width; x++)
{
for (int y = 0; y < board_height; y++)
{
if (layoutStore[x, y] != Gem_Type.empty)
{
if (allGems[x, y] == null)
{
Vector2Int current_gem_pos = new Vector2Int(x, y);
gem_script next_gem = NextGem(current_gem_pos);
if(next_gem != null)
{
Vector2Int next_gem_pos = next_gem.GetGemPos();
next_gem.SetGemXYPos(new Vector2Int(current_gem_pos.x, current_gem_pos.y));
allGems[x, y] = allGems[next_gem_pos.x, next_gem_pos.y];
allGems[next_gem_pos.x, next_gem_pos.y] = null;
}
}
}
}
}
}
Edit
I updated my code, but this only works if there is one Gem to drop down. If there are more than one gems, then they stay on the top

There was a typo in my edited script. The nextGem function should not start a y=0 but instead at currentGem.y. Otherwise, it starts from the beginning. Complete working code is:
private gem_script NextGem(Vector2Int currentGem)
{
for (int y = currentGem.y; y < board_height; y++ )
{
if(allGems[currentGem.x, y] != null && isGem(layoutStore[currentGem.x, y])){
return allGems[currentGem.x, y].GetComponent<gem_script>();
}
}
return null;
}
private IEnumerator ScrollNewGems()
{
gameState = GameState.Wait;
yield return new WaitForSeconds(.2f);
for (int x = 0; x < board_width; x++)
{
for (int y = 0; y < board_height; y++)
{
if (layoutStore[x, y] != Gem_Type.empty)
{
if (allGems[x, y] == null)
{
Vector2Int current_gem_pos = new Vector2Int(x, y);
gem_script next_gem = NextGem(current_gem_pos);
if(next_gem != null)
{
Vector2Int next_gem_pos = next_gem.GetGemPos();
next_gem.SetGemXYPos(new Vector2Int(current_gem_pos.x, current_gem_pos.y));
allGems[x, y] = allGems[next_gem_pos.x, next_gem_pos.y];
allGems[next_gem_pos.x, next_gem_pos.y] = null;
}
}
}
}
}
}

Related

What is the dynamic programming solution for Hamiltonian path algorithm used in this code?

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

Scan Line Flood Fill Algorithm in C#

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!!

Embedded For loops for a precipitation map, with Perlin noise in the Unity3d game engine issues

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

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

A* (A star) algorithm not exactly working

I'm trying to implement an A star searching method for a college work, so i kinda need to make it from scratch, but I'm having some trouble making it work the correct way.
Here's a picture of my problem:
As you can see, it does find a path, but not the easiest one.
Here is my implement:
public List<node> updateAstar(){
//clear all the lists
openedNodes.Clear();
closedNodes.Clear();
nodesToLook.Clear();
//check if starpos and endpos are okay
if(startPos!=null && endPos!=null){
float F;
node currentNote=Grid.getNodeAtPos(startPos);
openedNodes.Add(currentNote);
int i = 0;
int size = 100;
while(currentNote.type!=tilesType.END){
if(i<=size){ //debugging purpose. prevent infinite loop
nodesToLook.Clear();
foreach(node nearNode in currentNote.getNearestTiles()){
if(closedNodes.Find(r => ((r.pos.x==nearNode.pos.x)&&(r.pos.y==nearNode.pos.y)))==null){
nodesToLook.Add(nearNode);
}
}
float bestValue=float.PositiveInfinity;
node bestNode=new node();
foreach(node lookingNode in nodesToLook){
//check if current node is not on the closed list
if((closedNodes.Find(r => ((r.pos.x==lookingNode.pos.x)&&(r.pos.y==lookingNode.pos.y)))==null)
&&(openedNodes.Find(r => ((r.pos.x==lookingNode.pos.x)&&(r.pos.y==lookingNode.pos.y)))==null)
&& lookingNode.type!=tilesType.BLOCK){
//calculate F=G+H
//assume path number is 0 for the question purpose
F=lookingNode.G[pathNumber]+lookingNode.H[pathNumber];
if(F<bestValue){
bestValue=F;
bestNode=lookingNode;
}else
closedNodes.Add(lookingNode);
}
}
openedNodes.Add(bestNode);
currentNote=bestNode;
i++;
}else{
Debug.Log("Error getting better path");
break;
}
}
}else Debug.Log("Current path does not have an startpos nor endpos");
return openedNodes;
}
Here is how I instantiate each node (I save it on a matrix):
coordinate posAux=new coordinate();
this.myNodes=new node[columnNumber,lineNumber];
this.lineNumber=lineNumber;
this.columnNumber=columnNumber;
for(int y=0;y<lineNumber;y++){ // Y Desce = linhas
for(int x=0; x<columnNumber; x++){ // X vai pro lado = colunas
//create a node based on matrix position
posAux.Set(x, y);
tilesType type;
node current=new node(posAux);
//update up and left nodes
//"nodeDireita" means rightNode and "nodeEsquerda" means left node
if(x-1>=0){
current.nodeEsquerda=myNodes[x-1, y];
myNodes[x-1, y].nodeDireita=current;
}
if(y-1>=0){
current.nodeAcima=myNodes[x, y-1];
current.nodeAcima.nodeAbaixo=current;
}
//UNity stuff to set type of node visually based on what object is in it
Collider[] colliders;
if((colliders = Physics.OverlapSphere(coordinate.gridToUnity(posAux), 3f)).Length >0){
foreach(Collider collider in colliders){
objScript obj = collider.gameObject.GetComponent<objScript>();
current.type=obj.type;
if(current.type==tilesType.START){
path Path = new path (obj.pos, obj.posEnd, this);
addPath (Path);
Path.numeroPath=paths.IndexOf(Path);
}
}
}
myNodes[x,y]=current;
}
}
//adicionar vetor[] para H e G com numero de paths nos nodes
//create a vector for multiple paths in each node
int numeroPaths = paths.Count;
for (int y = 0; y < lineNumber; y++) {
for (int x = 0; x < columnNumber; x++) {
myNodes [x, y].H=new float[numeroPaths];
myNodes [x, y].G=new float[numeroPaths];
}
}
//adicionar Heuristica e G para cada node em cada path
//calculate heuristic and G for each node in each path
foreach (path Path in paths) {
coordinate start=Path.startPos, end=Path.endPos;
int numeroPath=paths.IndexOf(Path);
for (int y = 0; y < lineNumber; y++) {
for (int x = 0; x < columnNumber; x++) {
coordinate pos = myNodes [x, y].pos;
//G e H as manhattan distance
/*Mathf.Sqrt(Mathf.Pow((start.x - pos.x), 2) + Mathf.Pow((start.y - pos.y), 2)); euclidian-does not apply x.x */
myNodes [x, y].H[numeroPath]=Mathf.Abs(pos.x-end.x) + Mathf.Abs(pos.y-end.y);
myNodes [x, y].G[numeroPath]=Mathf.Abs(start.x-pos.x) + Mathf.Abs(start.y-pos.y);
}
}
}
Code refs:
--node is a custom class that holds "G" and "H" that I use the Manhattan formula to define, "x", "y", "BLOCK" or "NORMAL"(availability of the position)
--openedNodes is a List that I put the correct nodes for the path
--closedNodes are the nodes I checked, but have bigger "F" values;
--nodesToLook are the neighbor nodes for checking.
I appreciate any help.
Thanks.
Since you haven't posted your whole code, i have not the slightest clue what you are doing with your nodes, but what i can see:
You are not updating G. G is not a heuristic, it is the actual cost of reaching that node. Aka: nextTile.G = currentTile.G + distanceBetween(currentTile,nextTile)
You are only adding the best option to the open list. So instead of checking all 4, you only check 1
i can go on, but your whole algorithm does not work like A*, at all. Fixing your code means rewriting it completely.
The algorithm is realy easy. The pseudocode on wikipedia can be copied and implemented directly. It just seems you missed a couple of steps there and implemented a lot of things incorrectly.

Categories