Issues with line-clearing algorithm for Tetris game? - c#

I'm working on a text-based Tetris game and running into some issues with clearing lines. I have my DeleteRow() method which deletes the given row by working upwards from the given row, overwriting each row's data with the data of the row above it. This seems to work:
/**
* Deletes row r from the array of landed Tetromino blocks.
* #param r The row to delete from the landed array.
**/
private void DeleteRow(int row) {
for(int r = row; r > 0; r--) {
for(int c = 0; c < Board.WIDTH; c++) {
// Overwrite the value of this column from the row above.
still[r, c] = still[(r - 1), c];
}
}
}
Where "still" is the 2D array defined as such.
private int[,] still;
And initialized here:
public Board() {
still = new int[Board.HEIGHT, Board.WIDTH];
}
But in my CheckRows() method, which is what calls DeleteRow(), I seem to be having an issue where it will clear the first row that's passed to it, but subsequent rows are either ignored or it will delete the wrong row:
/**
* Checks each row to see if they are full, and if so, deletes the row and adds to score.
* #param score A long representing the current score, passed as reference.
**/
public void CheckRows(ref long score) {
List<int> rowsToClear = new List<int>();
for(int r = 0; r < Board.HEIGHT; r++) {
bool zero = false;
for(int c = 0; c < Board.WIDTH; c++) {
if(still[r, c] == 0) {
zero = true;
break;
}
}
if(!zero) rowsToClear.Add(r);
}
// Delete all the rows that did not contain zeros.
if(rowsToClear.Count > 0) {
// Add to the score depending on the number of rows cleared.
score += (new int[4] { 40, 100, 300, 1200 })[rowsToClear.Count - 1];
// Delete each row in the list and then increment all other row indices to account for
// the fact that the rows above this one are being copied to this one.
for(int i = (rowsToClear.Count - 1); i >= 0; i--) {
DeleteRow(rowsToClear[i]);
rowsToClear.ForEach(x => x++);
}
}
}
I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
I have noticed though that if those rows are not deleted in the first CheckRows() call, they will be in the next iteration of the main game loop.
Is there some flaw in the logic I'm using? This is my first time making a Tetris game. I'm doing this to familiarize myself with C#, and I'm just not sure what the issue is here. Can someone else spot what's wrong?

I have to assume this has something to do with the line following the call to DeleteRow, where I increment the row numbers of the other rows that need to be cleared to account for the fact that I'm shifting each row downward to delete the row.
This is the line you are speaking of:
rowsToClear.ForEach(x => x++);
That line of code does absolutely nothing: The values will not be incremented.
You can perform an action on the element passed to the delegate in ForEach but you cannot replace it with a new value. Here is the documentation from MSDN:
Modifying the underlying collection in the body of the Action delegate is not supported and causes undefined behavior.
That will not work and neither will this:
foreach (var thisNum in nums)
{
thisNum++; // <== will not compile
}
This will increment the list:
for (int i = nums.Count - 1; i >= 0; i--)
{
nums[i]++;
}
<== Try Me ==>

After rearranging my CheckRows method around, I realized that the solution was that I needed to delete the rows in top to bottom order, as opposed to bottom to top.

Related

How do I store references to elements of a 2D array?

I'm checking each element of a 2d array for specific conditionals.
If the element is true I want to check it's direct neighboring adjacent elements.
If the neighboring elements are false I want to keep track of them, lets call these "validElements".
Once all elements have been checked I want to pick one of the validElements at random and make it true, then clear that collection of validElements and repeat this process an arbitrary amount of times.
I know how to check each element and their neighbors for the conditionals but i'm not sure the proper way to keep track of the references to elements that are considered "validElements". How should I keep track of this data?
public bool [,] Grid = new bool [3,3];
// lets make the middle element true so we have a starting point.
Grid[Grid.GetLength(0) / 2, Grid.GetLength(1) / 2] = true;
for (int row = 0; row < Grid.GetLength(0); row++)
{
for (int col = 0; col < Grid.GetLength(1); col++)
{
if (Grid[row, col])
{
// if neighboring cells are false keep track of them somehow
}
}
}
One of the most basic solutions would be to create a list of coordinates where you'll store all the Xs and Ys where you found a valid element. So, before your
for (int row = 0; row < Grid.GetLength(0); row++)
add a
var validElements = new List<(int x, int y)>();
and instead of your
// if neighboring cells are false keep track of them somehow
do
if (/*some condition*/)
{
validElements.Add((row, col));
}
Next, once the loop is done, you need to pick a random valid element and set it to true. You can do this by
var random = new Random();
var randValidElement = validElements[random.Next(validElements.Count - 1)];
// above line is assuming you have at least one valid element.
// You should do a check if validElements.Count == 0 then do something
Grid[randValidElement.x, randValidElement.y] = true; // this is the final step

