I am learning c# and I was trying to test my abilities by making a short game using the .net framework console. I want to make it so that if you don't type "(the thing the player needs to type)" within x amount of time, then they fail, and they go to the death screen.
I have tried looking up stuff like the System.Threading.CountdownEvent, Systen.Threading.Timer and System.Timers.Timer etc, but all the tutorials don't help.
tutorialfight:
if (fight == "Y")
{
Console.WriteLine("Goblin: 'You seem week, hit me first!'");
}
else if (fight == "N")
{
Console.WriteLine("This is the tutorial, you have no choice, Would you like to fight? (Y/N)");
fight = Console.ReadLine();
goto tutorialfight;
}
else
{
Console.WriteLine("Type Y for yes or N for no");
fight = Console.ReadLine();
goto tutorialfight;
}
int goblinhp = 5;
Console.WriteLine("Misery: 'Type what is says to do some damage. If you fail, you take some damage, and if you dont kill the enemy within the amount of time allowed, you die!'");
Console.WriteLine("Type 'x3hu' in 10 seconds to deal 1 damage");
string x3hu = Console.ReadLine();
if (x3hu != "x3hu")
{
Console.WriteLine("Your health went down 3 points!");
health = health - 3;
Console.WriteLine("Health: " + health);
Console.WriteLine("Misery: 'Dont worry, you've still got loads of health left!'");
}
else
{
goblinhp = goblinhp - 1;
Console.WriteLine("Goblin health: " + goblinhp);
Console.WriteLine("Misery: 'Great job!'");
}
It seems like you already have a grasp of what you need:
Some way of detecting Console input is available to read
Some way of setting a timeout for user input
You should be able to accomplish this by using the Console.KeyAvailable property and tracking the elapsed time of a Stopwatch.
The pseudo code would go something like this:
Console.WriteLine("You have 10 seconds to press attack (x)");
var timeout = 10;
StopWatch.Start();
while(Stopwatch.Elapsed.Seconds < timeout && !Console.KeyAvailable)
{
// wait
}
// process which event happened first
The conditions and looping logic could vary but this should be enough to get you headed in the right direction :)
KeyAvailable documentation
Stopwatch documentation
I made a simple solution based on Dawid Owens' answer.
class Program
{
static async Task Main(string[] args)
{
var timeout = 10;
var textToWrite = "Hello World!";
bool isTimeIsUp = false;
bool returnPressed = false;
StringBuilder enteredText = new StringBuilder();
Console.WriteLine($"You have {timeout} seconds to write: '{textToWrite}'");
Stopwatch stopwatch = Stopwatch.StartNew();
while (!returnPressed)
{
while (!isTimeIsUp && !Console.KeyAvailable)
{
isTimeIsUp = stopwatch.Elapsed.Seconds >= timeout;
}
if (isTimeIsUp) break;
var ch = Console.ReadKey();
returnPressed = ch.Key == ConsoleKey.Enter;
if (!returnPressed)
{
enteredText.Append(ch.KeyChar);
}
}
if (isTimeIsUp || enteredText.ToString() != textToWrite)
{
Console.WriteLine($"\nFailure!");
}
else
{
Console.WriteLine($"\nSuccess!");
}
}
}
Related
Sup, I am completely new to stackOverflow and I started to learn how to do C# during my class. For my final project, I'm doing a reaction game where you have to press the key t as fast as possible before the timer. However, one major issue came up. I tried using a timer method and had a variable inside of it called timeLeft. What I want to happen is that as soon as the user presses t, the time stops and it shows you the remaining time. However what I get instead is that no matter what, the computer doesn't take the key and tells you that you ran out of time. I didn't know where else to put this question in, but I felt like this is a good spot. The project is made in C# and with visual studio. My code is below, and if there is anything i should change or do for this project, let me know. Thank you!
static void Main(string[] args)
{
string starter;
ConsoleKeyInfo stopper;
Console.WriteLine("Welcome to REFLEX. Check how fast are those hands?");
Console.WriteLine("When you are ready to start, type start");
starter = Console.ReadLine();
if (starter.ToLower() == "start")
{
Console.WriteLine("Go");
Console.WriteLine("Press t to stop timer!");
stopper = Console.ReadKey();
Console.WriteLine(RunTimer(stopper));
}
else
{
Console.WriteLine("what are you doing? You have one more chance, otherwise restart");
Console.WriteLine("Welcome to REFLEX. Check how fast are those hands?");
Console.WriteLine("When you are ready to start, type start");
starter = Console.ReadLine();
if (starter.ToLower() == "start")
{
Console.WriteLine("Go");
Console.WriteLine("Press t to stop timer!");
stopper = Console.ReadKey();
Console.WriteLine(RunTimer(stopper));
}
else
{
Console.WriteLine("That's it, no more!");
}
}
}
static string RunTimer(ConsoleKeyInfo stopper)
{
int timeLeft;
for (timeLeft = 450000000; timeLeft > 0; timeLeft--)
{
while (stopper.Key != ConsoleKey.T)
{
do
{
stopper = Console.ReadKey();
Console.WriteLine(RunTimer(stopper));
}
while (timeLeft > 0);
}
}
if (timeLeft == 0)
{
return "Sorry, you ran out of time, try again!";
}
else
{
return "Congrats you won. You had " + timeLeft + " milseconds left";
}
}
The below should work for you, it will block until a key is pressed at Console.ReadKey, if you wanted to overcome this you would need to make it multithreaded but thats likely a problem for another day.
static string RunTimer()
{
int maxTime = 450000000;
ConsoleKeyInfo keyPressed;
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
do
{
keyPressed = Console.ReadKey();
}
while (stopwatch.Elapsed.Milliseconds < maxTime && keyPressed.Key != ConsoleKey.T);
stopwatch.Stop();
if (stopwatch.Elapsed.Milliseconds < maxTime)
{
return "Sorry, you ran out of time, try again!";
}
else
{
int timeLeft = maxTime - stopwatch.Elapsed.Milliseconds;
return "Congrats you won. You had " + timeLeft + " milseconds left";
}
}
To Use it change this
if (starter.ToLower() == "start")
{
Console.WriteLine("Go");
Console.WriteLine("Press t to stop timer!");
stopper = Console.ReadKey();
Console.WriteLine(RunTimer(stopper));
}
to
if (starter.ToLower() == "start")
{
Console.WriteLine("Go");
Console.WriteLine("Press t to stop timer!");
Console.WriteLine(RunTimer());
}
I'm just beginning in c# and I made a little program here. I'm just wondering if goto is a valid way of returning to certain sections of my code or if there is a more appropriate and practical way of doing it.
namespace Section5Tests
{
class Program
{
static void Main(string[] args)
{
Start:
var number = new Random().Next(1, 10);
int secret = number;
Console.WriteLine("Secret Number is between 1 and 10. ");
for (var i = 0; i < 10; i++)
{
Console.WriteLine("Guess the secret number you only have 3 attempts!");
Middle:
var guess = Convert.ToInt32(Console.ReadLine());
if (guess == secret)
{
Console.WriteLine("WoW! You got it! Well done!");
goto Playagain;
}
else
{
Console.WriteLine("Incorrect! Try again");
goto Middle;
}
}
Console.WriteLine("Sorry you lost =(");
Playagain:
Console.WriteLine("Try Again? Y/N");
var answer = Console.ReadLine();
if (answer.ToLower() == "y")
{
goto Start;
}
else
{
Console.WriteLine("Thankyou for playing =)");
}
}
}
}
In C#, a better way of doing things like this would be to refactor the program into individual methods which have unique and descriptive names. This is a much better solution than using goto in 99.9% of cases.
You generally never want all of your code to be in a single main method. Instead, I would refactor the game itself into its own method. Then, in the main loop, you can only check if the user is playing.
static void Main (string[] args)
{
var isPlaying = true;
while (isPlaying)
{
isPlaying = PlayGame();
}
Console.WriteLine("Thankyou for playing =)");
}
That way, you can have the PlayGame method return a bool to specify if the user is still playing. Instead of using goto, you can control the program's flow using check variables and smart coding:
static bool PlayGame ()
{
int number = new Random().Next(1, 10);
var userWon = false;
Console.WriteLine("Secret Number is between 1 and 10. ");
for (var numOfAttempts = 10; numOfAttempts > 0; numOfAttempts--)
{
Console.WriteLine($"Guess the secret number you only have {numOfAttempts} attempts!");
var guess = Convert.ToInt32(Console.ReadLine());
if (guess == number)
{
userWon = true;
break;
}
Console.WriteLine("Incorrect! Try again");
}
if (userWon)
Console.WriteLine("WoW! You got it! Well done!");
else
Console.WriteLine("Sorry you lost =(");
Console.WriteLine("Try Again? Y/N");
var answer = Console.ReadLine();
return answer.ToLower() == "y";
}
If you have to ask, you're not ready for GOTO. Don't use it. Use loops and functions instead.
To get you started, instead of Start:, use this:
string answer = "y";
while (answer = "y")
{
Of course you'll need to close that loop up down near your goto Start; statement, but this looks like coursework so I'll stop here. This should get you going.
wondering if goto is a valid way of returning to certain sections
of my code
No, it never is.
Only jumping forward out of deeply nested loops where a break won't go far enough, is valid. (And even that is frowned upon by many..)
All other uses are way too hard to understand when reading and debugging the code.
Instead write meaningful conditions in your loops! And try to concentrate on what you want to achieve, not how..
Breaking down your goals top down into small and simple chunks of work will help avoid coming even close to think about 'program flow' and helps thinking about 'solving problems' instead.
I wanted to be a bit different from the other answers. So, I have added if else statements inside your if statements to replace the goto() function. It's a bit of a long way of doing it but I hope it is easy enough to understand.
static void Main(string[] args)
{
var number = new Random().Next(1, 10);
int secret = number;
bool playAgain = true;
while (playAgain == true)
{
Console.WriteLine("Secret Number is between 1 and 10. ");
for (var i = 10; i > 0; i--)
{
Console.WriteLine("Guess the secret number you only have " + i + " attempts!");
var guess = Convert.ToInt32(Console.ReadLine());
if (guess == secret)
{
Console.WriteLine("WoW! You got it! Well done!");
Console.WriteLine("Would you like Play Again ? Y/N");
var play = Console.ReadLine();
if (play == "Y")
{
break;
}
else
{
playAgain = false;
break;
}
}
else if(i == 1)
{
Console.WriteLine("Sorry you lost =(");
Console.WriteLine("Try Again? Y/N");
var play = Console.ReadLine();
if (play == "Y")
{
break;
}
else
{
Console.WriteLine("Thankyou for playing =)");
playAgain = false;
}
}
}
}
}
I have created a game that gives a player 5 chances to play after which I would like to ask the player if they would like to play again or quit. I have seen it done in Python, but I do not know python. My code works perfectly fine, but I would like to add these two additional functions
How can I achieve these functionality in C#?
For reference this is what my code main class code looks like.
namespace NumBaseBall
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("\t\t\t*************************************");
Console.WriteLine("\t\t\t* Let's Have Some Fun *");
Console.WriteLine("\t\t\t* Welcome To The *");
Console.WriteLine("\t\t\t* Number Baseball Game *");
Console.WriteLine("\t\t\t*************************************\n");
GameResults gameresults = new GameResults();
for (int trysCounter = 1; trysCounter <= 5; trysCounter++)
{
gameresults.Strikes = 0;
Random r = new Random();
var myRange = Enumerable.Range(1, 9);
var computerNumbers = myRange.OrderBy(i => r.Next()).Take(3).ToList();
Console.WriteLine("The Game's Three Random Integers Are: (Hidden from user)");
foreach (int integer in computerNumbers)
{
Console.WriteLine("{0}", integer);
}
List<int> playerNumbers = new List<int>();
Console.WriteLine("Please Enter Three Unique Single Digit Integers and Press ENTER after each:");
for (int i = 0; i < 3; i++)
{
Console.Write("");
int number = Convert.ToInt32(Console.ReadLine());
playerNumbers.Add(number);
}
gameresults.StrikesOrBalls(computerNumbers,playerNumbers);
Console.WriteLine("---> Computer's Numbers = {0}{1}{2}", computerNumbers[0], computerNumbers[1], computerNumbers[2]);
Console.WriteLine("---> Player's Numbers = {0}{1}{2}", playerNumbers[0], playerNumbers[1], playerNumbers[2]);
Console.WriteLine("---> Game Results = {0} STRIKES & {1} BALLS\n", gameresults.Strikes, gameresults.Balls);
Console.WriteLine("You have played this games {0} times\n", trysCounter);
gameresults.TotalStrikes = gameresults.TotalStrikes + gameresults.Strikes;
Console.WriteLine("STRIKES = {0} ", gameresults.TotalStrikes);
if (gameresults.TotalStrikes >= 3)
{
gameresults.Wins++;
Console.WriteLine("YOU ARE A WINNER!!!");
break;
}
}
if (gameresults.TotalStrikes <3)
Console.WriteLine("YOU LOSE :( PLEASE TRY AGAIN!");
}
}
}
Insert your code inside an loop which checks if the user wants to continue:
while(true) // Continue the game untill the user does want to anymore...
{
// Your original code or routine.
while(true) // Continue asking until a correct answer is given.
{
Console.Write("Do you want to play again [Y/N]?");
string answer = Console.ReadLine().ToUpper();
if (answer == "Y")
break; // Exit the inner while-loop and continue in the outer while loop.
if (answer == "N")
return; // Exit the Main-method.
}
}
But perhaps it would be better to split one big routine up into seperate routines.
Lets rename your Main-method to PlayTheGame.
Split up my routines into:
static public bool PlayAgain()
{
while(true) // Continue asking until a correct answer is given.
{
Console.Write("Do you want to play again [Y/N]?");
string answer = Console.ReadLine().ToUpper();
if (answer == "Y")
return true;
if (answer == "N")
return false;
}
}
And now the Main-method can be:
static void Main(string[] args)
{
do
{
PlayTheGame();
}
while(PlayAgain());
}
You'de have to move some local variables to the class as static fields. Or you could make an instance of a Game class, but I think that is one step to far right now.
There are two ways you could accomplish this:
https://msdn.microsoft.com/en-us/library/system.diagnostics.process.kill%28v=vs.110%29.aspx
System.Diagnostics.Process.GetCurrentProcess().Kill();
Or
https://msdn.microsoft.com/en-us/library/system.environment.exit(v=vs.110).aspx
int exitCode =1;
System.Environment.Exit(exitCode);
Environment.Exit is the preferred way to exit your program since the Kill command "causes an abnormal process termination and should be used only when necessary."[msdn]
First, take the suggestion of moving the code for the actual game playing into a separate function. It will clean things up a lot.
Something like
private static bool PlayGame()
{
// Win branch returns true.
// Loss branch returns false.
}
This then lets you greatly simplify the Main function allowing it to only handle the menu functionality.
For the actual menu functionality, I tend to prefer do/while loops. You have a bit of an extra stipulation that you only ask after 5 plays, but that's easy enough to deal with.
static void Main(string[] args)
{
int playCount = 0;
string answer = "Y";
bool winner;
do
{
if(playCount < 5)
{
playCount++;
}
else
{
do
{
Console.Write("Play again? (Y/N): ");
answer = Console.ReadLine().ToUpper();
} while(answer != "Y" && answer != "N");
}
winner = PlayGame();
} while(!winner && answer == "Y");
Console.WriteLine("Thanks for playing!");
}
You could simplify it a bit by moving the test for 5 games into the if conditional with the use of an increment operator. The only issue is if someone plays your game a billion or so times, things might get weird.
static void Main(string[] args)
{
int playCount = 0;
string answer = "Y";
bool winner;
do
{
if(playCount++ > 3)
{
do
{
Console.Write("Play again? (Y/N): ");
answer = Console.ReadLine().ToUpper();
} while(answer != "Y" && answer != "N");
}
winner = PlayGame();
} while(!winner && answer == "Y");
Console.WriteLine("Thanks for playing!");
}
Edit: Changed things a bit as it looks like in your original code the game ends after the person wins the game.
Per your question in your comment below, you could make a static instance of your GameResults class in your Program class. Your code would end up looking something like the following
class Program
{
private static GameResults results = new GameResults();
public static void Main(string[] args)
{
// Code
}
private static bool PlayGame()
{
// Code
}
}
In PlayGame you would just use the static results object instead of creating a new one every time PlayGame is called.
I'm really sorry because I didn't put the code in English and because I didn't write the all of code, that you can understand it then.
So I edited and deleted the post I've written and here Is all the code with some comments to help you understand it. Problem is marked with *** , and if I know that this is probably very stupid question, but I couldn't find the answer.
HERE IT IS: `// Creates the array with automaticlly generated values to 100
int arrayNumber = 0;
List array = new List();
while(true){
if (array.Count == 100){
break;
} else{
array.Add(arrayNumber);
arrayNumber++;
}
}
// Changes the title of program
Console.Title = "Calculator of time spent in school.";
// Prints welcome message
Console.WriteLine("Hi user.!");
// Sleeps for a while
Thread.Sleep(1500);
// Writes the next message
Console.WriteLine("\nWelcome to Calculator of time spent in school.");
Thread.Sleep(2500);
DoAgain:
Console.Clear();
// Asks user when school starts in hours
Console.Write("Write the time when school starts in hours: ");
Thread.Sleep(500);
// Creates new variable type of INT and checks is it valid
string TIMEschoolStarts = Convert.ToString(Console.ReadLine());
int timeSchoolStarts;
if (Convert.ToInt32(TIMEschoolStarts) < 6)
{
timeSchoolStarts = Convert.ToInt32(TIMEschoolStarts);
do
{
timeSchoolStarts++;
} while (timeSchoolStarts < 6);
}
else if (Convert.ToInt32(TIMEschoolStarts) > 18)
{
timeSchoolStarts = Convert.ToInt32(TIMEschoolStarts);
do
{
timeSchoolStarts--;
} while (timeSchoolStarts > 18);
}
else if (array.Contains(Convert.ToInt32(TIMEschoolStarts)) == false)
{
goto DoAgain;
}
// Creates new variable for the Time in Minutes when school starts
Console.Write("Write the time when school starts in minutes: ");
Thread.Sleep(500);
string TimesInMinutes = Convert.ToString(Console.ReadLine());
int minutesTime;
// Checks is variable written and edit it
if (TimesInMinutes == "")
{
minutesTime = 0;
}
else {
minutesTime = Convert.ToInt32(TimesInMinutes);
}
Console.Clear();
Console.Write("Write how much classes you have: ");
int numberOfClasses = Convert.ToInt32(Console.ReadLine());
Console.Clear();
int timeSpentHours, timeSpentMinutes, timeSpent;
// Gives new values of time SPENT in school
timeSpent = numberOfClasses * 45 + (numberOfClasses - 2) * 5 + 20;
timeSpentHours = timeSpent / 60;
timeSpentMinutes=timeSpent % 60;
// Gives new values of time to the variables
// ****
timeSchoolStarts += timeSpentHours;
// ****
minutesTime += timeSpentMinutes;
// Prints the value when school is ending to the user
Console.WriteLine("School is ending in {0} hours, {1} minutes.\nTotal time spent in school is {2} hours, {3} minutes.", timeSchoolStarts, minutesTime, timeSpentHours, timeSpentMinutes);
Thread.Sleep(4000);
Console.Write("\Type RUN to start program again: ");
string again = Convert.ToString(Console.ReadLine());
if (again.ToUpper()=="RUN") {
goto DoAgain;`
PS : Just to know this is program what calculates time spent in school. If you find better way to create it please post.
Greetings from Serbia!
At first: the return value of ReadLine() is a string, the convert is unnecessary.
Second: The do-while loop is also unnecessary.
Third: goto is a no go in a clean code!
Fourth: Is "niz" the array and whats your target ?
I'm trying to make a test to see if someone has certain skills.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class timerup
{
public bool timeup = false;
}
class Program
{
public static void timer()
{
for (int i = 1; i < 3; i++)
{
System.Threading.Thread.Sleep(1000);
if (i == 5)
{
object a;
a = true;
a = new timerup();
timerup ClassRef;
ClassRef = (timerup)a;
ClassRef.timeup = true;
}
}
}
static void Main(string[] args)
{
Console.Title = "The Secret Agent Test";
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Welcome, agent. This is the test to see if\nyou are good enough to be a full member of the OT Secret Agency.");
Console.WriteLine("Do you want to continue? [Y/N]");
string cont = Console.ReadLine();
if (cont == "y" || cont =="Y")
{
Console.Clear();
Console.WriteLine("Let's continue the test.");
Console.WriteLine("Crack the password:");
Console.WriteLine("Username: IDIOT_NOOB1337\nPROFILE: Likes memes such as doge.\nIs an elitist (Over the things he likes)\nOnly uses the word idiot as an insult");
Console.WriteLine("Password:");
string pass1 = Console.ReadLine();
if (pass1 == "AnyoneWhoDoesn'tLikeDogeIsAnIdiot" || pass1 == "anyonewhodoesn'tlikedogeisanidiot")
{
Console.WriteLine("Account accessed.");
Console.WriteLine("Stage 1 Complete.");
Console.WriteLine("Loading next level...");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Level 2 loaded.");
System.Threading.Thread.Sleep(1000);
Console.Clear();
Console.WriteLine("Nice. You certainly have skill. But this test.... determines speed of mind.");
System.Threading.Thread.Sleep(2500);
Console.Clear();
Console.WriteLine("You only have two seconds to answer the next question. Press any key when ready.");
Console.ReadKey();
Console.Clear();
Console.WriteLine("What is 12x12?!"); // QUESTION
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(timer)); // SUCH COMPLEX CODE FOR A TIMER... WTF.
string product = Console.ReadLine();
object b;
b = true;
b = new timerup();
timerup ClassRef;
ClassRef = (timerup)b;
bool timerthing = ClassRef.timeup;
if (product != "144" || timerthing == true)
{
Console.WriteLine("Sorry, you are incorrect. Restart the test again.");
System.Threading.Thread.Sleep(2000);
Console.Clear();
System.Environment.Exit(-1);
}
else
{
Console.WriteLine("Impressive. Your mind is fast, too. Well, be prepared for the next test. Pressure.");
}
}
}
}
}
}
The thread does not execute; I suspect it is because of the string product = Console.ReadLine(); bit. The second question of this quiz was 12x12, and you have 2 seconds to answer, except the thread that counted the two seconds wasn't executed... Why...? And if you know, how would I fix it?
You only created a thread. You should also start it.
System.Threading.Thread t = new System.Threading.Thread(timer);
t.Start();
Just wrote this down as an example of how you can check for how long time has passed without using a thread.
bool isInTime = false;
var start = DateTime.Now;
Console.WriteLine("answer this in 5 seconds, what is 2x2");
var answer = Console.ReadLine();
if ((DateTime.Now - start).TotalSeconds <= 5)
isInTime = true;
if (isInTime && answer == "4")
Console.WriteLine("Good job you are now an agent");
else
Console.WriteLine("To slow and too dumb");
Console.ReadKey();
Stopwatch is another alternative: http://www.dotnetperls.com/stopwatch
If you really want threads (which are overkill for this problem) there are some good examples here: https://msdn.microsoft.com/en-us/library/ts553s52(v=vs.110).aspx
The two answers are on the spot, so let me just add how you can create a timer that's not as convoluted :)
var timeIsUp = false;
var timer = new Timer(_ => { timeIsUp = true; }, null, 5000, Timeout.Infinite);
But in general, #JensB is absolutely right - using multi-threading should be the last option. It's very hard to handle multi-threading properly, so avoiding it is a pretty decent strategy. The Timer example I've shown is also multi-threaded - the callback on the timer will occur on a different thread. This introduces synchronization issues, but they shouldn't be too painful for a simple case like this. To improve upon this, you'd at least want to ensure the local is updated safely:
var syncObject = new object();
var timeIsUp = false;
var timer = new Timer(_ => { lock (syncObject) { timeIsUp = true; } }, null, 5000,
Timeout.Infinite);
var answer = Console.ReadLine();
lock (syncObject)
{
if (timeIsUp) ...
}
Finally, using Thread manually is completely unnecessary nowadays. It's much easier to use Tasks for concurrency and multi-threading. For example:
var timerTask = Task.Delay(5000);
var answer = Console.ReadLine();
if (timerTask.IsCompleted) Console.WriteLine("Too late");
The best option IMO would be to use proper asynchronous APIs - sadly, the .NET Console class doesn't have those. As silly as it is, it seems that this is a pretty decent option:
void Main()
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(2));
var task = Task.Run(() => ReadLineFromConsole(cts.Token));
task.Wait(cts.Token);
if (task.IsCanceled)
{
Console.WriteLine("Too slow!");
return;
}
var result = task.Result;
if (result != "144")
{
Console.WriteLine("Wrong!");
return;
}
// Continue
}
public string ReadLineFromConsole(CancellationToken token)
{
var buffer = new StringBuilder();
int ch;
while (!token.IsCancellationRequested)
{
Console.In.Peek();
token.ThrowIfCancellationRequested();
ch = Console.In.Read();
if (ch == -1) return buffer.Length > 0 ? buffer.ToString() : null;
if (ch == '\r' || ch == '\n')
{
if (ch == '\r' && Console.In.Peek() == '\n') Console.In.Read();
return buffer.ToString();
}
buffer.Append((char)ch);
}
token.ThrowIfCancellationRequested();
// Shouldn't be reached, but the compiler doesn't know that.
return null;
}
The interesting point about this approach is that I can exit the application (and abort the input) even if the user doesn't press enter. It also allows you to tie together complex work flows using await, although that's slightly tricky in a console application.
The helper method ReadLineFromConsole actually works the same as the usual ReadLine method, however, it also checks for cancellation, and to prevent it from "stealing" data from later ReadLine calls, it will Peek first. This doesn't make it thread-safe - you still shouldn't using multiple readlines at the same time from different threads. But it does mean that we can ignore the output when it finally comes. Bear in mind that the thread will be waiting all this time until a console input comes - do not use this to launch multiple simultaneous requests without ensuring there's some input on the way eventually (e.g. using the usual Console.ReadLine in between the ReadLineFromConsole calls etc.).
Some refactoring to your code and solution to your problem :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
namespace ConsoleApplication2
{
class Program
{
static void WriteText(params string[] lines) { WriteText(0, lines); }
static void WriteText(double delaySecs, params string[] lines)
{
for (int i = 0; i < lines.Length; i++) Console.WriteLine(lines[i]);
if (delaySecs > 0) Thread.Sleep(TimeSpan.FromSeconds(delaySecs));
}
static void Main(string[] args)
{
Console.Title = "The Secret Agent Test";
Console.ForegroundColor = ConsoleColor.Green;
WriteText("Welcome, agent. This is the test to see if\nyou are good enough to be a full member of the OT Secret Agency.", "Do you want to continue? [Y/N]");
var readk = Console.ReadKey();
if (readk.Key == ConsoleKey.Y || readk.Key == ConsoleKey.N)
{
Console.Clear();
WriteText("Let's continue the test.\n", "Crack the password:\n", "Username: IDIOT_NOOB1337\nPROFILE: Likes memes such as doge.",
"Is an elitist (Over the things he likes)", "Only uses the word idiot as an insult", "Password:");
string pass1 = Console.ReadLine();
if (pass1 != "AnyoneWhoDoesn'tLikeDogeIsAnIdiot" && pass1 != "anyonewhodoesn'tlikedogeisanidiot") return;
WriteText(2, "Account accessed.", "Stage 1 Complete.", "Loading next level...");
WriteText(1, "Level 2 loaded.");
Console.Clear();
WriteText(2.5, "Nice. You certainly have skill. But this test.... determines speed of mind.");
Console.Clear();
Console.WriteLine("You only have two seconds to answer the next question. Press any key when ready.");
Console.ReadKey();
Console.Clear();
Console.WriteLine("What is 12x12?!"); // QUESTION
int allowedTime = 2 * 1000; // time allowed
new Thread(() =>
{
Stopwatch s = new Stopwatch();
s.Start();
while (s.ElapsedMilliseconds < allowedTime) { }
WriteText(2, "Sorry, you're too late. Restart the test again.");
Console.Clear();
Environment.Exit(-1);
}).Start();
string product = Console.ReadLine();
if (product == "144") Console.WriteLine("Impressive. Your mind is fast, too. Well, be prepared for the next test. Pressure.");
WriteText(2, "Sorry, you are incorrect. Restart the test again.");
Console.Clear();
}
}
}
}