Multithreaded console cursor manipulation race condition in C# - c#

I would be very thankful if anyone could give me a pointer in the right direction towards solving this problem I've been having in the last two days: I am working on a typing software for the Visual Studio 2010 console and need to represent time and be able to get input from the keyboard at the same time.
The problem is that the ReadLine() method allows me to write only when the timer thread is sleeping, maybe I could solve it via delegates or advanced task continuation methods, I'm a bit confused really since these are new concepts. Here is the code (sorry for the ugly formatting):
using System;
using System.Threading.Tasks;
using System.Threading;
namespace Typing_Game_tests
{
class Input_and_timer_multithread
{
public static void TimerThread()
{
for (int i = 1, mins = -1; i <= 1860; i++)
{
Console.SetCursorPosition(0, 0);
if (i % 60 == 1)
{
mins++;
}
Console.WriteLine("Timer: " + mins + " minute(s) and " + i % 60 + " seconds elapsed");
Console.SetCursorPosition(0, 6);
Thread.Sleep(1000);
}
}
static string keysRead;
public static void GetInput()
{
while (Console.ReadKey(true).Key != ConsoleKey.Enter)
{
Console.SetCursorPosition(0, 6);
keysRead = Console.ReadLine();
Console.SetCursorPosition(0, 6);
}
Console.SetCursorPosition(0, 6);
Console.WriteLine(keysRead);
}
static void Main(string[] args)
{
Thread timerMain = new Thread(new ThreadStart(TimerThread));
timerMain.Start();
Task getinputMain = new Task(GetInput);
getinputMain.Start();
}
}
}
Most of the examples I have found and studied have lambda expressions and delegates, which are new subjects I still have some difficulties understanding and thus implementing.
It would've been easier if I could've stopped the main timer thread until the ReadLine() did his job, but it wouldn't have made sense; plus I've tried using the Timers namespace, but I've had the same problem.
I wanted to give the impression that the cursor was permanently getting input from the user, but I still haven't found a way to efficiently switch back and forth the threads without deadlocking them.
Thanks.

This kind of code doesn't work any more since .NET 4.5. Console.ReadKey() and ReadLine() take a lock that prevents other threads from writing to the console. You'll need to replace it so the lock can't be taken anymore. That requires polling the keyboard with Console.KeyAvailable. Here is a simplistic replacement method for Console.ReadLine():
static object ConsoleLock = new object();
static string ReadString() {
var buf = new StringBuilder();
for (; ; ) {
while (!Console.KeyAvailable) System.Threading.Thread.Sleep(31);
lock(ConsoleLock) {
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter) {
Console.WriteLine();
return buf.ToString();
}
else if (key.Key == ConsoleKey.Backspace) {
if (buf.Length > 0) {
buf.Remove(buf.Length - 1, 1);
Console.Write("\b \b");
}
}
else {
buf.Append(key.KeyChar);
Console.Write(key.KeyChar);
}
}
}
}
Also lock on the ConsoleLock in your other thread that moves the cursor so output is strictly separated.

Related

Refactoring C# code for conversion to a WPF application (Visual Studio)