C# Loop to scan top,bottom,left,right elements of 2d Array

I am trying to complete a game over condition for a peg solitaire game i am writing.
I have all of the movements and pieces removing as they should.
Each piece is held within a 2d array as an Ellipse UI Ellement and when a piece is taken it is replaced with a border Ui Element.
I have started to use a method adapted from a Stackoverflow post that scans the 8 adjacent squares around the array element.
public static IEnumerable<T> AdjacentElements<T>(T[,] arr, int row, int column)
{
int rows = arr.GetLength(0);
int columns = arr.GetLength(1);
for (int j = row - 1; j <= row + 1; j++)
for (int i = column - 1; i <= column + 1; i++)
if (i >= 0 && j >= 0 && i < columns && j < rows && !(j == row && i == column))
yield return arr[j, i];
}
}
The method called once a piece is taken.
var results = AdjacentElements(grid, 3, 3);
foreach (var result in results)
Debug.WriteLine(result);
When it encounters an ellipse it should check the squares directly above,below,left and right of the Ellipse, at the moment is all 8 squares, i only need four (top, bottom, left and right).
I am using grid reference 3,3 to test at the moment and it is printing out as expected but for all 8 squares.
If any of the four squares in turn encounter and ellipse the next square in a straight line should be a Border in order to be a possible move.
For example:
Circle 1 is the Ellipse being checked.
The circles below,left and right are ignored.
The Cirle 2 is chosen as Square 3 is empty.
This would produce a valid move so the game would continue.
If no valid move is found the game will end.
I not sure how to proceed with this, I was thinking of putting the method call inside a nested for loop to go over every element but I'm thinking it would be very inefficient.
var results = AdjacentElements(grid, i, j);
foreach (var result in results)
//condition here
I don't think i truly understand what you want to do. However, yes you could do nested loops. but sometimes its just easier to poke at it
Given some array x, y
var x = 23;
var y = 3;
Exmaple
var checks = List<Point>();
checks.Add(new Point(x+1,y));
checks.Add(new Point(x-1,y));
checks.Add(new Point(x,y+1));
checks.Add(new Point(x,y-1));
foreach(var check in checks)
//If Bounds check
//do what you need to do

How to cycle through an (n by 12) 2D array

