How to put User Input to end the loop on c# - c#

Im new to programming in C# and i encountered some problems, I am trying to create a countdown Console Command but im stuck on how to terminate the loop using a user input (in this case, when the user presses the "Enter" Button)
Here is the code that I currently have
using System;
using System.Threading;
class stopWatch {
public static void Main (string[] args) {
Console.WriteLine ("Access The Clock? Y/N");
string yN = Console.ReadLine();
if ((yN == "y") || (yN == "Y")) {
Console.WriteLine ("Timer (T) or Stopwatch (S)?");
var sT = Console.ReadLine();
if ((sT == "s") || (sT == "S")) {
Console.WriteLine ("Press the 'Enter' Button to Start");
Console.ReadLine();
Console.WriteLine("Stopwatch Started");
Console.WriteLine("Press the 'Enter' Button again to Stop");
for(int i = 0; i >= 0; i ++) {
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
}
else if ((yN =="n") || (yN == "N")) {
Console.WriteLine ("Alright, Thank You");
Environment.Exit(0);
}
else {
Console.WriteLine ("Wrong Input");
Environment.Exit (0);
}
}
}
keep in mind im very new to c# and loops, i'm getting a hard time translating and trying on everything, it'd be really grateful of me to get an extra explanation for your answers

You need to use separate thread for that. Threads is advanced theme, but the basic goal is to do several things at once (in your example run stopwatch and wait for user input). Here is full working code based on your sample:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp13
{
class Program
{
public static void Main(string[] args)
{
Console.WriteLine("Access The Clock? Y/N");
string yN = Console.ReadLine().ToLower();
if (yN == "y")
{
Console.WriteLine("Timer (T) or Stopwatch (S)?");
var sT = Console.ReadLine().ToLower();
if (sT == "s")
{
Console.WriteLine("Press the 'Enter' Button to Start");
Console.ReadLine();
bool runStopWatch = true;
Task.Run(() =>
{
for (int i = 0;; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000);
if (!runStopWatch)
{
break;
}
}
});
Console.WriteLine("Stopwatch Started");
Console.WriteLine("Press the 'Enter' Button again to Stop");
Console.ReadLine();
runStopWatch = false;
}
}
else if (yN == "n")
{
Console.WriteLine("Alright, Thank You");
Environment.Exit(0);
}
else
{
Console.WriteLine("Wrong Input");
Environment.Exit(0);
}
}
}
}
Task.Run starts a new thread, so your stopwatch runs independent of the main thread (which waits for user to press Enter). Variable runStopWatch is shared between threads (don't think about it right now), so when main thread sets it to false, stopwatch's thread sees it and terminated the loop.
Side mote. More proper way to do this is to use CancellationToken istead of bool variable. But again. don't worry about it right now.

Since you're new im going to try to explain it as simple as possible. The solution you're trying to create is a solution to a multi-threaded problem. This means you will have to do an action while another action takes place. This can be resolved using async methods, this might be too advanced for now, so I will suggest reading up on loops and then multi-threaded programming first.
But for an answer i will have to refer to this post here on how to fix this particular problem. (Look at the second part of the accepted answer).

Related

How to ask for a yes/no answer within a loop?

I'm trying to write a simple console game application & I'm running into some idk moments. Okay, so in the code I'm trying to get a Y/N input and have start the game over if Y, or Exit if N. I'd love to hear some various solutions & there's bonus <3 for a coding sample that assigns random door number every time the game is started or played.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Threading;
namespace Desicions
// Typo I know.
{
class Program
{
static void Main(string[] args)
{
bool WrongInput = true;
while (WrongInput)
{
Console.Clear();
Console.WriteLine("Would you perfer what's behind door 1, 2, or 3?");
string userValue = Console.ReadLine();
if (userValue == "1")
{
Console.WriteLine("You won a new car!");
WrongInput = false;
}
else if (userValue == "2")
{
Console.WriteLine("You won a boat!");
WrongInput = false;
}
else if (userValue == "3")
{
Console.WriteLine("Aww, you did not win this time please play again.");
WrongInput = false;
}
else
{
Console.WriteLine("Please choose door number 1, 2, or 3. Press Enter to return to game.");
Console.ReadLine();
}
}
Thread.Sleep(2000);
Console.WriteLine();
Console.WriteLine("Would you like to play again? (Y/N)");
while ((Console.ReadLine() == "Y") || (Console.ReadLine() == "y"))
//Should I use this instead?
//while String.Equals(Console.ReadLine(), "y", StringComparison.CurrentCultureIgnoreCase);
//if so, why?
{
//What goes here for (Y)/(y) input to return into top of while (WrongInput) loop?
Console.WriteLine("Currently in Dev [:");
}
while ((Console.ReadLine() == "N") || (Console.ReadLine() == "n"))
{
//I would like for a (N)/(n) input to WriteLine("Exiting game..."); Thread.Sleep(2000); then exit. Solutions plz por favor?
}
//BONUS c# <3 for code sample for setting doors numbers randomly!
Console.ReadLine();
}
}
}
Here's a somewhat robust approach to tackle this problem:
using System;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Choose 1, 2, 3 or X to exit.");
// read input
var s = Console.ReadLine();
// shall we exit ?
if (s != null && s.Trim().Equals("X", StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine("Bye bye !");
break;
}
// user picked an activity
int result;
if (!int.TryParse(s, out result)) continue; // failed to read input
// at this point, do something interesting
Console.WriteLine("You selected : " + result);
}
}
}
}
As you'll see,
it won't matter whether user typed in upper case or lower case, with leading/trailing spaces -> the approach handles that
you will compare against a number the user has choosen VS a string (better)
etc ...
Go on and improve on that :)
EDIT : choose a random door
const int maxDoors = 3;
var doors = new Dictionary<int, string>
{
{0, "Door 1"},
{1, "Door 2"},
{2, "Door 3"}
};
var random = new Random();
while (true)
{
// integrate this inside the previous example loop
var door = random.Next(maxDoors);
var doorName = doors[door];
Console.WriteLine("You have chosen the door: " + doorName);
}
You would benefit greatly from simply doing Console.ReadLine().ToLower() (Or ToUpper()), to ignore any case issues.
As for what you currently have, by saying (Console.ReadLine() == "foo" || Console.ReadLine() == "bar"), you are calling the command to take in user input twice, meaning the user will need to input whether they'd like to play again twice before you break into while loop.
As for your random door number, check out System.Random, and assign it to a variable at the start of your game.
https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx
Also, rather than calling an empty Console.WriteLine();, simply add in "\n" to your string to create a newline.
And finally, it's "prefer," not "perfer."
I saw this on the top questions, and I may be stepping over my own limit. Currently learning Java. Just trying to help, sorry If I am of no use.
But couldn't you use a do while loop and towards the end ask for input from the user. Perform the check at the end with the do while loop, if Y/yes continue if N/no don't?
chicken scratch:
do
{
//Game code
//ask for input
} while input == Y | input == y

