I have this problem with my code I cant fix.
I am making a quiz, but the questions mustnt be asked twice so I avoided that, but when it runs out of options it crashes.
It also could be the code after it, but I dont think so.
Here is my code:
private static void chooseQuestion()
{
Random randomQuestion = new Random();
int returnValue = randomQuestion.Next(1, 3);
switch (returnValue)
{
case 1:
if (randomValues.questionOneChosen != 1)
{
questionOne();
}else
{
chooseQuestion();
}
break;
case 2:
if (randomValues.questionTwoChosen != 1)
{
questionTwo();
}else
{
chooseQuestion();
}
break;
}
endQuiz();
}
and here is what is after it:
private static void endQuiz()
{
Console.Clear();
Console.WriteLine();
text.centered("You completed the QUIZ, well done!");
Console.WriteLine();
text.centeredWrite("Press ENTER to go back to the menu");
string input = Console.ReadLine();
if (input == "")
{
Menu.main();
}else
{
endQuiz();
}
}
If you need more code to help me, please ask me.
Thanks in advance!
A StackOverflowException on StackOverflow, nice!
Does Menu.main() end up calling chooseQuestion()?
When a function is called, a new entry on the stack is created with room for all its local variables and the address to return to when it returns. If a function calls itself (recursive call), its original stack entry is not released. If it calls itself often enough without returning, the stack will eventually run out of room and give this exception. Having said that, the stack is pretty big - I think the default is 1 megabyte - so you need a lot of recursive calls to exhaust it.
You've got two functions which both call themselves. If they do it often enough, you'll run out of stack space. Just replace the recursive calls with a loop. Here's your first function:
private static void chooseQuestion()
{
bool endQuizChosen = false;
while ( !endQuizChosen ) {
Random randomQuestion = new Random();
int returnValue = randomQuestion.Next(1, 3);
// ... the rest of the function ...
endQuizChosen = endQuiz();
}
}
and edit endQuiz() to not call itself but to return true if user wants to stop and false if they want to go on.
Your chooseQuestion method's logic goes like this:
a. Pick a 1 or 2, randomly
b. If 1, and I haven't asked question 1 yet, show question 1
c. if 1, and I have asked question 1, go to step (a)
d. repeat (b) and (c) for question 2
...
once you answer both questions, you try to get another random question. But there are no more questions available. So you recurse into an exception.
Don't write your logic this way. Use a collection, and remove the item from the collection after you've shown that question. When the collection is empty, you're done.
This is never getting set.
randomValues.questionOneChosen
So whatever it is, if it's not 1 or 2 the method is going to call itself again. It's probably always zero.
Related
I have been working on a text adventure for a while, but I am having an issue. I have a tutorial, but I want to know how when the user types something that isn't listed, instead of asking the question again, just displaying the option. My example here is a tutorial option that isn't available yet, so they have to type back. I want it so if they type anything other then back, it just displays [Back] and not "That option isn't listed.". In the code example I have tried to use more than one static void because maybe I couldn't reference my own void but that wouldn't work, And I separated the writing and the back option into two but it still doesn't work. When the code example has SW(60, "text") that is just a custom thing for slow write and the 60 is 60 millisecond delay. So, whenever I run it, and type something other then back it will say "That option isn't listed.", like planed but then somehow clear the line and tell you the settings are disabled.
Here is my code.
'''
static string TutorialBack;
public static void TutorialLine()
{
Console.Clear();
SW(40, "The tutorial is currently being devolped, thank you for your patience and support.");
Game.TutorialOof();
}
public static void TutorialOof()
{
Game.Tutorial();
}
static void Tutorial()
{
Thread.Sleep(1600);
Console.WriteLine();
SW(40, "[Back]");
Console.WriteLine();
TutorialBack = Console.ReadLine();
if (string.Equals(TutorialBack, "back", StringComparison.CurrentCultureIgnoreCase))
{
Console.Clear();
Game.Menue();
}
else
{
SW(60, "That option isn't listed.");
Game.TutorialOof();
}
Thanks for any help.
Text adventures are still alive? ;)
I think you should use the var to check equals not the type (string).
// ...
if (TutorialBack.Equals("Back", StringComparison.CurrentCultureIgnoreCase))
{
Console.Clear();
Game.Menue();
}
// ...
Edit: Sorry if I misunderstood what you mean. If you want keep the current just quote out the instruction inside else.
else
{
//SW(60, "That option isn't listed.");
//Game.TutorialOof();
}
Or
else
{
//SW(60, "That option isn't listed.");
Game.TutorialOof();
}
I'm modifying existing C# code in order to pilote a piston. Every 30ms, I have a direct feedback of the position of this piston, through an event. The value is stored in a global variable I use to get the current position of the piston.
What I'm trying to achieve: for a given distance input (A->C), I want the piston to travel at full speed for 95% of the distance (A->B), and then slower for the remaining 5% (B->C).
I have access to a command that defines the speed and the destination of the piston : pos(velocity, destination).
However, if I write that code:
pos(fullSpeed,B);
pos(reducedSpeed, C);
the piston directly goes from fullSpeed to reducedSpeed
I tried to use a while loop to compare the current position of the piston with the goal destination, however, upon entering the while loop, the variable storing the piston position does not update anymore.
However, I noticed that by throwing a MessageBox in between, the position value keeps on getting updated, and I can simply click "ok" to launch the second command.
pos(fullSpeed,B);
MessageBox.show("Wait");
pos(reducedSpeed, C);
I would like to know why the "while" loop stops the update of the position variable but the MessageBox does not. I mean, as long as I don't click the "ok" button, the box is here preventing me from doing anything, which for me ressembles a while loop behaviour. Is there another way for me to do this instead of the MessageBox ?
I have little to no knowledge when it comes to C# and no support. I have tried to look in the documentation, but I did not find an answer (I have probably missed it). Any lead is more than welcome.
EDIT: I have no documentation for that code, and it is barely commented. Here is what I gathered (really hope it helps):
To move the piston, taht function is called:
MyEdc.Move.Pos(control, speed, destination, ref MyTan);
control simply define what we pilote (a distance or a load, it is an enum), and I have no idea what MyTan does. Only thing I know is that the MyEdc.Move.Pos returns an error code.
If I look at the definition of "pos", I am redirected to class
public DoPEmove Move;
containing among other things:
public DoPE.ERR Pos(DoPE.CTRL MoveCtrl, double Speed, double Destination, ref short Tan);
DoPE.ERR is also an type enum. However, I cannot reach the definition of a function named "Pos". Coud it be within the .dll included ?
The following is the code that allows me to access the position of the piston (without the global variables):
private int OnData(ref DoPE.OnData Data, object Parameter)
{
if (Data.DoPError == DoPE.ERR.NOERROR)
{
DoPE.Data Sample = Data.Data;
Int32 Time = Environment.TickCount;
if ((Time - LastTime) >= 300 /*ms*/)
{
LastTime = Time;
string text;
text = String.Format("{0}", Sample.Time.ToString("0.000"));
guiTime.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S].ToString("0.000"));
guiPosition.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F].ToString("0.000"));
guiLoad.Text = text;
text = String.Format("{0}", Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E].ToString("0.000"));
guiExtension.Text = text;
}
}
return 0;
}
Which is called using
MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData);
I realise how little I know on how the soft operates, and how frustrating this is for you. If you think this is a lost cause, no problem, I'll try Timothy Jannace solution, and if it does not help me, I'll stick with the MessageBox solution. I just wanted to know why the MessageBox allowed me to sort of achieve my objectif, but the while loop did not, and how to use it in my advantage here.
I tried to use a while loop to compare the current position of the
piston with the goal destination, however, upon entering the while
loop, the variable storing the piston position does not update
anymore.
While you are in the while loop, your app can no longer receive and process the feedback event.
One possible solution would be to use async/await like this:
private const int fullSpeed = 1;
private const int reducedSpeed = 2;
private int currentPistonPositon = 0; // global var updated by event as you described
private async void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
pos(fullSpeed, B);
await Task.Run(() =>
{ // pick one below?
// assumes that "B" and "currentPistonPosition" can actually be EXACTLY the same value
while (currentPistonPositon != B)
{
System.Threading.Thread.Sleep(25);
}
// if this isn't the case, then perhaps when it reaches a certain threshold distance?
while (Math.Abs(currentPistonPositon - B) > 0.10)
{
System.Threading.Thread.Sleep(25);
}
});
pos(reducedSpeed, C);
}
Note the button1_Click method signature has been marked with async. The code will wait for the while loop inside the task to complete while still processing event messages because of the await. Only then will it move on to the second pos() call.
Thank you for your answer ! It works like a charm ! (good catch on the
EXACT value). I learnt a lot, and I am sure the async/await combo is
going to be very usefull in the future ! – MaximeS
If that worked well, then you might want to consider refactoring the code and making your own "goto position" method like this:
private void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
GotoPosition(fullSpeed, B);
GotoPosition(reducedSpeed, C);
}
private async void GotoPosition(int speed, int position)
{
pos(speed, position);
await Task.Run(() =>
{
while (Math.Abs(currentPistonPositon - position) > 0.10)
{
System.Threading.Thread.Sleep(25);
}
});
}
Readability would be greatly improved.
You could even get fancier and introduce a timeout concept into the while loop. Now your code could do something like below:
private void button1_Click(object sender, EventArgs e)
{
int B = 50;
int C = 75;
if (GotoPosition(fullSpeed, B, TimeSpan.FromMilliseconds(750)).Result)
{
if (GotoPosition(reducedSpeed, C, TimeSpan.FromMilliseconds(1500)).Result)
{
// ... we successfully went to B at fullSpeed, then to C at reducedSpeed ...
}
else
{
MessageBox.Show("Piston Timed Out");
}
}
else
{
MessageBox.Show("Piston Timed Out");
}
}
private async Task<bool> GotoPosition(int speed, int position, TimeSpan timeOut)
{
pos(speed, position); // call the async API
// wait for the position to be reached, or the timeout to occur
bool success = true; // assume we have succeeded until proven otherwise
DateTime dt = DateTime.Now.Add(timeOut); // set our timeout DateTime in the future
await Task.Run(() =>
{
System.Threading.Thread.Sleep(50); // give the piston a chance to update maybe once before checking?
while (Math.Abs(currentPistonPositon - position) > 0.10) // see if the piston has reached our target position
{
if (DateTime.Now > dt) // did we move past our timeout DateTime?
{
success = false;
break;
}
System.Threading.Thread.Sleep(25); // very small sleep to reduce CPU usage
}
});
return success;
}
If you're using events you are probably having concurrency issues. Especially with events being raised every 30ms!
A very simple way to handle concurrency is to use a lock object to prevent different threads from using contested resources simultaneously:
class MyEventHandler
{
private object _lockObject;
MyEventHandler()
{
_lockObject = new object();
}
public int MyContestedResource { get; }
public void HandleEvent( object sender, MyEvent event )
{
lock ( _lockObject )
{
// do stuff with event here
MyContestedResource++;
}
}
}
Keep in mind that is very simple and by no means perfect in every scenario. If you provide more information about how the events are raised and what you're doing with them people will be able to provide more help.
EDIT:
Using that signature you posted for the Pos method I was able to find documentation on the library you are using: https://www.academia.edu/24938060/Do_PE
The reason you only see the method signature when you goto definition is because the library has been compiled into a dll. Actually, it probably wouldn't be that useful to see the code anyway because it looks like the library is a C# wrapper around native (c or c++) code.
Anyways, I hope the documentation is helpful to you. If you look at page 20 there are some pointers on doing movement. This is going to be a challenge for a new programmer but you can do it. I would suggest you avoid using the event handler to drive your logic and instead stick with using the synchronous versions of commands. Using the synchronous commands your code should operate the same way it reads.
I believe what you'll want to do is add a call to:
Application.DoEvents();
This will allow your application to process posted messages (events), which will allow that global variable to be updated.
I just wanted to know why the MessageBox allowed me to sort of achieve my objectif, but the while loop did not, and how to use it in my advantage here.
The reason that works is because you're giving the WndProc a chance to process events which have been sent to the application. It's not an intended feature of that call to MessageBox.Show();, but it is a consequence. You can do the same thing with a call to Application.DoEvents(); without the interruption of the message box.
This question already has answers here:
Option yes/no C# Console
(7 answers)
Closed 3 years ago.
I am learning about C # programming and I stumble on a part of my code. I use visual studio 2017.
I develop a mystery number game and I would like to make sure to replay but I do not know how to do it.
I would like that when we press O or N it plays again or else it quits, but I can not. Scanf and printf do not work. Thank you for your next answer.
char play;
bool isvalid = true;
void Number()
{
//Code...
While(isvalid);
{
Console.WriteLine("want to play again o/n ");
if(play == 'o')
{
Number();
}
else
{
play == 'n';
}
}
}
Number();
I see couple of issues in the pasted code:
First of all, you there is a semicolon after the while loop. Because of it, the lines in the curly brackets are not getting executed again and again as you expect them to.
Second - you are not populating play variable. You should use Console.Readline (you can enter any string and then hit enter to come out of input prompt) OR Console.Read (to read just 1 char)
Third - You are calling Number function again and again - if you play your game for sufficiently long time it would cause stack overflow exception.
What I understood from your code is you want to execute some code again and again if user keeps on entering 'o' character. Otherwise you want to stop that loop.
The code would be :
void Number()
{
//Code...
While(isvalid);
{
Console.WriteLine("want to play again o/n ");
play = Console.Read();
if(!string.IsNullOrEmpty(play) && play.Equals('o', CultureInfo.InvariantCulture)
{
/////// Some code you want to execute;
}
else
{
////// Some code you want to execute if user enters anything other than 'o'.
////// If you want to stop loop, set isValid = false; OR use break statement
}
}
}
I know that this answer may not solve your problem completely.
Hope this helps you to understand what code you should write.
I am making a music maker program in C# (visual studio).
Here is my code:
int accCount = 0;
enum accidental { flat, sharp, none }
accidental thisAcc = accidental.none;
if (keyComboBox.SelectedItem.ToString().Length < 8)
{
MessageBox.Show("Please select a key!");
}
else switch (keyComboBox.SelectedItem.ToString())
{
case "C major - A minor":
accCount = 0; thisAcc = accidental.none;
break;
case "G major - E minor":
accCount = 1; thisAcc = accidental.sharp;
break;
...etc..
}
and so on...This all is included in postButton_click(postButton_Click(object sender, EventArgs e)
But when I click the button, an exception is shown (An unhandled exception of type 'System.StackOverflowException' occurred in Program.exe)And if I select "break", this line is selected:
object key(int count, accidental ac) (here is the cursor){
return key(0, accidental.none);
}
Does anyone know what is wrong?Sorry if this question is not specific enough, just tell me.
Well yes, look at this code (reformatted from your question for readability):
object key(int count, accidental ac)
{
return key(0, accidental.none);
}
That will just invoke the same method... which will invoke the same method... which will invoke the same method etc, until it runs out of stack space.
It's not clear what you intended to return from this method, but you need to stop recursing in this infinite way.
A stackoverflow means that you have some unbound recursion in your application. In English this essentially means you're calling a method again and again.
Can you see how you're calling your method key within itself?
Ok, so I have a program that checks a twitch url for whenever someone new follows the channel by comparing a certain string is different from a "temp" string that I use for reference. But instead of only outputting a message every time the string is different it gets stuck in a loop of outputting the latest follower and then second latest follower then latest follower again etc.
What am I missing? Also, is there a better way of checking if a certain string is updated?
private void DonationListen()
{
try
{
followers = this.donationClient.DownloadString("https://api.twitch.tv/kraken/channels/" + channel.Trim() + "/follows");
donationTimer.Interval = 10000;
donationTimer.Elapsed += new ElapsedEventHandler(CheckUpdates);
donationTimer.Start();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private void CheckUpdates(object source, ElapsedEventArgs e)
{
donationTimer.Stop();
int startIndex = followers.IndexOf("display_name\":") + 15;
int endIndex = followers.IndexOf(",\"logo", startIndex);
prevFollower = followers.Substring(startIndex, (endIndex - 1) - startIndex);
if (firstRun == true)
{
temp = prevFollower;
}
else if (prevFollower != temp)
{
//New follower detected
temp = prevFollower;
if (updateFollower != null)
{
updateFollower(prevFollower);
}
}
else
{
//Follower is the same as before
}
firstRun = false;
DonationListen();
}
I'm thinking it might have something to do with the downloadstring trying to get a new string from the url but failing since it's currently being updated and therefore the CheckUpdates doesn't have correct information or something?
Without a good code example, it is difficult to know for sure what the problem is. So we are left inspecting the code you did show us.
Based on that, it appears to me as though your "loop" is being caused by repeatedly subscribing to the same event.
In your DonationListen() method, you have this statement:
donationTimer.Elapsed += new ElapsedEventHandler(CheckUpdates);
In the CheckUpdates() method (i.e. the handler you are subscribing), you have this statement (as the very last statement):
DonationListen();
In other words, every time the timer's Elapsed event is raised, you add another event handler instance to the event. For every handler you add, the CheckUpdates() method will be called.
Again, without a good code example, it is difficult to know for sure what the best fix would be. But given the code that is here, it appears to me that you could just remove that last statement from the CheckUpdates() method, as the DonationListen() method does not appear to do anything that needs doing again.