I have a 2-D array (with dimensions magnitudes n by 5), which I'm picturing in my head like this (each box is an element of the array):
(http://tr1.cbsistatic.com/hub/i/2015/05/07/b1ff8c33-f492-11e4-940f-14feb5cc3d2a/12039.jpg)
In this image, n is 3. I.e, n is the number of columns, 5 is the number of rows in my array.
I want to find an efficient way to iterate (i.e walk) through every path that leads from any cell in the left most column, to any cell in right most column, choosing one cell from every column in between.
It cannot be simply solved by n nested loops, because n is only determined at run time.
I think this means recursion is likely the best way forward, but can't picture how to begin theoretically.
Can you offer some advice as to how to cycle through every path. It seems simple enough and I can't tell what I'm doing wrong. Even just a theoretical explanation without any code will be very much appreciated.
I'm coding in C#, Visual Studio in case that helps.
UPDATE:: resolved using code below from http://www.introprogramming.info/english-intro-csharp-book/read-online/chapter-10-recursion/#_Toc362296468
static void NestedLoops(int currentLoop)
{
if (currentLoop == numberOfLoops)
{
return;
}
for (int counter=1; counter<=numberOfIterations; counter++)
{
loops[currentLoop] = counter;
NestedLoops(currentLoop + 1);
}
}
This is a factorial problem and so you might run quite quickly into memory or value limits issues.
Took some code from this SO post by Diego.
class Program
{
static void Main(string[] args)
{
int n = 5;
int r = 5;
var combinations = Math.Pow(r, n);
var list = new List<string>();
for (Int64 i = 1; i < combinations; i++)
{
var s = LongToBase(i);
var fill = n - s.Length;
list.Add(new String('0', fill) + s);
}
// list contains all your paths now
Console.ReadKey();
}
private static readonly char[] BaseChars = "01234".ToCharArray();
public static string LongToBase(long value)
{
long targetBase = BaseChars.Length;
char[] buffer = new char[Math.Max((int)Math.Ceiling(Math.Log(value + 1, targetBase)), 1)];
var i = (long)buffer.Length;
do
{
buffer[--i] = BaseChars[value % targetBase];
value = value / targetBase;
}
while (value > 0);
return new string(buffer);
}
}
list will contain a list of numbers expressed in base 5 which can be used to found out the path. for example "00123" means first cell, then first cell then second cell, then third cell and finall fourth cell.
Resolved:: see the code posted in the edited question above, and the link to a recursion tutorial, where it takes you through using recursion to simulate N nested, iterative loops.

C# Using .Count with Tuple Items

I am trying to compare the contents of a number of Tuples inside of a List. The code runs reasonable well, but on certain occasions, completeList[z].Item1.Count will come back with the wrong number. When I look in the debugger, in one example a = completeList[z].Item1.Count might return 18 when the count is 9. So this causes it to go out of range.
completeList.Count returns correctly, as does completeList[z].Item1[b], so I'm not sure what I could be doing wrong with completeList[z].Item1.Count.
Thanks for any insights!
int x = completeList.Count;
int y = 0;
while (y < x)
{
for (int z = 0; z < x; z++)
{
if (y != z && completeList[y].Item6.Equals(completeList[z].Item6))
{
int a = completeList[z].Item1.Count;
a = a -1;
for (int b = 0; b < a; b++)
{
if (completeList[y].Item1.Contains(completeList[z].Item1[b]))
{
completeList[z].Item1.RemoveAt(b);
completeList[z].Item2.RemoveAt(b);
completeList[z].Item3.RemoveAt(b);
completeList[z].Item4.RemoveAt(b);
completeList[z].Item5.RemoveAt(b);
}
}
}
}
y++;
}
You're removing items from the list. This is changing the count, and also moving around the indexes of the existing items. The count is correct at the start of the loop, but by the time you've actually removed an item, the count is no longer valid.
The most effective solution is to not iterate the list while removing items at all; instead rely on the list's RemoveAll method to remove all of the items that match your list of items to remove. While it's possible for you to manage the indexes properly to remove while iterating, you should avoid this complexity.

Searching a random Array for a specific value

Hi I have been told to "Write code that determines if a value, say “5”, is contained in the array from task 7(a random array) by going backwards through the array starting at the end and comparing the search value with the values in the array. After the search, if the value is found print “The value has been found” otherwise print “The value has not been found”."
I understand the creation of the random array but I am stuck on how to work backwards through it and locate the specific value.
Here is the code so far
class Program
{
static void Main(string[] args)
{
int[] myArray = new int[10];
Random rand = new Random();
for (int i = 0; i < myArray.Length; i++)
{
myArray[i] = rand.Next(19);
}
}
}
}
Use the loop starting from largest to smallest index.
bool found = false;
for (int i = myArray.Length - 1; i >=0 ; i--)
if(myArray[i] == 5)
found = true;
if(found)
{
}
else
{
}
To go backward just use a for loop with the iterator to i--.
for (int i = myArray.Length - 1; i >= 0; i---)
{
if(// Check if myArray[i] equals the value to find)
{
// If it is the case, you can get out from the for loop with break
break;
}
}
The for loop is splitted in 4 parts:
for (initializer; condition; iterator)
body
The initializer is executed before the first iteration in the loop (here you want to start at the last index in the array : myArray.Length - 1)
The condition is evaluated for each iteration, if this condition is true then it goes to 3 (you want to stay in the for loop while i >= 0) otherwise it quit the for loop
The body is executed for each iteration that satisfy the condition
The iterator is executed (here as you want to go backward you want to decrease i)
Then it go back to 2

Categories