Incrementation in loop or if statement - c#

So here's the thing. I have a game in which I have a spawner - to spawn obstacles. And I want after every 5th obstacle to increase the speed. Example: 1st, 2nd, 3rd, 4th, 5th obstacles have default speed = 5 and I want 6th, 7th, 8th, 9th, 10th to have an increased speed to speed = 10 and etc. I already have some nested if statements,but I want to inscrease the speed infitely for example to have speed = 1000 for the 100th obstacle. Here's my code:
IEnumerator SpawnObstacles()
{
int i = 0;
do
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
Destroy(obstacle, 10f);
i++;
if(i >= 10)
{
IncreaseSpeed();
if(i >= 20)
{
IncreaseSpeed();
if(i >= 30)
{
IncreaseSpeed();
}
}
}
}
while (true);
}
private void IncreaseSpeed()
{
Obstacle obs = FindObjectOfType<Obstacle>();
obs.speed += 5f;
}

The modulo operator % helps with things like this. It gives the remainder of the division operation, for example:
3%5 is 3
4%5 is 4
5%5 is 0
6%5 is 1
7%5 is 2
8%5 is 3
9%5 is 4
10%5 is 0
So if your x is starting at 0, and you want to increase speed every 5, you just look at whenever the modulo is 4..
x can go as high as you like (well, int.MaxValue) but the modulo will always be 0..4:
//do some stuff
//should we increase speed?
if(x % 5 == 4)
IncreaseSpeed();
Handy tool for making circular arrays too.. someArray[i % someArray.Length] will never go out of bounds, because the result of a modulo is always between 0 and one less than the length

What you could do instead is either :
Keep track of how many obstacle instances you've spawned so far using a variable(maybe a static variable https://www.c-sharpcorner.com/UploadFile/1ce252/static-variables-and-static-methods-in-C-Sharp/ ) and make sure to simply increase the speed for every 5 instances using modulo %. You can take a look for more information here : https://blog.mattclemente.com/2019/07/12/modulus-operator-modulo-operation.html
A quick introduction to Modulos:
//It essentially shows the rest of a division
5 / 5 == 1
//Since there's no rest there,
5 % 5 == 0
//But in the case of where there is a rest
6 / 5 == 1 (Rest 1)
//Hence
6 % 5 == 1
//Other examples :
7 % 5 == 2
8 % 5 == 3
9 % 5 == 4
10 % 5 == 0 //!! The result is back to zero here as 10/5 == 2 with no Rest
Implement a recursive function that's in the Object that spawns in another object with the same attributes but pass on a parameter which indicates how many instances have been spawned so far and check if it's the 5th instance, if so add speed to it.

You have a Coroutine in an infinite loop anyway ... why not simply do
IEnumerator SpawnObstacles()
{
do
{
for(var x = 0; x < 5; x++)
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
Destroy(obstacle, 10f);
}
// These lines are automatically reached after every 5 iterations
// no need for any módulo or other magic check
Obstacle obs = FindObjectOfType<Obstacle>();
obs.speed += 5f;
}
while (true);
}
Or since I'm not sure of this entire FindObjectWithTag thingy ... actually it seems to me it is the same object you Instantiate right? So why not simply
IEnumerator SpawnObstacles()
{
var speed = 5;
do
{
for(var x = 0; x < 5; x++)
{
var randomTime = Random.Range(minTime, maxTime);
var randomObstacle = Random.Range(0, obstacles.Length);
yield return new WaitForSeconds(randomTime);
var obstacle = Instantiate(obstacles[randomObstacle], transform.position, Quaternion.identity);
obstacle.GetComponent<Obstacle>().speed = speed;
Destroy(obstacle, 10f);
}
// These lines are automatically reached after every 5 iterations
// no need for any módulo or other magic check
speed += 5f;
}
while (true);
}
Though to me it stays a bit unclear how exactly your speed shall grow. From your description it sounds like you want to add 5 after every group of iterations .. however this would mean you have speed = 100 (20*5) for the 100th object .. not speed = 1000 as you wrote

The simplest thing i can think of is setting a counter for the spawned obstacles and incrementing the speed after it reaches 5 and resetting the counter to zero. You don't really need to get into the fancy stuff for something so simple just use an if statement to check the counter.Hope I helped, cheers!

Related

HackerRank Climbing the Leaderboard