How do I nullify (or assign another value to) a System.ConsoleKey value once it's finished being used?

I have a repeating code here, full of goto statements that make this while loop well... Repeat forever.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test
{
class Program
{
static void Main(string[] args)
{
main();
}
public static ConsoleKeyInfo keyPressed;
private static void main()
{
start:
keyPressed = Console.ReadKey();
while (true)
{
loopstart:
if (keyPressed.Key == ConsoleKey.Enter)
{
Console.WriteLine("You pressed the Enter Key!");
goto loopstart;
}
if (keyPressed.Key == ConsoleKey.Escape)
{
Console.WriteLine("You pressed the Escape Key!");
goto loopstart;
}
if (keyPressed.Key == ConsoleKey.Spacebar)
{
Console.WriteLine("You pressed the Spacebar!");
goto loopstart;
}
else
{
break;
}
}
Console.WriteLine("You broke the loop!");
goto start;
}
}
}
Without removing any code, is it possible to change the value of keyPressed.Key or keyPressed itself to NULL; the state it was when it was declared, or to any other value/key that's not the spacebar, enter or escape key?
Of course, the problem could be solved by removing all the goto loopstart; in the code, but that's against the point of the question.
What I want to do is make the keyPressed.Key value NULL (or any other value) so that all the IF statements will result in false, which therefore means not running the goto loopstart code.
The problem now is that when I try to nullify it with a simple keyPressed = null;, it comes with the error of:
Cannot convert null to 'System.ConsoleKeyInfo' because it is a non-nullable value type.
Is there any way I can nullify (or change the value to something else) so that I can break the loop?
(As in: Make the IF statement get to the point where it has to run the else code)
It should look something like:
...
{
loopstart:
if (keyPressed.Key == ConsoleKey.Enter)
{
Console.WriteLine("You pressed the Enter Key!");
// keyPressed = null; <-- Does not work.
// Do something to make ConsoleKey.Key to equal something else.
goto loopstart;
}
if (keyPressed.Key == ConsoleKey.Escape)
{
Console.WriteLine("You pressed the Escape Key!");
...
Obviously with the // Do something to make ConsoleKey.Key to equal something else. replaced with working code?
If this works, the first time the loop runs (presuming the key pressed at the start is either the Spacebar, Escape or Enter keys) would result with the goto loopstart; being used, and the second time round would skip through to the goto start; where it'll ask for another key.
And then the process repeats at the speed of which the user gives an input, rather than repeating with the same key without stop, or asking for another key.
Basically: Make the loop run the IF statement as a proper IF statement instead of a FOR loop.
See also
Why use goto-statement, it's very outdated constructure. You can easily continue the loop. And else check is also redundant. You can simply read the key from Console before check, like this:
while (true)
{
keyPressed = Console.ReadKey();
switch (keyPressed.Key)
{
case ConsoleKey.Enter:
Console.WriteLine("You pressed the Enter Key!");
continue;
case ConsoleKey.Escape:
Console.WriteLine("You pressed the Escape Key!");
continue;
case ConsoleKey.Spacebar:
Console.WriteLine("You pressed the Spacebar!");
continue;
}
// should be outside the switch for breaking the loop
break;
}
If you want to clear keyPressed, use default construction, like this:
keyPressed = default(ConsoleKeyInfo);
But why do you want to do this? Garbage Collection will clear the memory by itself, you should not go into there.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace test
{
class Program
{
static void Main(string[] args)
{
main();
}
public static ConsoleKeyInfo keyPressed;
private static void main()
{
start:
keyPressed = Console.ReadKey();
while (true)
{
loopstart:
if (keyPressed.Key == ConsoleKey.Enter)
{
Console.WriteLine("You pressed the Enter Key!");
keyPressed = new ConsoleKeyInfo('a', ConsoleKey.A, false, false, false);
goto loopstart;
}
if (keyPressed.Key == ConsoleKey.Escape)
{
Console.WriteLine("You pressed the Escape Key!");
goto loopstart;
}
if (keyPressed.Key == ConsoleKey.Spacebar)
{
Console.WriteLine("You pressed the Spacebar!");
goto loopstart;
}
else
{
break;
}
}
Console.WriteLine("You broke the loop!");
goto start;
}
}
}

Flow of program not what was expected

Building a simple app here; the methods in question:
static coin class
public static void SetUpCoins() {
coin1 = new Coin();
coin2 = new Coin();
}
public static void PlayConsole() {
SetUpCoins();
OutputWinner();
}
public static void OutputWinner() {
if (coin1.ToString() == "Heads" && coin2.ToString() == "Heads") {
Console.WriteLine("You threw Heads - you win");
++point_player;
} else if (coin1.ToString() == "Tails" && coin2.ToString() == "Tails") {
Console.WriteLine("You threw Tails - I win");
++point_comp;
} else {
Console.WriteLine("You threw Odds");
WaitForKey_ConsoleOnly("Press any key to throw again");
PlayConsole();
}
Console.WriteLine("You have {0} points and the computer has {1} points", point_player, point_comp);
if (WantToPlayAgain_ConsoleOnly()) { // ask user if they want to play again; return bool
PlayConsole();
}
}
private static bool WantToPlayAgain_ConsoleOnly() {
string input;
bool validInput = false;
do {
Console.Write("Play Again? (Y or N): ");
input = Console.ReadLine().ToUpper();
validInput = input == "Y" || input == "N";
} while (!validInput);
return input == ("Y");
}
If false was to return from WantToPlayAgain_ConsoleOnly() the program does not exit. Here is an example of the output, which explains my problem:
Why, when WantToPlayAgain_ConsoleOnly is false, does the program not pass control the playConsole method then exit. instead of this repetition.
After OutputWinner is finished running, it then jumps into PlayConsole, and then back into the else statement of the OutputWinner - not sure why.
Because you are calling PlayConsole() after "Press any key to throw again". Once that call returns the program will unconditionally continue with "You have {0} points and the computer has {1} points", regardless of what happened during the call.
Try rewriting the logic to be iterative rather than recursive?
As the previous answers said, you have a problem when you throw odds.
Because at that point you call the exact same method again and when that call returns, you resume right at the score display and the player is asked again to play.
Because you are calling PlayConsole recursively, that means for every time you throw "Odd", you will get one more prompt that you didn't ask for.
You could restructure the method like this:
public static void OutputWinner() {
do {
// Game code
}
while (WantToPlayAgain_ConsoleOnly());
}
That also gets rid of the recursion.

How to stop a running method with keyboard input in a Console Application on C#?

In short, I'm utilizing C# to scientific computation and I've written a method that has a while loop that may run to a user-specified quantity of steps... Actually, this method may take too long to execute (like more than 5 hours). When it takes this long, I may want to stop the method pressing Esc key, for example.
As I read something about breaking while, it is as simple as a Boolean flag or something like this. So I thought in something like this:
public Double? Run(int n)
{
int i = 0;
while ((i < n) && (/* inputkey != ConsoleKey.Escape */))
{
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
// I'm not worried about the return statement, as it is easy to do...
// returns null if the user skipped the method by pressing Escape
// returns null if the method didn't converged
// returns the double value that the method calculated otherwise
}
Well, this is what I wondered until now... So please, could you give useful ideas to this extent? How can I wait for a user input (I thought about Events, but I'm not sure how to implement it here and I think that it will make the code even slower, if I have to listen to a key at every while step the code goes into...
Well, any ideas or comments?
Update: I think I should have had described better the problem. All the solutions you gave me may solve this problem I proposed, but I think I was not completely reliable to my real problem. I don't know if I should ask another question or keep with this one...
You could run this method from a separate thread and set a stop variable when a key is pressed:
object myLock = new object();
bool stopProcessing = false;
public Double? Run(int n)
{
int i = 0;
while (i < n)
{
lock(myLock)
{
if(stopProcessing)
break;
}
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
}
and when a key is pressed, update stopProcessing accordingly:
Console.ReadKey();
lock(myLock)
{
stopProcessing = true;
}
If you're just wanting to stop the application, Ctrl-C from the command line will do it. If you really need to intercept input during a long running process, you might want to spawn a worker thread to do the long running process and then just use the main thread to interact with the console (i.e. Console.ReadLine()).
You will need to do this using threading. When you start the task, spawn a new thread and execute the task on that thread. Then in your Program.cs, wait for user input. If the user enters something meaningful - in your case, the Esc key - alert the background thread of the action. The simplest way to do this is by setting a static variable. The background thread will be checking this static variable and when it has been changed, the background thread will clean itself up and abort.
See the MSDN article on Threading.
A code sample will be a little more in depth, but it would look something like this:
public class Program.cs
{
public static myFlag = false;
public void Main()
{
thread = new Thread(new ThreadStart(DoWork));
thread.Start();
Console.ReadLine();
myFlag = true;
}
public static DoWork()
{
while(myFlag == false)
{
DoMoreWork();
}
CleanUp()
}
public static DoMoreWork() { }
public static CleanUp() { }
}
pool on Console.KeyAvailable in timely manner and take the action accordingly.
using System;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static bool _cancelled = false;
static void Main( string[] args )
{
var computationTask = Task.Factory.StartNew(PerformIncredibleComputation);
var acceptCancelKey = Task.Factory.StartNew(AcceptCancel);
while (!acceptCancelKey.IsCompleted && ! computationTask.IsCompleted)
{
computationTask.Wait (100);
}
if( acceptCancelKey.IsCompleted && !computationTask.IsCompleted )
{
computationTask.Wait (new System.Threading.CancellationToken ());
}
else if(!acceptCancelKey.IsCompleted)
{
acceptCancelKey.Wait(new System.Threading.CancellationToken());
}
}
private static void PerformIncredibleComputation()
{
Console.WriteLine("Performing computation.");
int ticks = Environment.TickCount;
int diff = Environment.TickCount - ticks;
while (!_cancelled && diff < 10000)
{
//computing
}
Console.WriteLine("Computation finished");
}
private static void AcceptCancel()
{
var key = Console.ReadKey(true);
Console.WriteLine("Press Esc to cancel");
while(key.Key != ConsoleKey.Escape)
{
key = Console.ReadKey(true);
}
_cancelled = true;
Console.Write("Computation was cancelled");
}
}
}

Threading: Variable Access and Termination of a Thread

Let me start by showing you my code so far:
using System;
using System.Threading;
class MathQuiz
{
static void Main()
{
int score = 0;
string preanswer;
decimal answer = 0;
Console.WriteLine("Welcome to Project5, a MathQuiz project.");
Console.WriteLine("You will be asked 10 questions, and will have 30 seconds to read and answer each one.");
Console.WriteLine("Press any key to begin.");
Console.ReadKey(true);
Console.WriteLine("What is 2 + 2?");
Thread ask = new Thread (new ThreadStart (MathQuiz.prompt));
ask.Start();
Thread.Sleep(3000);
//This is where I want to end the thread if it isn't already done.
if (answer == 4)
{
score = score+1; //Here's where I don't know if my adding is correct.
}
Console.WriteLine("Press any key to move on to the next question!");
Console.ReadKey(true);
}
static void prompt()
{
preanswer = (Console.ReadLine());
if (!decimal.TryParse(preanswer, out answer))
{
Console.WriteLine("That wasn't even a number or decimal!");
}
else
{
answer = decimal.Parse(preanswer);
}
}
}
So, when I try and compile this code, I get CS0103 errors for preanswer and answer in the "prompt" method.
This leads to 3 questions:
What EXACTLY do I have to do to make preanswer and answer accessible to the "prompt" method?
Did I add 1 onto the score variable correctly?
How can I terminate a thread if it is running? (In this case, the "ask" thread wouldn't end until they typed an answer.)
Please just tell me what to change. I don't know coding words and terminology because I just started a few weeks ago. Please try to be as clear as possible.
static string preanswer;
static decimal answer = 0;
static void Main()
{
int score = 0;
//string preanswer;
//decimal answer = 0;
...
etc.
To wait for the thread, use Join()... This will tell the thread which the function is called on to wait for the thread until it joins back:
ask.Join(int);

Categories