I am attempting to make a simple program that counts up one each time I press spacebar. I am very very new to writing software and most of this is completely foreign to me, so if you understand how I am thinking incorrectly I will welcome your insight!
using System;
namespace Counter
{
class Program
{
static void Main(string[] args)
{
var tap = 0;
while (Console.ReadKey(true).Key != ConsoleKey.Spacebar)
tap++;
Console.WriteLine();
}
}
}
This is what I tried. The console shows (0) and then closes when I press spacebar. I don't know how to stop it from closing the console, it's as if all it has in the code body is a variable and "Console.WriteLine()".
try this:
var tap = 0;
while (Console.ReadKey(true).Key != ConsoleKey.Spacebar)
{
tap++;
Console.WriteLine($"Key is not Spacebar, tap count = {tap}");
}
Console.WriteLine("You just press spacebar, now press enter to exit this console app");
Console.ReadLine();//press enter to exit
static void Main(string[] args)
{
int tap = 0;
while (Console.ReadKey(true).Key == ConsoleKey.Spacebar)
{
tap++;
Console.WriteLine("You pressed 'Spacebar' " + tap + "-time(s) so far.");
}
Console.WriteLine("You didn't press 'Spacebar' this time!");
System.Threading.Thread.Sleep(3000); // wait 3 seconds so you actually have the chance to read the text in the console
}
Event though I would recommend something like this instead:
static void Main(string[] args)
{
int tap = 0;
while(true)
{
if(Console.ReadKey(true).Key == ConsoleKey.Spacebar)
{
tap++;
Console.WriteLine("You pressed 'Spacebar' " + tap + "-time(s) so far.");
}
else
{
Console.WriteLine("You didn't press 'Spacebar' this time!");
System.Threading.Thread.Sleep(3000); // wait 3 seconds so you actually have the chance to read the text in the console
break;
}
}
}
I don't know if this is how to resolve a question in stackoverflow, but the answer was to make an if/elseif loop where Spacebar was an input that was counted and Enter was a Boolean value that closed the program when triggered to True.
{
var tap = 0;
bool quit = false;
while (quit == false)
{
if (Console.ReadKey().Key == ConsoleKey.Spacebar)
{
tap++;
Console.WriteLine(tap);
}
else if (Console.ReadKey(true).Key == ConsoleKey.Enter)
{
quit = true;
}
}
}
With the ElseIf I was able to count every trigger of Spacebar, whereas previously I had that statement within the If loop and it waited on the next keypress to see whether it was the Enter key or not.
Thanks for the help!
The goal of my current Typing Game feature (code provided below) is to countdown from either 10(hard), 20(medium), or 30(easy), depending on the users difficulty selection once the game has started. Once the countdown reaches zero OR the user runs out of lives, the game is over (which stops the countdown). The game starts properly, displays the first word to be typed by the user, and begins counting down from 10. My current problem is that I can't figure out how to stop the timer from counting down past 0. I have tried using t.Change(Timeout.Infinite , Timeout.Infinite) and checking when timeLeft == 0 with no prevail. I really feel like I am overthinking this (or underthinking), some help or a nudge in the right direction would be greatly appreciated!
my code is provided below:
class Program
{
// List of words to be typed
public static string[] words = { "some", "side", "add", "another", "would", "book" ,"water", "came" ,"your" ,"big","both", "from", "learn", "been", "me" ,"school" ,"land", "took", "place",
"try", "line", "tell", "earth", "can", "do","children", "without", "my", "must", "take", "follow", "year", "is", "being", "different", "miss", "could", "on", "he", "open", "night", "were",
"line","said", "around", "an", "plant", "know", "set","been", "life","young","of", "face", "we", "hand", "while", "is", "white", "call", "live","may", "study","after" ,"down", "him", "now", "different",
"could", "over", "work","all", "mean","begin","go","fall", "really", "oil", "before","into","one","name","has","a", "well", "got","something","turn" };
// Keeps track of words array
public static int numWords = 88;
public static int correctWords = 0;
// Initialize our random number generator
public static Random rand = new Random();
// Handles user input
public static string userInput = "";
public static string endGameChoice = "";
// Handles gamestate variables
public static bool gameActive = false;
public static int numLives = 0;
public static int timeLeft;
public static System.Threading.Timer t;
// Entry Point
static void Main(string[] args)
{
// Start the game
Start();
}
// Handles gameplay
static void Play()
{
// Assigns the current word to any random word in
// the words array
string currentWord = words[rand.Next(0, 88)];
// Print the current word separated by lines
Console.WriteLine("********");
Console.WriteLine(currentWord);
Console.WriteLine("********");
// While the answser is incorrect/empty
while (!userInput.Equals("exit"))
{
// Reads user input
userInput = Console.ReadLine();
if (userInput.Equals(""))
{
Play();
}
// Checks if userInput is equal to current word
if (!(userInput.Equals(currentWord)))
{
// If incorrect, display loss of life
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Incorrect!!!");
Console.WriteLine("Lives: " + numLives);
numLives--; // Take a life away
Console.ResetColor();
}
if (numLives == -1)
{
outOfLives();
}
if (userInput.Equals(currentWord))
{
correctWords++;
Play();
}
}
if (userInput.Equals("exit"))
{
Environment.Exit(0);
}
}
// Function for running out of lives
private static void outOfLives()
{
Console.WriteLine("Game over! You typed " + correctWords + " words correctly!");
Console.WriteLine("Type anything and press enter to retry, Press Escape to Quit!");
var endGameKey = Console.ReadKey();
if (endGameKey.Key == ConsoleKey.Escape)
{
Environment.Exit(0);
}
if (endGameKey.Key == ConsoleKey.Enter)
{
restartGame();
}
else
{
outOfLives();
}
}
// Function for running out of time
private static void outOfTime()
{
Console.WriteLine("Out of time! You typed " + correctWords + " words correctly!");
Console.WriteLine("Type anything and press enter to retry, Press Escape to Quit!");
var endGameKey = Console.ReadKey();
if (endGameKey.Key == ConsoleKey.Escape)
{
Environment.Exit(0);
}
if (endGameKey.Key == ConsoleKey.Enter)
{
restartGame();
}
else
{
outOfTime();
}
}
// Prompts user for input for difficulty along with game instructions
static void StartMessage()
{
Console.WriteLine("Welcome to my Typing Practice App!");
Console.WriteLine("Type the word displayed as fast as you can");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Difficulties are listed from hardest to easiest");
Console.WriteLine();
Console.WriteLine("Select a difficulty( 1 ,2 , or 3 ): ");
Console.WriteLine();
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("-- Press ENTER --");
Console.WriteLine("*** Satan Himself ***");
Console.WriteLine();
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("-- Type 1 --)");
Console.WriteLine("*** Software Engineer ***");
Console.WriteLine();
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("-- Type 2 --)");
Console.WriteLine("*** Social Media Fanatic ***");
Console.WriteLine();
Console.ResetColor();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("-- Type 3 --)");
Console.WriteLine("*** Filthy Peasant ***");
Console.WriteLine();
Console.ResetColor();
string difficultyChoice = Console.ReadLine();
switch (difficultyChoice)
{
case "1":
numLives = 1;
Console.WriteLine("You have 2 lives! Good luck!");
timeLeft = 10;
break;
case "2":
numLives = 3;
Console.WriteLine("You have 4 lives! Good luck!");
timeLeft = 20;
break;
case "3":
numLives = 5;
Console.WriteLine("You have 6 lives! Good luck!");
timeLeft = 30;
break;
default:
numLives = 0;
Console.ForegroundColor = ConsoleColor.DarkRed;
Console.WriteLine("Miss one and you're done!!!!!");
Console.ResetColor();
timeLeft = 10;
break;
}
}
public static void restartGame()
{
Console.Clear();
//numLives = 5;
numWords = words.Length;
correctWords = 0;
Start();
}
public static void SetTimer(Object o)
{
timeLeft--;
Thread.Sleep(1000);
Console.WriteLine(timeLeft);
if (timeLeft == 0)
{
outOfTime();
}
}
public static void Start()
{
// Display start message
StartMessage();
gameActive = true;
t = new System.Threading.Timer(SetTimer, null, 0, 1250);
// While user wants to play
while (!userInput.Equals("exit"))
{
// While the game is active
while (gameActive == true)
{
// Start the game
Play();
}
}
if (userInput.Equals("exit"))
{
Environment.Exit(0);
}
}
}
}
You don't need a progammatic timer or thread to have a game timer. Just record the time at the beginning of the game:
//During initialization
var startTime = DateTime.Now;
And any time you wish to display or use a timer, compute it:
var timerValue = DateTime.Now - startTime;
Console.WriteLine("Time elapsed: {0} seconds", timerValue.Seconds);
Try to use this. Coroutines should solve your problem if you can get it to work.
you can use Timer in this way.
It works for you
var timer2 = new Timer();
timer2.Elapsed += (o, e) =>
{
Console.WriteLine("Time Elapsed {0}", e.SignalTime);
timer2.Stop();
};
timer2.Interval = 1000;
timer2.Start();
As a direct fix to the problem presented (timer going below 0), you need to stop the timer when it is no longer needed. For that, use this line:
t.Change(Timeout.Infinite, Timeout.Infinite);
You could add this line to the start of your outOfLives and outOfTime methods:
private static void outOfLives()
{
t.Change(Timeout.Infinite, Timeout.Infinite);
Console.WriteLine("Game over! You typed " + correctWords + " words correctly!");
...
and...
private static void outOfTime()
{
t.Change(Timeout.Infinite, Timeout.Infinite);
Console.WriteLine("Out of time! You typed " + correctWords + " words correctly!");
...
EDIT: Sorry. Just re-read your question and realised you had already tried this line. Did you have it in the correct place(s)?
I am just starting to use multi-threading, and I am trying to build a console application that starts to count with 2 different threads when the user hits the Enter key, and stops counting when the Enter key is pressed a second time, and then outputs the count for both threads (I realize that with my code, these will be 2 different numbers). I set this up, but for some reason, one of the numbers is usually negative.There is also quite a bit of a delay before I receive count output. Why am I getting negative counts, why is the delay so long, and how can I correct this?
My code so far;
class Program
{
static void Main(string[] args)
{
System.Threading.Thread threadA = new Thread(ThreadA);
System.Threading.Thread threadB = new Thread(ThreadB);
Console.WriteLine("Once you press enter, this application will count as high as it can until you press enter again.");
ConsoleKeyInfo info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
threadA.Start();
threadB.Start();
}
}
private static bool continueCounting = true;
static void ThreadA()
{
int count = 0;
for (int i = 0; i < int.MaxValue ; i++)
{
count++;
}
ConsoleKeyInfo info2 = Console.ReadKey();
if (info2.Key == ConsoleKey.Enter)
continueCounting = false;
Console.WriteLine(count);
}
static void ThreadB()
{
int count = 0;
while (continueCounting)
{
count++;
}
Console.WriteLine(count);
Console.ReadLine();
}
}
one of the numbers is usually negative
That's because there was no limit guard on the ThreadB loop. It was entirely possible for count to reach MaxValue then wrap around to negative maxvalue.
I also put all keyboard checks in your main thread where they arguably should be. It is here that we set continueCounting to false when the second enter key is pressed.
I also made continueCounting volatile as it is being used by multiple threads and its value should not be CPU optimised/cached.
Try this code, this fixes the delay you were experiencing; allows both threads to count at once; and exit ASAP when the enter key is pressed.
class Program
{
#region Static fields
private static volatile bool continueCounting = true;
#endregion
#region Methods
static void Main(string[] args)
{
var threadA = new Thread(ThreadA);
var threadB = new Thread(ThreadB);
Console.WriteLine(
"Once you press enter, this application will count as high as it can until you press enter again.");
var info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
threadA.Start();
threadB.Start();
}
info = Console.ReadKey();
if (info.Key == ConsoleKey.Enter)
{
continueCounting = false;
}
Console.ReadLine();
}
static void ThreadA()
{
var count = 0;
for (var i = 0; i < int.MaxValue && continueCounting; i++)
{
count++;
}
Console.WriteLine($"A: {count}");
}
static void ThreadB()
{
var count = 0;
while (continueCounting && count < int.MaxValue)
{
count++;
}
Console.WriteLine($"B: {count}");
}
#endregion
}
I have a short question about timers. In my code I want to create a crop farming game, with a timer that shows if the plant is finished.
Why does this:
public static string currentPlant;
public static Timer growTimer;
public static void InitGrowTimer( int time, string name )
{
growTimer = new Timer();
growTimer.Tick += new EventHandler(growTimer_Finished);
growTimer.Interval = time;
currentPlant = name;
}
public static void plantCrop(string crop)
{
if (plantActive == false)
{
if (plants.Contains(crop.ToLower()))
{
// growTimer.Interval = <plant>Time;
// proceed plants
switch (crop.ToLower())
{
case "wheat":
InitGrowTimer(wheatTime, wheatName);
growTimer.Start();
break;
default:
MessageBox.Show("FATAL ERROR\nThe plant is contained in the definition list but not in the plant menu!", "Civilisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
else
{
MessageBox.Show("This plant is not available!", "Civilisation", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("There is already a plant in progress! Current plant: {0}", currentPlant);
}
}
private static void growTimer_Finished (object sender, EventArgs e)
{
growTimer.Stop();
MessageBox.Show("Your " + currentPlant + " is finished!", "Civilisation", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
not start the timer, or just doesnt show the messagebox at the end. What am I doing wrong at creating the timer or creating the tick event?
EDIT: This is my main method:
static void Main(string[] args)
{
InitializeLists();
// game begin
Farm.plantCrop("wheat");
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("Please enter your desired name: ");
QC.resetColors();
name = Console.ReadLine();
Console.WriteLine(/*Introduction*/"Welcome to the world of Civilisation. In this world it is your choice what\n" +
"you are up to. You can be a farmer, miner or fighter, whatever you want, the\n" +
"world is yours to explore! Have fun!"
);
Console.ReadKey();
Console.Clear();
while (true) // run game
{
// menu
Console.Write(" What do you want to do?\n" +
"Farm Mine Explore Go to the city\n"
);
input = Console.ReadLine();
if (menuContent.Contains(input.ToLower()))
{
if (input.ToLower() == menuContent.ElementAt(0))
{
// farm
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("-- Farm --\nSelect a crop to plant:");
Console.ForegroundColor = ConsoleColor.DarkGreen;
int icount = 0;
for ( int i = 0; i < Farm.plants.Count; i++)
{
if (icount < 3)
{
Console.Write(Farm.plants.ElementAt(i));
Console.Write("\t\t");
icount++;
}
else
{
Console.Write("\n");
icount = 0;
Console.Write(Farm.plants.ElementAt(i));
Console.Write("\t\t");
icount++;
}
}
Console.WriteLine();
QC.resetColors();
string crop = Console.ReadLine();
Farm.plantCrop(crop);
}
if (input.ToLower() == menuContent.ElementAt(1))
{
// miner
}
if (input.ToLower() == menuContent.ElementAt(2))
{
// fight
}
}
}
}
The System.Windows.Forms.Timer timer is made for a Windows Form app with a single UI thread.
You need to use the System.Threading.Timer timer instead for your console application.
It's creation and the parameters of the callback are a little different:
public static void InitGrowTimer(int time, string name)
{
growTimer = new System.Threading.Timer(GrowTimer_Finished, null, time, Timeout.Infinite);
currentPlant = name;
}
private static void GrowTimer_Finished(object sender)
{
MessageBox.Show("Your " + currentPlant + " is finished!", "Civilisation", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
You do not need any Startmethod, it will be started automatically on creation.
You don't need a Stop either; it will run only once because of the Timeout.Infinite parameter.
You can replace null with the object you want the callback to receive, if needed (i.e. what would have been in EventArgs).
Little side note: I've renamed your callback method in PascalCase. Per convention, the methods in C# should always start with a capital letter.
Because you, as you tell yourself, do not start the timer.
growTimer = new Timer();
growTimer.Tick += new EventHandler(growTimer_Finished);
growTimer.Interval = time;
growTimer.Start();
I have a function that reads numbers from console and writes them to queue and a massive of threads that should read numbers from queue, calculate factorials and write it to file.
static void Main(string[] args)
{
int threadsNumber = 0;
Console.WriteLine("Enter the number of threads: ");
while ((Int32.TryParse(Console.ReadLine(), out threadsNumber) == false)||(threadsNumber <= 0))
{
Console.WriteLine("You need to enter a positive number!");
}
Console.Clear();
Queue<int> numbersQueue = new Queue<int>();
Thread [] threadMas = new Thread[threadsNumber];
for (int i = 0; i < threadsNumber; i++)
{
threadMas[i] = new Thread(() => ThreadProc.ThreadProcStart(numbersQueue));
threadMas[i].Start();
}
//some code...
ThreadProc:
class ThreadProc
{
private static object locker = new object();
public static void ThreadProcStart(Queue<int> queue)
{
while (true)
{
int num = Deq(queue); //Checks if queue is empty. If not -
//Returns first element
if (num != -1)
{
BigInteger bigInt = Factorial.FactTree(num); //factorical
Writer.WriteToFile(num + " factorial: " + bigInt, "result.txt"); //write to file
}
else
{
// Here I need to add something
Thread.Sleep(0);
}
}
}
public static int Deq(Queue<int> queue)
{
lock (locker)
{
if (queue.Count != 0)
{
return queue.Dequeue();
}
return -1;
}
}
}
I need to stop all threads when all calculations are done. How can i mark thread as "unactive" when the queue is empty without actually aborting it?
Use BlockingCollection, which underneath the covers uses ConcurrentQueue. Your code will be soooo much simpler and you won't have to worry about subtle locking bugs.
Your code would then look something like this:
static void Main(string[] args)
{
int threadsNumber = 0;
Console.WriteLine("Enter the number of threads: ");
while ((Int32.TryParse(Console.ReadLine(), out threadsNumber) == false) || (threadsNumber <= 0))
{
Console.WriteLine("You need to enter a positive number!");
}
Console.Clear();
BlockingCollection<int> queue = new BlockingCollection<int>();
Thread[] threadMas = new Thread[threadsNumber];
for (int i = 0; i < threadsNumber; i++)
{
threadMas[i] = new Thread(ProcessQueue);
threadMas[i].Start(queue);
}
string userInput = null;
do
{
Console.WriteLine("Enter a number: ");
userInput = Console.ReadLine();
if (userInput != "stop") {
queue.Add(int.Parse(userInput)); // These numbers get process by the threads in parallel.
}
} while (userInput != "stop");
queue.CompleteAdding(); // Use this to signal that no more items will be added to the queue.
// Now the main thread exits.
// But the program stays alive until the other threads finish
// clearing the queue.
}
private static void ProcessQueue(object data)
{
BlockingCollection<int> queue = (BlockingCollection<int>)data;
foreach (var num in queue.GetConsumingEnumerable()) // This will automatically exit the loop once CompleteAdding is called and the queue is empty.
{
BigInteger bigInt = Factorial.FactTree(num); //factorical
Writer.WriteToFile(num + " factorial: " + bigInt, "result.txt"); //write to file
}
}