Dynamic Array Depth/Width Code - c#

I apologize if this question is too vague because I haven't actually built out any code yet, but my question is about how to code (perhaps in C# in a Unity3d script, but really just generically) the dynamically changing unit depth/width in total war games.
In TW games, you can click and drag to change a unit from an nx2 formation to 2xn formation and anything in between. Here's a video (watch from 15 seconds in to 30 seconds in):
https://www.youtube.com/watch?v=3aGRzy_PzJQ
I'm curious, generically speaking, about the code that would permit someone to on the fly exchange the elements of an array like that. I'm assuming here that the units in the formation are elements in an array
so, you might start with an array like this:
int[,] array = new int[2, 20];
and end up with an array like this:
int[,] array = int[20, 2];
but in between you create the closest approximations, with the last row in some cases being unfilled, and then the elements of that last row would have to center visually until the column width was such that the number of elements in all the rows are equal again.
It kind of reminds me of that common intro to programming problem that requires you to write to the console a pyramid made of *'s all stacked up and adding one element per row with spaces in between, but a lot more complicated.
Most of the lower-tech formation tactics games out there, like Scourge of War just let you choose either Line Formation (2 rows deep) or column formation (2 columns wide), without any in between options, which was perhaps an intentional design choice, but it makes unit movement so awkward that I had to assume they did it out of technical limitations, so maybe this is a hard problem.