For this guessing game, I want to refactor this glass cannon of a code for conversion to a WPF application. Any methods I could use to shorten this/successfully convert and tips on VS, in general, would be greatly appreciated.
I'm using the WPF app (core) as a template for this program. As well as using the Microsoft tutorial for it to build it. The UI for this project is mostly done, just need to import this code.
Do note I'm in high school so my scope of knowledge isn't that big.
EDIT: Ok.
Firstly what I want to do with this code is cutoff the needless "IF" and "console.write" statements for a clean solution.
Secoundly I'm seperating the solution into two files, an App.xaml.cs file and a MainWindow.Xaml.cs file. Within App.xaml file I'm putting all my public classes(ex:guess,rnd,etc). While the MainWindow.Xaml file is where I'm putting the "Game Logic".
What I've done so far is; in regards to MainWindow.xaml is two methods. A public method that initializes rnd ccalculations. And a "Button_Click" private method that once the user submits their guess the "game" sees if it matches and displays if they are right or wrong including how long it took them to guess corectly.
class MainClass
{
public static void Main (string[] args)
{
Random rnd = new Random();
int ans = rnd.Next(1,10);
Console.WriteLine("Pick an integer between 1 and 10");
var num1 = Console.ReadLine();
int v1 = Convert.ToInt32(num1);
if(v1 == ans)
{
int count = 1;
Console.WriteLine($"{v1} is correct. You Win!");
Console.WriteLine($"It took you to {count} gues find the number {ans}." );
}
else
{
if(v1<ans){
Console.WriteLine("To high");
}
else
Console.WriteLine("To low");
Console.WriteLine("Pick an interger between 1 and 10");
Console.WriteLine($"{v1} isn't correct. Try again!");
var num2 = Console.ReadLine();
int v2 = Convert.ToInt32(num2);
if(v2 == ans)
{
int count = 2;
Console.WriteLine($"{v2} is correct. You Win!");
Console.WriteLine($"It took you {count} gueses to find the number {ans}" );
}
else
{
if(v1<ans){
Console.WriteLine("To high");
}
else
Console.WriteLine("To low");
Console.WriteLine("Pick an interger between 1 and 10");
Console.WriteLine($"{v2} isn't correct. Try again!");
var num3 = Console.ReadLine();
int v3 = Convert.ToInt32(num3);
if(v3 == ans)
{
int count = 3;
Console.WriteLine($"{v3} is correct. You Win!");
Console.WriteLine($"It took you {count} gueses to find the number {ans}" );
}
else
{
if(v1<ans){
Console.WriteLine("To high");
}
else
Console.WriteLine("To low");
Console.WriteLine("Pick an interger between 1 and 10");
Console.WriteLine($"{v3} isn't correct");
}
Console.WriteLine($"You Lose! The correct number is {ans}. ");
}
}
}
}
So many things happening here, we would need a lot of time to explain.
Let me try to get to the basics. A class in C# is a blueprint of state and behavior.
In that term, you could model your code as a GameRound
public class GameRound {
private int noOfTries;
private int maxNoOfTries;
private int correctNumber;
private bool success;
public bool HasRoundEnded { get {
return maxNoOfTries == noOfTries;
}
}
public bool Success { get {
return success;
}
}
public GameRound() {
Random rnd = new Random();
int ans = rnd.Next(1,10);
correctNumber = ans;
}
public bool GuessSolution(int guess) {
if (guess == correctNumber) {
this.success = true;
} else {
this.success = false;
maxNoOfTries++;
}
return this.success;
}
You can see that most of your logic, was included in a class. I'll leave it to you to figure out how to use it.
You'll notice that there is no dependency on Console.Write or read. You can use that code in a console application or a UI or even a website. This happens because we separate the concerns of the class to only model a game round.
Another piece of advice, is for you to use a while loop with the class provided, to solve your problem in the console application. That way you'll understand how to use repeating structures and objects of classes.

How to implement in console quiz the removal of an option every t2 seconds (t2<T)

I am new to C# and I am trying to build console quiz.
Here is my problem:
For every option removed I have to reduce one option (let's say the total points are 100).
If one option is removed I need to reduce the total points by 25 (i.e now the total points will be 75).
JSON data:
{
"question": [
{
"level": "Easy",
"cat": "sports",
"description": "Who is the Highest run getter in 2019",
"Option1": "Rohit Sharma",
"Option2": "Virat Kohli",
"Option3": "Kl Rahul",
"Option4": "S Dhawan",
"Answer":"1"
}]
}
Program:
using System;
using System.Timers;
namespace CProgram
{
class EasyQuestion
{
private string mLevel;
private string mCat;
private string mDescription;
private string mOption1;
private string mOption2;
private string mOption3;
private string mOption4;
private string mAnswer;
public string MDescription { get => mDescription; }
public string MOption1 { get => mOption1; }
public string MOption2 { get => mOption2; }
public string MOption3 { get => mOption3; }
public string MOption4 { get => mOption4; }
public string MAnswer { get => mAnswer; }
public string MLevel { get => mLevel; }
public string MCat { get => mCat; }
public static int sQcount=1;
public int sPlayerScore=0;
public int mNoOfQuesAnswerd=0;
static Timer questionTimer = new Timer(60000) ;
private static void QuestionTimer_Elapsed(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Time up!");
System.Console.WriteLine("Lets Move on to Next Question");
questionTimer.Stop();
}
public EasyQuestion(string level,string cat,string description,string Option1,string Option2,string Option3,string Option4,string Answer)
{
this.mLevel=level;
this.mCat=cat;
this.mDescription=description;
this.mOption1=Option1;
this.mOption2=Option2;
this.mOption3=Option3;
this.mOption4=Option4;
this.mAnswer=Answer;
}
public EasyQuestion()
{
}
public void AskEasyQues(EasyQuestion easyQuestion)
{
System.Console.WriteLine("Here is Your:"+sQcount+" Question:");
System.Console.WriteLine("***********************************");
System.Console.WriteLine("Question is of The Category:"+easyQuestion.MCat);
System.Console.WriteLine("***********************************");
System.Console.WriteLine(easyQuestion.MDescription);
System.Console.WriteLine("--------------------------------------");
System.Console.WriteLine("1:"+easyQuestion.MOption1+" "+"2:"+easyQuestion.MOption2);
System.Console.WriteLine();
System.Console.WriteLine("3:"+easyQuestion.MOption3+" "+"4:"+easyQuestion.MOption4);
System.Console.WriteLine();
questionTimer.Elapsed += QuestionTimer_Elapsed;
questionTimer.Enabled = true;
questionTimer.Start();
System.Console.WriteLine("Enter your Choice:");
/*for (int a = 60; a >= 0; a--)
{
Console.Write("\rGenerating Preview in {0:00}", a);
System.Threading.Thread.Sleep(1000);
} */
string ans=Console.ReadLine();
if(ans==easyQuestion.MAnswer)
{
questionTimer.Stop();
mNoOfQuesAnswerd++;
System.Console.WriteLine();
System.Console.WriteLine("------Well Played Champion!!!!!!-----");
sPlayerScore=sPlayerScore+100;
}
else
{
System.Console.WriteLine();
System.Console.WriteLine("------Wrong Choice Lets Move On--------");
}
System.Console.WriteLine();
System.Console.WriteLine("Press any Key To Continue For Next Question");
Console.ReadLine();
System.Console.WriteLine();
System.Console.WriteLine("----------------------------");
sQcount=sQcount+1;
Console.Clear();
}
}
}
I have a timer of 60 seconds and I have to remove an option every 15 seconds.
Here; I wrote this for you to show you why you can't easily do what you're asking:
static void Main(string[] args)
{
string[] answers = new[] { "answer one", "answer two", "answer three", "answer four" };
Random r = new Random();
int rightAnswer = 2;
Console.Write("\r" + string.Join(", ", answers));
for (int i = 1; i < 60; i++)
{
if (i % 15 == 0)
{
//randomly remove an answer that is not the right one
int a = r.Next(answers.Length);
while (a == rightAnswer || answers[a][0] == ' ') // dont remove the right answer! dont pick an answer that is already blanked
a = r.Next(answers.Length);
answers[a] = new string(' ', answers[a].Length); //replace answer with spaces
//return to the start of the line and overwrite
Console.Write("\r" + string.Join(", ", answers));
}
System.Threading.Thread.Sleep(100);
}
Console.Write("\nQuit");
}
It "works" in that it will remove one option every 1.5 seconds (if you want 15, extend the sleep) but the question cannot be answered on the console. As soon as you put a ReadLine() in there to get the answer, the program will halt waiting at that point until the user puts the answer in. You can take this and work out some other super complicated way of getting the answer in, such as opening a listening port and having the user telnet into the program and submit their answer that way etc...
But truly; have a play and see what me and Chris are saying and then do it in a windows GUI
Timers and consoles do not mix that well. Or really at all. In Console usually you go from one Blocking Input request to the next (ReadLine() and ReadKey()), with the odd phase of processing in between.
It is possible to poll input without blocking in a console, but that is a pretty advanced topic. And if you ever need to do that, chances are you should not have a console programm in the first place.
The rest is just clear+reprint or setting the cursor back and overwriting. Personally I prefer the clean+rewrite method for such a case.
Counting of the time can be done with DateTime.Now and .AddSeconds(). But I can only repeat, that with Windows Forms or another GUI this would be incredibly trivial. Would be just adding a timer and setting one button to hidden.

How to run two while loops?

I'm pretty new to C# and coding in general so it is hard for me to explain
and this might be something simple.
The program I am trying to make changes values in the game (Assault Cube).
Without the inner while loops, it just changes the values, I would like them to loop.
I have one outer while loop with multiple loops inside.
The inner while loops are there to loop the function but it stops the outer while loop.
I would like multiple inner loops to run along with the outer one.
I have tried what feels like everything. I tried Booleans, Breaks, Returns.
But nothing I have tried has fixed my problem. It may not be because they don't work, it may just be me using them wrong.
while (true) //outer loop
{
Console.Title = "SlavScripts";
int address1 = LocalPlayer + Health;
int address2 = LocalPlayer + Armor;
string Player = "";
Console.WriteLine("---SlavScripts v2.0---");
Console.WriteLine("");
Console.WriteLine("[1] Player Options");
Console.WriteLine("");
string answer = "";
answer = Console.ReadLine();
if (answer == "1")
{
Console.WriteLine("--Player Options--");
Console.WriteLine("");
Console.WriteLine("[1] Godmode");
Console.WriteLine("[2] Armor");
Console.WriteLine("");
Player = Console.ReadLine();
if (Player == "1")
{
Console.WriteLine("Godmode Enabled");
Console.WriteLine("");
while (true)
{
vam.WriteInt32((IntPtr)address1, 6969); //value to loop.
}
}
else if (Player == "2")
{
Console.WriteLine("Infinite Armor Enabled");
Console.WriteLine("");
while (true)
{
vam.WriteInt32((IntPtr)address2, 6969); //value to loop.
}
}
}
}
(full code: https://pastebin.com/bBcBPYs6)
Expected:
I enter the corresponding digit to activate the function
The function that was activated loops, and original text appears which allows me to navigate to another function.
Actual:
I enter the corresponding digit to activate the function.
The function activates and loops, but does not show opening text and disallows my to type further.
Think about what is happening in your code. Each instruction in your code is executing one after the other (superficially thinking - this might not be exactly true at assembly execution level but you get the idea). For example the line if (answer == "1") will only executed when the line before it (that is answer = Console.ReadLine();) completes its execution.
When you create the "outer while loop" (as you called), everything inside the loop will execute 1 instruction at a time following the order they are written and when the last instruction inside the loop is executed, the execution will jump back to the first line of code inside the loop.
If you put another while loop inside the "outer one", and say it will not have an exit condition (by declaring while(true) you are creating a loop that will never stop executing its embedded statements). Therefore when the execution reaches one of these while(true) loops it will be trapped inside them looping its embedded statements forever.
What I described is how instructions are executed. The other part you need to know is what is a thread. You can think of a thread as an isolated machine where every code is executed. When you don't deal with threads directly in your code, the compiler will automatically asks the operating system to create a thread to run your code at - this is usually referred to as the main thread.
So, what you actually need in your code is to inform the operating system to put the execution of each one of those "inner while(true)" loops inside a thread other than the main one and leaving the main thread to execute only code that can sequentially be executed.
You can learn how to use threads in your application here.
And as a side-note: that is probably not what you want to create a loop spinning code every cycle. You should consider pausing the execution inside those "inner loops" by putting the thread that is executing it to sleep for some time each time it iterates through (you can do this by just calling System.Threading.Thread.Sleep(100) - it will pause the thread execution by 100 milliseconds and will save some cpu execution time).
As per your expectation,
Please find the sample code snippet for getting user input continuously - achieved using Task in C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleTaskStop
{
class Program
{
static public void SetValue(string address, int value)
{
while (!_cancelled)
{
//vam.WriteInt32((IntPtr)address1, 6969); //value to loop.
}
Console.WriteLine(address + " - completed");
}
static bool _cancelled = false;
static void Main(string[] args)
{
while (true) //outer loop
{
Console.Title = "SlavScripts";
Console.WriteLine("---SlavScripts v2.0---");
Console.WriteLine("");
Console.WriteLine("[1] Player Options");
Console.WriteLine("");
string answer = "";
answer = Console.ReadLine();
_cancelled = false;
if (answer == "1")
{
var acceptUserInput = Task.Factory.StartNew(AcceptUserInput);
acceptUserInput.Wait();
}
}
}
private static void AcceptUserInput()
{
// Task to perform the value setting in the
Task computationTask = null;
Console.WriteLine("Enter Player Input");
Console.WriteLine("1 God Mode");
Console.WriteLine("2 Armour Mode");
Console.WriteLine("Press Esc to cancel");
var key = Console.ReadKey(true);
while (key.Key != ConsoleKey.Escape)
{
if (key.Key == ConsoleKey.D1 || key.Key == ConsoleKey.NumPad1 )
{
Console.WriteLine("Godmode Enabled");
Console.WriteLine("");
_cancelled = true;
if (computationTask != null)
{
computationTask.Wait(new System.Threading.CancellationToken());
}
_cancelled = false;
computationTask = Task.Factory.StartNew(() => SetValue("data1", 6979));
}
else if (key.Key == ConsoleKey.D2 || key.Key == ConsoleKey.NumPad2)
{
Console.WriteLine("Infinite Armor Enabled");
Console.WriteLine("");
_cancelled = true;
if (computationTask != null)
{
computationTask.Wait(new System.Threading.CancellationToken());
}
_cancelled = false;
computationTask = Task.Factory.StartNew(() => SetValue("data2", 6979));
}
key = Console.ReadKey(true);
}
_cancelled = true;
Console.Write("Computation was cancelled");
}
}
}
Thanks to everyone who replied!
Multi-Threading sounds alot harder than it actually is and it also simplified my code alot!
Still have to add Thread.Sleep's :D
Cheers!
New & Improved - pastebin.com/qN7ci0By

C# clickclickclick console game - correct button reading

I'm trying to create a simple clickclickclick game for the console in c# with Visual Studio. The only thing that is not working yet, is how the console can count presses of any button on the keyboard. The problem with the existing code is that the statement stays true even after releasing the button.
using System;
using System.Diagnostics;
public class clickclickclickgame
{
public static void Main()
{
Stopwatch timer = new Stopwatch();
int amountofpresses = 0;
timer.Start();
while (timer.ElapsedMilliseconds < 2000)
{
if (Console.KeyAvailable == true) // this is where my question is about.
{
timer.Restart();
amountofpresses++;
}
timer.Stop();
Console.WriteLine(amountofpresses);
}
}
Replace it by:
int i = 0;
Stopwatch sw = new Stopwatch();
sw.Start();
while(true)
{
Console.ReadKey(true);//True makes it so you don't see the key in Console
if(sw.ElapsedMilliseconds < 2000)
{
i++;
sw.Restart();
}
else
{
sw.Stop();
break;
}
}
Console.WriteLine("Pressed amount: " + i);
Console.ReadKey();
This works like you wan't the only problem is it checks the state after ReadKey witch means if I'm game over I have to press once more for my highscore. btw 2000 ms is 2 seconds, quite high for this game :)
To help you on your way to increasing difficulty id say make 2000 a variable and subtract score * 2 (just an example) every key press. That way it gets harder and harder.

C# How to end an infinite loop that also recieves user input with the press of Escape in Console Application

I have just started learning C# and I'm practicing some basic coding in the console application and I'm trying to make a program that adds two integers together by using an infinite loop and a method. But I want to be able to end the loop by pressing escape but the problem is, is when I press any key besides escape after the loop has completed for the first time, the program crashes. It says "Input string was not in a correct format." Any suggestions would be greatly appreciated!
Thanks!
namespace ConsoleApplication2
{
class Program
{
static void Addxy(int x, int y)
{
Console.WriteLine(x+y);
}
static void Main(string[] args)
{
while(true)
{
Addxy(Convert.ToInt32(Console.ReadLine()), Convert.ToInt32(Console.ReadLine()));
ConsoleKeyInfo end;
end = Console.ReadKey();
if (end.Key == ConsoleKey.Escape)
break;
else
{
}
}
}
}
}
You have two Console.ReadLine() before Console.ReadKey(). So the first two inputs should be numerals followed by the Escape key to terminate the program. Remember to press Enter after the first two numeral entry.
You could use break to end the loop, but a more elegant way would be to define a variable that determines if the loop should keep running. Here is my suggestion for your problem:
static void Main(string[] args)
{
bool keepLooping = true;
while (keepLooping)
{
//Do your stuff here
if (Console.ReadKey().Key == ConsoleKey.Escape)
{
keepLooping = false;
}
}
}
or an even shorter version if you prefer that:
static void Main(string[] args)
{
bool keepLooping = true;
while (keepLooping)
{
//Do your stuff here
keepLooping = Console.ReadKey().Key != ConsoleKey.Escape;
}
}

Categories

Resources