This question has to do with this challenge on HackerRank. It seems to be failing some cases, but I'm not clear what's wrong with the algorithm (many people seem to have problem with timeouts, that's not an issue here, everything runs plenty fast, and all the cases that are visible to me pass, so I don't have a specific case that's failing).
The essential outline of how the algorithm works is as follows:
First be sure that Alice isn't already winning over the existing highest score (degenerate case), if she is just tell the world she's #1 from start to finish. Otherwise, at least one score on the leaderboard beats Alice's first try.
Start by walking down the scores list from the highest until we find a place where Alice fits in and record the scores that beat Alice's initial score along the way.
If we reach the end of the scores list before finding a place for Alice's bottom score, pretend there is a score at the bottom of the list which matches Alice's first score (this is just convenient for the main loop and reduces the problem to one where Alice's first score is on the list somewhere)
At this point we have a (sorted) array of scores with their associated ranks, rankAry[r - 1] is the minimum score needed for Alice to attain rank r as of the end of the if clause following the first while loop.
From there, the main algorithm takes over where we walk through Alice's scores and note her rank as we go by comparing against the benchmarks from the scores array that we setup as rankAry earlier. curRank is our candidate rank at each stage which we've definitely achieved by the time this loop starts (by construction).
If we're at rank 1 we will be forever more, so just populate the current rank as 1 and move on.
If we're currently tied with or beating the current benchmark and that's not the end of the line, keep peeking at the next one and if we're also beating that next one, decrease the current benchmark location and iterate
Once this terminates, we've found the one we're going to supplant and we cannot supplant anything further, so assign this rank to this score and repeat until done
As far as I can tell this handles all cases correctly, even if Alice has repeated values or increases between the benchmarks from scores, we should stay at the same rank until we hit the new benchmarks, but the site feedback indicates there must be a bug somewhere.
All the other approaches I've been able to find seem to be some variation on doing a binary search to find the score each time, but I prefer not having to constantly search each time and just use the auxiliary space, so I'm a little stumped on what could be off.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int[] res = new int[alice.Length];
if (scores.Length == 0 || alice[0] >= scores[0]) { //degenerate cases
for (int i = 0; i < alice.Length; ++i) {
res[i] = 1;
}
return res;
}
int[] rankAry = new int[scores.Length + 1];
rankAry[0] = scores[0]; //top score rank
int curPos = 1; //start at the front and move down
int curRank = 1; //initialize
//initialize from the front. This way we can figure out ranks as we go
while (curPos < scores.Length && scores[curPos] > alice[0]) {
if (scores[curPos] < scores[curPos-1]) {
rankAry[curRank] = scores[curPos]; //update the rank break point
curRank++; //moved down in rank
}
curPos++; //move down the array
}
if (curPos == scores.Length) { //smallest score still bigger than Alice's first
rankAry[curRank] = alice[0]; //pretend there was a virtual value at the end
curRank++; //give rank Alice will have for first score when we get there
}
for (int i = 0; i < alice.Length; ++i) {
if (curRank == 1) { //if we're at the top, we're going to stay there
res[i] = 1;
continue;
}
//Non-degenerate cases
while (alice[i] >= rankAry[curRank - 1]) {
if (curRank == 1 || alice[i] < rankAry[curRank - 2]) {
break;
}
curRank--;
}
res[i] = curRank;
}
return res;
}
You have a couple of bugs in your algorithm.
Wrong mapping
Your rankAry must map a rank (your index) to a score. However, with this line rankAry[0] = scores[0];, the highest score is mapped to 0, but the highest possible rank is 1 and not 0. So, change that to:
rankAry[1] = scores[0];
Wrong initial rank
For some reason, your curRank is set to 1 as below:
int curRank = 1; //initialize
However, it's wrong since your alice[0] is less than scores[0] because of the following block running at the beginning of your method:
if (scores.Length == 0 || alice[0] >= scores[0]) { //degenerate cases
for (int i = 0; i < alice.Length; ++i) {
res[i] = 1;
}
return res;
}
So, at best your curRank is 2. Hence, change it to:
int curRank = 2;
Then, you can also remove curRank++ as your curRank has a correct initial value from:
if (curPos == scores.Length) { //smallest score still bigger than Alice's first
rankAry[curRank] = alice[0]; //pretend there was a virtual value at the end
curRank++; // it's not longer needed so remove it
}
Improve "Non-degenerate cases" handling
Your break condition should consider rankAry at curRank - 1 and not curRank - 2 as it's enough to check the adjacent rank value. Also, a value at curRank - 2 will produce wrong results for some input but I won't explain for which cases specifically - I'll leave it up to you to find out.
Fixed Code
So, I fixed your method according to my comment above and it passed it all the tests. Here it is.
static int[] climbingLeaderboard(int[] scores, int[] alice) {
int[] res = new int[alice.Length];
if (scores.Length == 0 || alice[0] >= scores[0]) { //degenerate cases
for (int i = 0; i < alice.Length; ++i) {
res[i] = 1;
}
return res;
}
int[] rankAry = new int[scores.Length + 1];
rankAry[1] = scores[0]; //top score rank
int curPos = 1; //start at the front and move down
int curRank = 2; //initialize
//initialize from the front. This way we can figure out ranks as we go
while (curPos < scores.Length && scores[curPos] > alice[0]) {
if (scores[curPos] < scores[curPos-1]) {
rankAry[curRank] = scores[curPos]; //update the rank break point
curRank++; //moved down in rank
}
curPos++; //move down the array
}
if (curPos == scores.Length) { //smallest score still bigger than Alice's first
rankAry[curRank] = alice[0]; //pretend there was a virtual value at the end
}
for (int i = 0; i < alice.Length; ++i) {
if (curRank == 1) { //if we're at the top, we're going to stay there
res[i] = 1;
continue;
}
//Non-degenerate cases
while (alice[i] >= rankAry[curRank - 1]) {
if (curRank == 1 || alice[i] < rankAry[curRank - 1]) {
break;
}
curRank--;
}
res[i] = curRank;
}
return res;
}

dice roll game stuck in a loop

I am trying to make a program for a game of pig where there user enters a point total to play for and then takes turns with a computer player until one player reaches the point total. For the human turn the player rolls and if they roll 2-6 then they can use r to roll again or h to hold. when hold is selected it adds up the total points and goes to the next turn. If 1 is rolled then they get 0 points for the round and it goes to the computers turn. For some reason when I select h to hold it just keeps going and when it does end the points don't get added up. Not sure how to fix this
any help would be appericated
static int pigRoll()
{
Random random = new Random();
int die1 = 0;
die1 = random.Next(1, 6);
Console.WriteLine($"You rolled {die1}");
return die1;
}
static double humanTurn()
{
double pointTotal = 0;
string gameSelect = null;
var pigDiceRoll = 0;
Console.WriteLine("It's your turn");
do
{
pigDiceRoll = pigRoll();
if (pigDiceRoll != 1)
{
Console.WriteLine("r to roll or h to hold (r/h)");
gameSelect = Console.ReadLine();
pointTotal = pointTotal + pigDiceRoll;
}
else if(pigDiceRoll ==1)
{
pointTotal = 0;
}
} while (gameSelect != "r" || pigDiceRoll != 1);
Console.WriteLine($"Your turn point total is {pointTotal}");
return pointTotal;
}
The while statement should read:
while (gameSelect == "r" && pigDiceRoll != 1)
This translates to keep looping while the user wants to roll again and they didn't roll a 1.
Alternatively you could use:
while (!(gameSelect != "r" || pigDiceRoll == 1))
Both are logically the same, but the first is probably easier to read.

Can you spot the math bug in my tag helper method?

I have made a tag helper to render out a star rating with 0 to 5 stars, including half stars.
This is the tag, rendering five stars:
<star-rating rating="10"></star-rating>
The value for rating can be any whole number from 0 to 10, which is divided in half in the helper method, to account for half stars:
public class StarRatingTagHelper : TagHelper
{
public double? Rating { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagMode = TagMode.StartTagAndEndTag;
output.SuppressOutput();
var sb = new StringBuilder();
if (Rating != null)
{
double stars = (double)Rating/2;
sb.AppendFormat($#"<span title='Rating {stars}/5'>");
for (int s = 0; s < stars; s ++)
{
sb.AppendFormat("<span class='fas fa-star'></span>");
}
double dec = stars - Math.Truncate(stars);
if (dec == 0.5)
{
sb.AppendFormat("<span class='fas fa-star-half'></span>");
}
sb.AppendFormat("</span>");
}
else
{
sb.AppendFormat("No rating");
}
output.PreContent.SetHtmlContent(sb.ToString());
}
}
But my method has a math bug that I can't find. The method produces these outputs ( () = a star, ( = half a star ):
0.5: ()( (one too many)
1.0: () (correct)
1.5: ()()( (one too many)
2.0: ()() (correct)
2.5: ()()()( (one too many)
3.0: ()()() (correct)
3.5: ()()()()( (one too many)
4.0: ()()()() (correct)
4.5: ()()()()()( (one too many)
5.0: ()()()()() (correct)
There is an obvious pattern here. What is the cause?
Update
I got it to work with an ugly workaround:
double dec = stars - Math.Truncate(stars);
if (dec == 0.5)
{
for (int s = 1; s < stars; s++)
{
sb.AppendFormat("<span class='fas fa-star text-warning text-outline'></span>");
}
sb.AppendFormat("<span class='fas fa-star-half'></span>");
}
else
{
for (int s = 0; s < stars; s++)
{
sb.AppendFormat("<span class='fas fa-star'></span>");
}
}
If there is going to be a half star, it runs one for-loop, if not, it runs another, violating the DRY-principle...
In your for loop you are going up to s < stars this will mean for 0.5 it will create a full star, becuase it is greater than 0. You can Floor the stars to omit the decimals. e.g. Math.Floor(1.5) = 1 which is a possible fix, see below.
for (int s = 0; s < Math.Floor(stars); s++)
{
sb.AppendFormat("<span class='fas fa-star'></span>");
}
EDIT:
Math.Truncate() would also work.
Firstly, I would recommend using a decimal type here instead of a double, since you can get rounding errors when doing math (and then doing comparisons on the results) with doubles. You get "lucky" in your case because you're using .5 which doesn't appear to present the issue, but, for example, consider this line of your code:
double dec = stars - Math.Truncate(stars);
If stars was 2.4, then dec would be 0.39999999999999991
If stars was 2.6, then dec would be 0.60000000000000009
Neither case would give the expected output if you then did the comparison: if (dec == .4) or if (dec == .6). So in general, it's a good habit to use decimal if you're going to do math with the numbers and comparisons on the results.
To address the issue in your question, there are a couple of tricks you can use:
Cast the decimal to an int to get the whole number portion
(int) 2.5M == 2
Use the modulus operator (which returns the remainder of dividing one number by another) with 1 to get the decimal portion
2.5M % 1 == 0.5
For example:
// Loop from .5 to 10 in increments of .5 (the 'M' signifies that the number is a decimal)
for (decimal i = .5M; i <= 5; i += .5M)
{
Console.Write($"{i:0.0}: ");
// First write out our "whole" stars
for (int j = 0; j < (int) i; j++)
{
Console.Write("()");
}
// Then, if needed, write out the "half" star
if (i % 1 >= .5M) Console.Write("(");
Console.WriteLine();
}
GetKeyFromUser("\n\nDone! Press any key to exit...");
Output

Average all PingReply RoundTripTime in array C# WPF

I'm working on a new program in C# WPF, and part of it includes averaging ten PingReply.RoundTripTime and dividing it by two to find the one-way trip time. I put all the PingReply instances in an array, arrayReply, and now want to perform the above operations. I've tried a bunch of things, but this is what I have right now:
//New integer replyCount for counting ping replies
int replyCount = 0;
long Atime = 0;
long time = 0;
//for each PingReply instance in arrayReply
foreach (PingReply r in arrayReply)
{
//set long integer (64-bit) Atime (time of ping) to the previous value of Atime
//plus half of the next RoundTripTime divided by 2 (for approx one-way distance)
long oneWayTime = r.RoundtripTime / 2;
long x = Atime + oneWayTime;
//add one to replyCount
replyCount++;
//Divide Atime by number of replies
time = x / replyCount;
}
pingAverage.Text = time.ToString();
However, this results in 0 every time. How can I fix this?
Use LINQ to average:
var time = arrayReply.Average(pr => pr.RoundtripTime) / 2;

Random sprite in Unity

void Start() {
rch = Random.Range(0, 20);
ball = GameObject.FindGameObjectWithTag("Ball");
if (rch % 2 == 0) {
ball.GetComponent<SpriteRenderer>().sprite = s1;
}
else
{
ball.GetComponent<SpriteRenderer>().sprite = s2;
}
Debug.Log(rch);
}
I have the following code which is supposed to select a number between 0 and 20 and if the number is divisible with 2 spawn one spite and otherwise the other.
The code is called every time the object is destroyed and that's why I use the start function. With Debug.Log(rch), I print the random number and even if the number is not divisible by 2, I only get the ball1.

Categories

Resources