Problem
I’m trying to dive deeper into C#, and so I’ve been experimenting with some code. I’m creating a console application that shows the path of a projectile depending on the angle, and it works great! You can check it out here actually. It’s not perfect, but I like it.
Is there a better way to use my for loops? I’m using 3 inside eachother, and I’m not sure if that should be an issue. If you could take a look at the code in the link, it’d be great.
I realized my code might not have shown up. The code snippet won’t work if you try to do it as you’re missing a couple functions I’ve devised, but you should be able to get the idea just by looking at it.
using System.IO;
using System;
using System.Threading;
class Program
{
static void Main()
{
Console.Clear();
DoArc();
}
static void DoArc() {
int tempVal = 0;
string displayLine = "";
//HERE
for(int a = 0; a < 90; a+= 2) {
Console.Clear();
//HERE
for(int y = 0; y < 16; y++) {
//HERE
for(int x = 0; x < 40; x++) {
tempVal = Mathf.RoundToInt(Physics.HeightAtPoint(x, a, 15f, 0));
tempVal = Mathf.Clamp(tempVal, 0, 16);
if(Mathf.CloseTo(16 - y, tempVal, 1))
displayLine += " X";
else
displayLine += " .";
}
Console.WriteLine(displayLine);
displayLine = "";
}
Console.WriteLine("Angle: " + a);
Thread.Sleep(500);
}
}
}
Solution
I don’t see anything particularly wrong with the loops themselves, but, as far as coding style goes:
tempVal
anddisplayLine
should be declared as close as possible to the place where they’re first used.- the values returned by
Math.RoundToInt
andMathf.Clamp
don’t seem to have anything in common, so they should be assigned to different variables. Don’t reusetempVal
for different and unrelated things. tempVal
,x
,y
anda
should be given more meaningful names.- You should use a
StringBuilder
to build strings, instead of using string concatenation. - Your indentation is all over the place.
-
In C#, it is common practice to put the opening bracket
{
on its own line – although one could argue that this is a matter of personal preference and doesn’t really matter as long as you’re consistent throughout the codebase.for(int y = 0; y < 16; y++) { var sb = new StringBuilder(); //HERE for(int x = 0; x < 40; x++) { int height = Mathf.RoundToInt(Physics.HeightAtPoint(x, a, 15f, 0)); int clamp = Mathf.Clamp(height, 0, 16); if(Mathf.CloseTo(16 - y, clamp, 1)) sb.Append(" X"); else sb.Append(" ."); } Console.WriteLine(sb.ToString()); }
Adding to the answers already provided I’d like to say that it’s a bad practice to use all kinds of so called magic numbers/strings like 90, 14, 16… give them a meaning
var rightAngle = 90;
var maxHeight = 40;
for(int x = 0; x < maxHeight; x++)
{
}
or whatever the numbers represent.
Since your output is calculated directly from the values in your for statements, I would say that this is fine.
If you were using the for loops to iterate over collections, then you would have different options.
You could take a different approach by populating collections with numeric values using Enumerable.Range, projecting the results of your computations to a new collection (like a two-dimensional array of bools where true indicates the projectile is present at that position), but it’s probably only worth attempting as an academic exercise.
If you went a functional route, you could completely remove your dependence on collections by returning a function to print each row. However, if you wanted to not call any console methods from inside your functions you would have to represent the row some way (either a collection or perhaps a Tuple containing 2 values, length and position). I should note that C# is probably not the best fit for this option.