I wrote some code to clarify the question a bit. The method form() takes parameters for the number of units in the formation and the width of the formation, without using an array, and just prints out the formation to the console with any extra men put in the last row and centered:
x = number of men
y = width formation
r = number of rows, or depth (calculated from x and y)
L = leftover men in last row if y does not divide evenly into x
Space = number spaces used to center last row
form() = method called from the class Formation
I figured it should take the depth and number of men because the number of men is set (until some die but that's not being simulated here) and the player would normally spread out the men to the desired width, and so the number of rows, and number of men in those rows, including the last row, and the centering of that last row, should be taken care of by the program. At least that's one way to do that.
namespace ConsoleApplication6
{
class Formation
{
public int x;
public int y;
public Formation(int x, int y)
{
this.x = x;
this.y = y;
}
public void form()
{
int r = x / y;
int Left = x % y;
int Space = (y - Left)/2;
for (int i = 0; i < r;i++)
{
for (int j = 0; j < y; j++)
{
Console.Write("A");
}
Console.WriteLine();
if (i == r - 1)
{
for (int m = 0; m < Space; m++)
{
Console.Write(" ");
}
for (int k = 0; k < Left; k++)
{
Console.Write("A");
}
}
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("enter the number of men: ");
int a = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("enter the formation width: ");
int b = Convert.ToInt32(Console.ReadLine());
Formation testudo = new Formation(a,b);
testudo.form();
Console.ReadKey();
}
}
}
So, I think what I'm trying to do to improve this is have the above code run repeatedly in real time as the user inputs a different desired width (y changes) and men die (x changes)
But, if it were actually to be implemented in a game I think it would have to be an array, so I guess the array could take the x parameter as its max index number and then i would do a kind of for loop like for(x in array) or whatever print x instead of just console logging the letter A over and over

Related

C# - How to place a given number of random mines in an array

I'm new to c#. I have a task to make a type of minesweeper, but which immediately opens a solution.
static void Main(string[] args)
{
Console.Write("Enter the width of the field: ");
int q = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the length of the field: ");
int w = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter the number of bombs: ");
int c = Convert.ToInt32(Console.ReadLine());
Random rand = new Random();
var charArray = new char[q, w];
var intArray = new int[q, w];
for (int i = 0; i < q; i++)
{
for (int j = 0; j < w; j++)
{
intArray[i, j] = rand.Next(2);
charArray[i, j] = intArray[i, j] == 0 ? '_' : '*';
Console.Write(charArray[i, j]);
}
Console.WriteLine();
}
}
}
}
Two arrays should be output. Everything should be closed on the first one, that is, there should be only the characters: _ and *
0 - these are places without mines, I replaced them with a symbol _
1 - these are places with mines, I replaced them with an asterisk symbol, but they do not accept the number of mines entered by the user. And it is necessary that there are as many "*" characters as there are mines.
And in the second array there should be an already open solution of the game. That is, the cells next to which there are mines should take a number meaning the number of mines next to this cell.
Please help me..
Compiling the current code
Random random = new Random();
while(c > 0)
{
var rq = random.Next(q);
var rw = random.Next(w);
if(intArray[rq,rw] == 0)
{
intArray[rq, rw] = 1;
c--;
}
}
I would suggest dividing the problem in smaller manageable chunks. For instance, you can place the bombs in a initial step, and on a second step build the solution. You can build the solution at the same time you place the bombs, although for clarity you can do it after.
Naming of variables is also important. If you prefer using single letter variable names, I believe that's fine for the problem limits, however I would use meaningful letters easier to remember. eg: W and H for the width and height of the board, and B for the total number of bombs.
The first part of the problem then can be described as placing B bombs in a WxH board. So instead of having nested for statements that enumerate WxH times, it's better to have a while loop that repeats the bomb placing logic as long as you have remaining bombs.
Once you generate a new random location on the board, you have to check you haven't placed a bomb there already. You can have an auxiliary function HasBomb that checks that:
bool HasBomb(char[,] charArray, int x, int y)
{
return charArray[x,y] == '*';
}
I'll leave error checking out, this function being private can rely on the caller sending valid coordinates.
Then the bomb placing procedure can be something like:
int remainingBombs = B;
while (remainingBombs > 0)
{
int x = rand.Next(W);
int y = rand.Next(H);
if (!HasBomb(charArray, x, y)
{
charArray[x,y] = '*';
remainingBombs--;
}
}
At this point you may figure out another concern. If the number B of bombs to place is larger than the available positions on the board WxH, then you wont be able to place the bombs on the board. You'll have to check for that restriction when requesting the values for W, H and B.
Then in order to create the array with the number of bombs next to each position, you'll need some way to check for all the neighbouring positions to a given one. If the position is in the middle of the board it has 8 neighbour positions, if it's on an edge it has 5, and if it's on a corner it has 3. Having a helper function return all the valid neighbour positions can be handy.
IEnumerable<(int X, int Y)> NeighbourPositions(int x, int y, int W, int H)
{
bool leftEdge = x == 0;
bool topEdge = y == 0;
bool rightEdge = x == W - 1;
bool bottomEdge = y == H - 1;
if (!leftEdge && !topEdge)
yield return (x-1, y-1);
if (!topEdge)
yield return (x, y-1);
if (!rightEdge && !topEdge)
yield return (x+1, y-1);
if (!leftEdge)
yield return (x-1, y);
if (!rightEdge)
yield return (x+1, y);
if (!leftEdge && !bottomEdge)
yield return (x-1, y+1);
if (!bottomEdge)
yield return (x, y+1);
if (!rightEdge && !bottomEdge)
yield return (x+1, y+1)
}
This function uses Iterators and touples. If you feel those concepts are too complex as you said are new to C#, you can make the function return a list with coordinates instead.
Now the only thing left is to iterate over the whole intArray and increment the value on each position for each neighbour bomb you find.
for (int x = 0; x < W; x++)
{
for (int y = 0; y < H; y++)
{
foreach (var n in NeighbourPositions(x, y, W, H))
{
if (HasBomb(charArray, n.X, n.Y))
intArray[x,y]++;
}
}
}
The answers here are mostly about generating random x and random y put in loop and trying to put the mine into empty cells. It is ok solution, but if you think of it, it is not that sufficient. Every time you try to find a new random cell, there is chance that cell is already a mine. This is pretty much alright, if you don't put too many mines into your field, but if you put some greater number of mines, this event can occur quite often. This means that the loop might take longer than usually. Or, theoretically, if you wanted to put 999 mines into 1000 cell field, it would be really hard for the loop to fill all the necessary cells, especially for the last mine. Now, I am not saying that the solutions here are bad, I think, it's really alright solution for many people. But if someone wanted a little bit efficient solution, I have tried to crate it.
Solution
In this solution, you iterate each cell and try to calculate a probability of the mine to be placed there. I have come up with this easy formula, which calculates the probability:
Every time you try to place a mine into one cell, you calculate this formula and compare it to random generated number.
bool isMine = random.NextDouble() < calcProbability();

Battleships game, replace grid with character issues

I'm currently trying to create a battleships console application. I've very new to c# but I'm almost there now there's just issue.
What I'm trying to do is replace a string with H or M when a ship is hit the works fine the only issue I have is when the H or M is inserted it doesn't replace the character in its place and it just moves the characters alone, for example if I have 5 characters in a row it like so: 0 0 0 0 0 it would insert the H or M and show: M0 0 0 0 0
I've been trying all sorts to fix this but as I said my knowledge is very limited.
Ints and Grid creation:
int gridSize = 10;
int BattleShip = 5;
int Destroyer1 = 4;
int Destroyer2 = 4;
int Row;
int Column;
char[,] Vgrid = new char [gridSize, gridSize];
This generates the grid:
for (Row = 0; Row < gridSize; Row++)
{
Console.WriteLine();
Console.Write("{0} | ", GridNumber++);
for (Column = 0; Column < gridSize; Column++)
Console.Write(Vgrid[Column, Row] + "~ ");
}
This code controls the H or M replacing:
if (grid[temp, temp1] == Destroyer1 || grid[temp, temp1] == Destroyer2 || grid[temp, temp1] == BattleShip)
{
Console.WriteLine("HIT!");
Hit++;
Vgrid[temp, temp1] = 'H';
}
else
{
Console.WriteLine("MISS!");
Miss++;
Vgrid[temp, temp1] = 'M';
}
Is there a way I can do this? I just want to be able to replace the character in the grid with the H or M characters.
This grid is actually an overlay as the actual grid is an int and that is where the ships are plotted, this is the only way I thought I can use letters to signify a hit instead of numbers and keep the ships hidden to the players.
Attempting to alter the output of the Console window directly isn't necessarily the best approach for what you're wanting to achieve. Ideally, we should control what we write to the window, rather than try and control the window directly.
A simpler idea to achieve your goal might be to store two grids, one for your ships and another one for your shots. This allows you to separate what you draw to the console window from what you use to check if the player has landed a shot easily, keeping your ships hidden.
Firstly, let's create our setup:
int gridSize = 10;
int BattleShip = 5;
int Destroyer1 = 4;
int Destroyer2 = 4;
int[,] shipGrid = new int[gridSize, gridSize];
char[,] shotGrid = new char[gridSize, gridSize];
The big change is the grids - one for the ships, and another for the shots. We use the same gridSize variable to make sure that the grids are exactly the same size. This is important to make sure that the two grids line up exactly.
Then, we'll set our default values for our shotGrid. We can do this with a simple method that just sets every value to the default, in this case '~':
public void CreateShotGridDefaultValues ()
{
for (int y = 0; y < shotGrid.GetLength(1); y++)
{
for (int x = 0; x < shotGrid.GetLength(0); x++)
{
shotGrid[x, y] = '~';
}
}
}
Next, to check if a player has landed a shot, we use the shipGrid to check if the player's selection relates to a ship, but we update the shotGrid with the information we want to draw to the console window:
public void CheckPlayerShot(int xCoordinate, int yCoordinate)
{
if (shipGrid[xCoordinate, yCoordinate] == Destroyer1 || shipGrid[xCoordinate, yCoordinate] == Destroyer2 || shipGrid[xCoordinate, yCoordinate] == BattleShip)
{
Console.WriteLine("HIT!");
Hit++;
shotGrid[xCoordinate, yCoordinate] = 'H';
}
else
{
Console.WriteLine("MISS!");
Miss++;
shotGrid[xCoordinate, yCoordinate] = 'M';
}
}
Then, we draw out the shotGrid to the console window as follows:
public void DrawGrid()
{
Console.WriteLine();
for (int y = 0; y < shotGrid.GetLength(1); y++)
{
string currentLine = $"{y + 1} | ";
for (int x = 0; x < shotGrid.GetLength(0); x++)
{
char shot = shotGrid[x, y];
currentLine += shot.ToString() + " ";
}
Console.WriteLine(currentLine);
}
Console.WriteLine();
}
This method is a little different to yours, so let me explain a little further. The idea for this method is to build up the information we want to write to the console window one line at a time, rather than draw out every character one at a time. This prevents us from needing to change the console window output directly.
To achieve this, we use two loops, just like you did. The second for() loop iterates through the row's grid cells, and adds them to the currentLine string. Once we've finished a row, we just write out that string to the console window all at once.
With all that in place, you just need call the DrawGrid() method whenever you want to update the grid in the console window. To better understand when and where the best time and place to update the window might be, requires a better understanding of Game Loops. This page should be a terrific start on that path..
Edit: Updated answer to reflect comments.
You could use
Console.SetCursorPosition(Int32, Int32) Method
Sets the position of the cursor.
Or you could just keep everything in a matrix and redraw the screen on change

Print ASCII of a triangle shape with variable input

Struggling with the print. I know it should be two for loops to print out the repeated letters, however, having problems to indent the lines. it should be a simple console C# program to print out the shape like below with a input 3.
XXXXX
XXX
X
With input 4 it should be like
XXXXXXX
XXXXX
XXX
X
Here is my code. Two for loops get the letters correctly but the lines all lined up at the left, not center.
static void Main(string[] args)
{
string num = Console.ReadLine().Trim();
int n = Convert.ToInt32(num);
int k=1;
for(int i = n; i>=1; i--)
{
Console.WriteLine("\n");
Console.WriteLine("".PadLeft(n));
for (int j = (2*i-1); j>=1;j--)
{
Console.Write("0");
}
}
Console.Read();
}
The statement:
Console.WriteLine("".PadLeft(n));
is the right idea but it's not quite there.
In your case, n is the number of lines you wish to print and is invariant. The number of spaces you need at the start of each line should begin at zero and increase by one for each line.
In any case, printing any number of spaces followed by newline (because it's WriteLine) is not what you want, you should be using Write instead.
So the code between int n = Convert.ToInt32(num); and Console.Read(); would be better off as something like:
for (int lineNum = 0; lineNum < n; lineNum++) {
int spaceCount = lineNum;
int xCount = (n - lineNum) * 2 - 1;
Console.Write("".PadLeft(spaceCount));
Console.WriteLine("".PadLeft(xCount, 'X'));
}
You'll notice some other changes there. First, I've used more meaningful variable names, something I'm a bit of a stickler for - using i is okay in certain circumstances but I often find it's more readable to use descriptive names.
I've also used PadLeft to do the X string as well, so as to remove the need for an inner loop.

Sudoku Backtracking - Order of walking through fields by amount of possible values

I've created a backtracking algorithm for solving Sudoku puzzles which basicly walks through all the empty fields from left to right, top down respectively. Now I need to make an extended version in which the order in which the algorithm walks through the fields is defined by the amount of possibilities (calculated once, at initialization) for each of the fields. E.g. empty fields which initially have the fewest amount of possible values should be visited first, and only the initially possible values should be checked (both to reduce the amount of iterations needed). Now I'm not sure how to go on implementing this without increasing the amount of iterations needed to actually define these values for each field and then obtain the next field with the fewest possibilities.
For my backtracking algorithm I have a nextPosition method which determines the next empty field in the sudoku to visit. Right now it looks like this:
protected virtual int[] nextPosition(int[] position)
{
int[] nextPosition = new int[2];
if (position[1] == (n * n) - 1)
{
nextPosition[0] = position[0]+1;
nextPosition[1] = 0;
}
else
{
nextPosition[0] = position[0];
nextPosition[1] = position[1]+1;
}
return nextPosition;
}
So it basicly walks through the sudoku left-right, top-down respectively. Now I need to alter this for my new version to walk through the fields ordered by the fewest amount of possible values for the fields (and only trying the possible values for each field in my backtracking algorithm). I figured I'd try to keep a list of invalid values for each field:
public void getInvalidValues(int x, int y)
{
for (int i = 0; i < n * n; i++)
if (grid[y, i] != 0)
this.invalidValues[y, i].Add(grid[y, i]);
for (int i = 0; i < n * n; i++)
if (grid[i, x] == 0)
this.invalidValues[i, x].Add(grid[i, x]);
int nX = (int)Math.Floor((double)x / n);
int nY = (int)Math.Floor((double)y / n);
for (int x = 0; x < n; x++)
for (int y = 0; y < n; y++)
if (grid[nY * n + y, nX * n + x] != 0)
this.invalidValues[y, x].Add(grid[y, x]);
}
Calling this method for every empty field in the sudoku (represented in this.grid as 2D array [nn,nn]). However this causes even more iterations since in order to determine the amount of different invalid values for each field it'll have to walk through each list again.
So my question is whether someone knows a way to efficiently walk through the fields of the sudoku ordered by the amount of possible values for each field (at the same time keeping track of these possible values for each field since they are needed for the backtracking algorithm). If anyone could help me out on this it'd be much appreciated.
Thanks in advance!

Add consecutive numbers to a limit, display how many iterations

I'm learning C# by myself by book and would appreciate some help. I want to create a console program that adds consecutive numbers together until the total reaches a limit defined by the user. The program will then count and display how many iterations were performed.
This is the exercise as stated in the book:
In birthday cakes, people used to put as many candles as the number of years to celebrate. Assume that candles are sold in boxes of x pieces. Now suppose that a newborn child received his first box of birthday candles. Write a program that tells you after how many birthdays one needs to buy new box of candles
Edit: it works now - new code below.
using System;
class Program
{
static void Main(string[] args)
{
int x, y = 0, z = 0, a = 0;
Console.WriteLine("This program will calculate when you have to buy a new box of candles.");
Console.WriteLine("Enter the number of candles the box contains: ");
x = Convert.ToInt32(Console.ReadLine());
while (x > 0)
{
z = z + 1;
a = a + z;
y = x - a;
if (y <= z)
{
break;
}
}
Console.WriteLine("After {0} years you have to buy a new box of candles.", z);
Console.ReadLine();
}
}
This should solve your problem:
For your example with 20 candles, count_birthdays would be 6 after the loop, because you need 1+2+3+4+5 = 15 candles for the first 5 birthdays, so you won't have left over enough for the sixth.
int count_birthdays = 0;
int used_candles = 0;
while (used_candles <= candles)
{
count_birthdays++;
used_candles = used_candles + count_birthdays;
}
A hint would be that there is a 'break' keyword that will let you exit a loop.
Also, when you get to the 'while' loop, you may find this is a better way to express your solution. For now, look up 'break'

Categories