Threads do not start as expected - c#

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();
}
}
}
}

Related

On key down, abort a thread

I've just started learning C# and I'm trying to figure out threads.
So, I've made a two threads and I would like to stop one of them by pressing x.
So far when I press x it only shows on the console but it doesn't abort the thread.
I'm obviously doing something wronng so can someone please point out what I'm doing wrong? Thank you.
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//Creating Threads
Thread t1 = new Thread(Method1)
{
Name = "Thread1"
};
Thread t4 = new Thread(Method4)
{
Name = "Thread4"
};
t1.Start();
t4.Start();
Console.WriteLine("Method4 has started. Press x to stop it. You have 5 SECONDS!!!");
var input = Console.ReadKey();
string input2 = input.Key.ToString();
Console.ReadKey();
if (input2 == "x")
{
t4.Abort();
Console.WriteLine("SUCCESS! You have stoped Thread4! Congrats.");
};
Console.Read();
}
static void Method1()
{
Console.WriteLine("Method1 Started using " + Thread.CurrentThread.Name);
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Method1: " + i);
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Method1 Ended using " + Thread.CurrentThread.Name);
}
static void Method4()
{
Console.WriteLine("Method4 Started using " + Thread.CurrentThread.Name);
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Method4: " + i);
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("Method4 Ended using " + Thread.CurrentThread.Name);
}
It looks like you have a extra Console.ReadKey(); before if (input2 == "x"), that extra read causes the program to stop and wait before going inside your if statement waiting for a 2nd key to be pressed.
Also input.Key returns a enum, when you do the to string on it the enum will use a capital X because that is what it is set to. Either use input.KeyChar.ToString() to convert it to a string or use
var input = Console.ReadKey();
if (input.Key == ConsoleKey.X)
To compare against the enum instead of a string.
I also recommend you read the article "How to debug small programs", debugging is a skill you will need to learn to be able to write more complex programs. Stepping through the code with a debugger you would have seen input2 was equal to X so your if statement was
if ("X" == "x")
which is not true.

Is there a way to add a countdown to a .net console?

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!");
}
}
}

High CPU usage in console application

I want to constantly wait for a key combination to be pressed in my console application, but the way I am currently doing it seems to use a lot of CPU while the process is running.
For such a basic task it feels like there should be a better way to do this, but I'm unsure of what that is, I profiled my application with dotTrace and found that the only hot spot was this code below.
while (true)
{
if (!Console.KeyAvailable)
{
continue;
}
var input = Console.ReadKey(true);
if (input.Modifiers != ConsoleModifiers.Control)
{
continue;
}
if (input.Key == ConsoleKey.S)
{
Server?.Dispose();
}
}
If you are fine using standard Ctrl+C for exit instead of Ctrl+S you can use simple ReadKey. And make sure TreatControlCAsInput is set, oterwise, the application will just be killed.
static void Main(string[] args)
{
// important!!!
Console.TreatControlCAsInput = true;
while (true)
{
Console.WriteLine("Use CTRL+C to exit");
var input = Console.ReadKey();
if (input.Key == ConsoleKey.C && input.Modifiers == ConsoleModifiers.Control)
{
break;
}
}
// Cleanup
// Server?.Dispose();
}
Instead of watching this in a loop, use the keypress event to check each time a key is pressed.
This means you only check once for each key press.
Edit:
I missed the console app part but you can read the line like this:
from: https://www.dotnetperls.com/console-readline
using System;
class Program
{
static void Main()
{
while (true) // Loop indefinitely
{
Console.WriteLine("Enter input:"); // Prompt
string line = Console.ReadLine(); // Get string from user
if (line == "exit") // Check string
{
break;
}
Console.Write("You typed "); // Report output
Console.Write(line.Length);
Console.WriteLine(" character(s)");
}
}
}
No busy waiting is needed. Console.ReadKey() will block until there is a key press available, with basically no CPU usage. Thus, you don't need to check Console.KeyAvailable over and over again.
while (true)
{
// DO NOT INTERCEPT KEY PRESSES!
//IF CTRL+S IS FORWARDED TO THE CONSOLE APP, WEIRD THINGS WILL HAPPEN.
var input = Console.ReadKey(false);
if (input.Modifiers != ConsoleModifiers.Control)
{
continue;
}
if (input.Key == ConsoleKey.S)
{
Server?.Dispose();
}
}
I think you should use Thread.Sleep
while (true)
{
Thread.Sleep(100);
if (!Console.KeyAvailable)
{
continue;
}
var input = Console.ReadKey(true);
if (input.Modifiers != ConsoleModifiers.Control)
{
continue;
}
if (input.Key == ConsoleKey.S)
{
Server?.Dispose();
}
}
You only need this before finish Main(string[] args)
private static void Main(string[] args)
{
//call method for daemon before while
while (true)
{
Thread.Sleep(1000);
}
}

Beginner C# Good Practices

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;
}
}
}
}
}

How to allow user to try again or quit console program in C#?

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.

Categories