I'm working on an assignment, where I have to make an application where you can throw 1-4 dice. The results are then added to a list.
However, if any of them is a 6, that one isn't added to the list, instead 2 additional dice are thrown.
As long as a die returns a 6, 2 more dice have to be thrown, until no die returns a 6.
Does anyone here know how to solve this? My programming skills are really basic, and I haven't used them much since last year.
for (int i = 0; i < qty; i++)
{
int diceNr = RollDice(random);
dicelist.Add(diceNr);
Console.WriteLine(diceNr);
if (diceNr == 6)
{
dicelist.Remove(diceNr);
Console.WriteLine("You got a six, that means you get 2 extra throws!");
for (int x = 0; x < 2; x++)
{
diceNr = RollDice(random);
dicelist.Add(diceNr);
}
You can try using a while loop as follows:
int i = quantity; #initialize a variable to your maximum number of throws
while(i > 0){ #until you have throws yet
i--; #this is the equivalent of one throw
int diceNr = RollDice(random);
dicelist.Add(diceNr);
Console.WriteLine(diceNr);
if(diceNr == 6){
Console.WriteLine("You got a six, that means you get 2 extra throws!");
i = i + 2; #add your bonus throws
}
}
You can use a while loop that exits once you are out of rolls. If you roll a six, add two more to the remaining quantity otherwise add the rolled value to the list.
int qty = 6;
// decrement remaining quantity each iteration
// until zero rolls remain
while(qty-- > 0)
{
int diceNr = RollDice(random);
Console.WriteLine(diceNr);
if (diceNr == 6)
{
// rolled six, add two more rolls
Console.WriteLine("You got a six, that means you get 2 extra throws!");
qty += 2;
}
else
{
// not six, add to list
dicelist.Add(diceNr);
}
}
// output all the non-six values
var allRolls = string.Join(",", diceList);
Console.WriteLine("All rolls: " + allRolls);
You could also do this with recursion (though I don't recommend it for this problem):
private static void Play(List<int> diceList, Random random, int rolls)
{
// base case, no rolls remain
if (rolls == 0)
return;
int diceNr = RollDice(random);
Console.WriteLine(diceNr);
if (diceNr == 6)
{
Console.WriteLine("You got a six, that means you get 2 extra throws!");
// add one to rolls since we are "reusing" this slot
// adding two would give us one more roll than we really want
rolls++
}
else
{
diceList.Add(diceNr);
// decrement rolls by one
rolls--;
}
// recursively call Play with updated roll count
// which is either original + 1 OR original - 1
Play(diceList, random, rolls);
}
private static int RollDice(Random r)
{
return r.Next(1, 7);
}
public static void Main()
{
var diceList = new List<int>();
var random = new Random();
int qty = 6;
Play(diceList, random, qty);
// output all the non-six values
var allRolls = string.Join(",", diceList);
Console.WriteLine("All rolls: " + allRolls);
}
My (readable) solution would look like:
class Program
{
static void Main(string[] args)
{
var game = new Game(4);
game.Play();
}
}
public class Game
{
private readonly int _noOfDices;
private readonly List<int> _result;
private readonly Random _random = new Random();
public Game(int noOfDices)
{
_noOfDices = noOfDices;
_result = new List<int>();
}
public void RollDice()
{
var no = _random.Next(1, 7);
if (no == 6)
{
RollDice();
RollDice();
}
else
{
_result.Add(no);
}
}
public void Play()
{
for (var i = 1; i <= _noOfDices; i++)
{
RollDice();
}
Console.WriteLine($"Output: {string.Join(',',_result)}");
}
}
Output:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pi_szamlalo
{
class Program
{
static void Main(string[] args)
{
int limit = 100;
List<int> prime = new List<int>();
prime.Add(2);
for (int number = 3; number <= limit; number = number + 1)
{
foreach(int prime2 in prime) //here is the error at "in"
{
if (number % prime2 == 0)
{
Console.WriteLine(number + " is not a prime");
}
else
{
Console.WriteLine(number + " is a prime");
prime.Add(number);
}
}
}
Console.ReadKey();
}
}
}
The idea would be that the program tries to divide all the whole numbers to the limit with the already known primes. If the remainder is zero in one of the cases, the program will move on to the next.
However, if the program could not find a prime number in the list of primes, it will add it to the list, and say it's a prime.
So, in Visual Studio 2013, there are no sign of errors, except the crash.
Here is the error message The Error message
It is in hungarian, it says something like this:
The list has been modified, it may be that the operation cannot be runned.
Here's a working example taking into account my comment above: you need to run through the whole list of primes before deciding if a number is prime - currently you're writing whether it is or isn't a prime each time you test it: you can only determine that when you've finished testing.
class Program
{
static void Main(string[] args)
{
int limit = 100;
List<int> prime = new List<int>();
prime.Add(2);
for (int number = 3; number <= limit; number = number + 1)
{
bool isPrime = true;
foreach (int prime2 in prime)
{
if (number % prime2 == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
Console.WriteLine(number + " is a prime");
prime.Add(number);
}
else
{
Console.WriteLine(number + " is not a prime");
}
}
}
}
I am trying to find the smallest zero-one number (a number that composes of only zeroes and ones i.e. 11001) that is a multiple of the number input through STDIN, but the program crashes when i input 99999, and this is one of the test cases that should work as the input value should range between 1 and 100000, the program crashes at
ulong.Parse(binary); in Getmultiple(ulong n).
What change should i make for it to work ?
class Program
{
static void Main(string[] args)
{
while (true)
{
string s = Console.ReadLine();
ulong l;
ulong.TryParse(s, out l);
Console.WriteLine(Getmultiple(l));
}
}
private static ulong Getmultiple(ulong n)
{
for (ulong i = 1; ; i++)
{
String binary = Convert.ToString((long) i, 2);
ulong no = ulong.Parse(binary);
if (no % n == 0)
{
return no;
}
}
}
}
ERROR MESSAGE:
An unhandled exception of type 'System.OverflowException' occurred in
mscorlib.dll
Additional information: Value was either too large or too
small for a UInt64.
You're overflowing the unsigned long variable, meaning that there isn't a multiple of 99999 fitting your requirements that's also less than 2^64. If you refactor your code to use BigInteger instead of unsigned long, then you won't have the overflow problem. It might take some time for your algorithm to find the solution, though...
I'd also make this change, to sanity check your input:
BigInteger l;
if(BigInteger.TryParse(s, out l))
Console.WriteLine(Getmultiple(l));
else
Console.WriteLine("Not a valid integer");
looks like you are getting too large of a number for ulong no, you can use ulong.TryParse to avoid this.
private static ulong Getmultiple(ulong n)
{
for (ulong i = 1; ; i++)
{
String binary = Convert.ToString((long)i,2);
ulong no = 0;
if (ulong.TryParse(binary,out no))
{
if (no % n == 0)
{
return no;
}
}
else
{
return 0;
}
}
}
I chose to return 0 for an invalid result.
Using BigInteger works!! Some testcases that failed with ulong are now
working, thanks Esoteric Screen Name!
static void Main(string[] args)
{
while (true)
{
string s = Console.ReadLine();
BigInteger l;
if (BigInteger.TryParse(s, out l))
Console.WriteLine(Getmultiple(l));
else
Console.WriteLine("Not a valid integer");
}
}
private static BigInteger Getmultiple(BigInteger n)
{
for (BigInteger i = 1; ; i++)
{
String binary = Convert.ToString((long) i, 2);
BigInteger no = BigInteger.Parse(binary);
if (no % n == 0)
{
return no;
}
}
}
Just a beginner here, I have a console app that calculates the charge payable for a car park that's working fine, at least until you put in a value that's a positive number.
We've just started using multiple methods and my problem is that the method I'm passing the 'hours' value to use an If else statement to get the charge value but as the method passes back a double I'm a little confused as to what would be best practice for the else.
Ideally I'd want to be able to enter an invalid value (a negative integer) for 'hours' and the program would return an error message and bounce back to the start of the program again.
To get that effect now I'm setting the return on the else to change the value passed back to 0 and then using an if statement in the main method to deal with the situation where 'hours' = 0. Then I'm using a goto to do it right now but I'm not sure that's best practice. If it is, great, but if there's a better way I'd prefer not to be relying on a something that's messier.
Cheers for any and all help.
class Program
{
static void Main(string[] args)
{
double charge = 0;
double hours = 0;
string reg;
start:
while (hours != -999)
{
Console.Write("\nEnter hours : ");
hours = Convert.ToDouble(Console.ReadLine());
charge = CalcCharge(hours);
if (charge == 0)
{
Console.Write("Invalid hour value. Please try again.\n");
goto start;
}
Console.Write("\nEnter reg : ");
reg = Console.ReadLine();
if (reg == "Sligo")
charge = charge - ((charge / 100) * 10);
if (charge > 100)
charge = 100;
Console.Write("\nThe charge is ${0:f2}.", charge);
Console.ReadLine();
}
}
static double CalcCharge(double hours)
{
double result;
if (hours > 0 && hours < 7)
{
result = hours * 2;
return result;
}
if (hours >= 7 && hours <= 10)
{
result = hours * 3;
return result;
}
if (hours >= 11 && hours <= 15)
{
result = hours * 4;
return result;
}
if (hours > 15)
{
result = hours * 3;
return result;
}
else
{
return 0;
}
}
}
You should probably not use an else but instead throw an exception. An ArgumentException is probably correct to use here. Catch the exception instead of checking the return value:
public static void Main(string[] args)
{
//your code
try
{
charge = CalcCharge(hours);
}
catch(ArgumentException)
{
Console.Write("Invalid hour value. Please try again.\n");
continue;
}
...
static double CalcCharge(double hours)
{
//Your code
throw new ArgumentException("hours");
}
Also, avoid using goto as it's bad practice and can lead to very messy, spaghetti gross code. If you look at the example I use a continue, which basically says "go back to the start of the loop".
I would probably change your function to this:
static double CalcCharge(double hours)
{
double result = 0;
if (hours > 0 && hours < 7)
{
result = hours * 2;
}
else if ((hours >= 7 && hours <= 10) || hours > 15)
{
result = hours * 3;
}
else if (hours >= 11 && hours <= 15)
{
result = hours * 4;
}
else
{
throw new ArgumentOutOfRangeException("there was a problem!");
}
return result;
}
This is a little more concise and it lets you catch the exception in the parent function if something goes wrong.
When building a Windows Console App in C#, is it possible to write to the console without having to extend a current line or go to a new line? For example, if I want to show a percentage representing how close a process is to completion, I'd just like to update the value on the same line as the cursor, and not have to put each percentage on a new line.
Can this be done with a "standard" C# console app?
If you print only "\r" to the console the cursor goes back to the beginning of the current line and then you can rewrite it. This should do the trick:
for(int i = 0; i < 100; ++i)
{
Console.Write("\r{0}% ", i);
}
Notice the few spaces after the number to make sure that whatever was there before is erased.
Also notice the use of Write() instead of WriteLine() since you don't want to add an "\n" at the end of the line.
You can use Console.SetCursorPosition to set the position of the cursor and then write at the current position.
Here is an example showing a simple "spinner":
static void Main(string[] args)
{
var spin = new ConsoleSpinner();
Console.Write("Working....");
while (true)
{
spin.Turn();
}
}
public class ConsoleSpinner
{
int counter;
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("/"); counter = 0; break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); break;
}
Thread.Sleep(100);
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
Note that you will have to make sure to overwrite any existing output with new output or blanks.
Update: As it has been criticized that the example moves the cursor only back by one character, I will add this for clarification: Using SetCursorPosition you may set the cursor to any position in the console window.
Console.SetCursorPosition(0, Console.CursorTop);
will set the cursor to the beginning of the current line (or you can use Console.CursorLeft = 0 directly).
So far we have three competing alternatives for how to do this:
Console.Write("\r{0} ", value); // Option 1: carriage return
Console.Write("\b\b\b\b\b{0}", value); // Option 2: backspace
{ // Option 3 in two parts:
Console.SetCursorPosition(0, Console.CursorTop); // - Move cursor
Console.Write(value); // - Rewrite
}
I've always used Console.CursorLeft = 0, a variation on the third option, so I decided to do some tests. Here's the code I used:
public static void CursorTest()
{
int testsize = 1000000;
Console.WriteLine("Testing cursor position");
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < testsize; i++)
{
Console.Write("\rCounting: {0} ", i);
}
sw.Stop();
Console.WriteLine("\nTime using \\r: {0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
int top = Console.CursorTop;
for (int i = 0; i < testsize; i++)
{
Console.SetCursorPosition(0, top);
Console.Write("Counting: {0} ", i);
}
sw.Stop();
Console.WriteLine("\nTime using CursorLeft: {0}", sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
Console.Write("Counting: ");
for (int i = 0; i < testsize; i++)
{
Console.Write("\b\b\b\b\b\b\b\b{0,8}", i);
}
sw.Stop();
Console.WriteLine("\nTime using \\b: {0}", sw.ElapsedMilliseconds);
}
On my machine, I get the following results:
Backspaces: 25.0 seconds
Carriage Returns: 28.7 seconds
SetCursorPosition: 49.7 seconds
Additionally, SetCursorPosition caused noticeable flicker that I didn't observe with either of the alternatives. So, the moral is to use backspaces or carriage returns when possible, and thanks for teaching me a faster way to do this, SO!
Update: In the comments, Joel suggests that SetCursorPosition is constant with respect to the distance moved while the other methods are linear. Further testing confirms that this is the case, however constant time and slow is still slow. In my tests, writing a long string of backspaces to the console is faster than SetCursorPosition until somewhere around 60 characters. So backspace is faster for replacing portions of the line shorter than 60 characters (or so), and it doesn't flicker, so I'm going to stand by my initial endorsement of \b over \r and SetCursorPosition.
You can use the \b (backspace) escape sequence to backup a particular number of characters on the current line. This just moves the current location, it does not remove the characters.
For example:
string line="";
for(int i=0; i<100; i++)
{
string backup=new string('\b',line.Length);
Console.Write(backup);
line=string.Format("{0}%",i);
Console.Write(line);
}
Here, line is the percentage line to write to the console. The trick is to generate the correct number of \b characters for the previous output.
The advantage of this over the \r approach is that if works even if your percentage output is not at the beginning of the line.
\r is used for these scenarios.
\r represents a carriage return which means the cursor returns to the start of the line.
That's why Windows uses \n\r as its new line marker.
\n moves you down a line, and \r returns you to the start of the line.
I just had to play with the divo's ConsoleSpinner class. Mine is nowhere near as concise, but it just didn't sit well with me that users of that class have to write their own while(true) loop. I'm shooting for an experience more like this:
static void Main(string[] args)
{
Console.Write("Working....");
ConsoleSpinner spin = new ConsoleSpinner();
spin.Start();
// Do some work...
spin.Stop();
}
And I realized it with the code below. Since I don't want my Start() method to block, I don't want the user to have to worry about writing a while(spinFlag) -like loop, and I want to allow multiple spinners at the same time I had to spawn a separate thread to handle the spinning. And that means the code has to be a lot more complicated.
Also, I haven't done that much multi-threading so it's possible (likely even) that I've left a subtle bug or three in there. But it seems to work pretty well so far:
public class ConsoleSpinner : IDisposable
{
public ConsoleSpinner()
{
CursorLeft = Console.CursorLeft;
CursorTop = Console.CursorTop;
}
public ConsoleSpinner(bool start)
: this()
{
if (start) Start();
}
public void Start()
{
// prevent two conflicting Start() calls ot the same instance
lock (instanceLocker)
{
if (!running )
{
running = true;
turner = new Thread(Turn);
turner.Start();
}
}
}
public void StartHere()
{
SetPosition();
Start();
}
public void Stop()
{
lock (instanceLocker)
{
if (!running) return;
running = false;
if (! turner.Join(250))
turner.Abort();
}
}
public void SetPosition()
{
SetPosition(Console.CursorLeft, Console.CursorTop);
}
public void SetPosition(int left, int top)
{
bool wasRunning;
//prevent other start/stops during move
lock (instanceLocker)
{
wasRunning = running;
Stop();
CursorLeft = left;
CursorTop = top;
if (wasRunning) Start();
}
}
public bool IsSpinning { get { return running;} }
/* --- PRIVATE --- */
private int counter=-1;
private Thread turner;
private bool running = false;
private int rate = 100;
private int CursorLeft;
private int CursorTop;
private Object instanceLocker = new Object();
private static Object console = new Object();
private void Turn()
{
while (running)
{
counter++;
// prevent two instances from overlapping cursor position updates
// weird things can still happen if the main ui thread moves the cursor during an update and context switch
lock (console)
{
int OldLeft = Console.CursorLeft;
int OldTop = Console.CursorTop;
Console.SetCursorPosition(CursorLeft, CursorTop);
switch (counter)
{
case 0: Console.Write("/"); break;
case 1: Console.Write("-"); break;
case 2: Console.Write("\\"); break;
case 3: Console.Write("|"); counter = -1; break;
}
Console.SetCursorPosition(OldLeft, OldTop);
}
Thread.Sleep(rate);
}
lock (console)
{ // clean up
int OldLeft = Console.CursorLeft;
int OldTop = Console.CursorTop;
Console.SetCursorPosition(CursorLeft, CursorTop);
Console.Write(' ');
Console.SetCursorPosition(OldLeft, OldTop);
}
}
public void Dispose()
{
Stop();
}
}
Explicitly using a Carrage Return (\r) at the beginning of the line rather than (implicitly or explicitly) using a New Line (\n) at the end should get what you want. For example:
void demoPercentDone() {
for(int i = 0; i < 100; i++) {
System.Console.Write( "\rProcessing {0}%...", i );
System.Threading.Thread.Sleep( 1000 );
}
System.Console.WriteLine();
}
public void Update(string data)
{
Console.Write(string.Format("\r{0}", "".PadLeft(Console.CursorLeft, ' ')));
Console.Write(string.Format("\r{0}", data));
}
From the Console docs in MSDN:
You can solve this problem by setting
the TextWriter.NewLine property of the
Out or Error property to another line
termination string. For example, the
C# statement, Console.Error.NewLine =
"\r\n\r\n";, sets the line termination
string for the standard error output
stream to two carriage return and line
feed sequences. Then you can
explicitly call the WriteLine method
of the error output stream object, as
in the C# statement,
Console.Error.WriteLine();
So - I did this:
Console.Out.Newline = String.Empty;
Then I am able to control the output myself;
Console.WriteLine("Starting item 1:");
Item1();
Console.WriteLine("OK.\nStarting Item2:");
Another way of getting there.
This works if you want to make generating files look cool .
int num = 1;
var spin = new ConsoleSpinner();
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("");
while (true)
{
spin.Turn();
Console.Write("\r{0} Generating Files ", num);
num++;
}
And this is the method that i got from some answer below and modified it
public class ConsoleSpinner
{
int counter;
public void Turn()
{
counter++;
switch (counter % 4)
{
case 0: Console.Write("."); counter = 0; break;
case 1: Console.Write(".."); break;
case 2: Console.Write("..."); break;
case 3: Console.Write("...."); break;
case 4: Console.Write("\r"); break;
}
Thread.Sleep(100);
Console.SetCursorPosition(23, Console.CursorTop);
}
}
Here's another one :D
class Program
{
static void Main(string[] args)
{
Console.Write("Working... ");
int spinIndex = 0;
while (true)
{
// obfuscate FTW! Let's hope overflow is disabled or testers are impatient
Console.Write("\b" + #"/-\|"[(spinIndex++) & 3]);
}
}
}
If you want update one line, but the information is too long to show on one line, it may need some new lines. I've encountered this problem, and below is one way to solve this.
public class DumpOutPutInforInSameLine
{
//content show in how many lines
int TotalLine = 0;
//start cursor line
int cursorTop = 0;
// use to set character number show in one line
int OneLineCharNum = 75;
public void DumpInformation(string content)
{
OutPutInSameLine(content);
SetBackSpace();
}
static void backspace(int n)
{
for (var i = 0; i < n; ++i)
Console.Write("\b \b");
}
public void SetBackSpace()
{
if (TotalLine == 0)
{
backspace(OneLineCharNum);
}
else
{
TotalLine--;
while (TotalLine >= 0)
{
backspace(OneLineCharNum);
TotalLine--;
if (TotalLine >= 0)
{
Console.SetCursorPosition(OneLineCharNum, cursorTop + TotalLine);
}
}
}
}
private void OutPutInSameLine(string content)
{
//Console.WriteLine(TotalNum);
cursorTop = Console.CursorTop;
TotalLine = content.Length / OneLineCharNum;
if (content.Length % OneLineCharNum > 0)
{
TotalLine++;
}
if (TotalLine == 0)
{
Console.Write("{0}", content);
return;
}
int i = 0;
while (i < TotalLine)
{
int cNum = i * OneLineCharNum;
if (i < TotalLine - 1)
{
Console.WriteLine("{0}", content.Substring(cNum, OneLineCharNum));
}
else
{
Console.Write("{0}", content.Substring(cNum, content.Length - cNum));
}
i++;
}
}
}
class Program
{
static void Main(string[] args)
{
DumpOutPutInforInSameLine outPutInSameLine = new DumpOutPutInforInSameLine();
outPutInSameLine.DumpInformation("");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
//need several lines
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
outPutInSameLine.DumpInformation("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
outPutInSameLine.DumpInformation("bbbbbbbbbbbbbbbbbbbbbbbbbbb");
}
}
i was looking for same solution in vb.net and i found this one and it's great.
however as #JohnOdom suggested a better way to handle the blanks space if previous one is larger than current one..
i make a function in vb.net and thought someone could get helped ..
here is my code:
Private Sub sPrintStatus(strTextToPrint As String, Optional boolIsNewLine As Boolean = False)
REM intLastLength is declared as public variable on global scope like below
REM intLastLength As Integer
If boolIsNewLine = True Then
intLastLength = 0
End If
If intLastLength > strTextToPrint.Length Then
Console.Write(Convert.ToChar(13) & strTextToPrint.PadRight(strTextToPrint.Length + (intLastLength - strTextToPrint.Length), Convert.ToChar(" ")))
Else
Console.Write(Convert.ToChar(13) & strTextToPrint)
End If
intLastLength = strTextToPrint.Length
End Sub
I was doing a search for this to see if the solution I wrote could be optimised for speed. What I wanted was a countdown timer, not just updating the current line.
Here's what I came up with. Might be useful to someone
int sleepTime = 5 * 60; // 5 minutes
for (int secondsRemaining = sleepTime; secondsRemaining > 0; secondsRemaining --)
{
double minutesPrecise = secondsRemaining / 60;
double minutesRounded = Math.Round(minutesPrecise, 0);
int seconds = Convert.ToInt32((minutesRounded * 60) - secondsRemaining);
Console.Write($"\rProcess will resume in {minutesRounded}:{String.Format("{0:D2}", -seconds)} ");
Thread.Sleep(1000);
}
Console.WriteLine("");
Inspired by #E.Lahu Solution, implementation of a bar progress with percentage.
public class ConsoleSpinner
{
private int _counter;
public void Turn(Color color, int max, string prefix = "Completed", string symbol = "■",int position = 0)
{
Console.SetCursorPosition(0, position);
Console.Write($"{prefix} {ComputeSpinner(_counter, max, symbol)}", color);
_counter = _counter == max ? 0 : _counter + 1;
}
public string ComputeSpinner(int nmb, int max, string symbol)
{
var spinner = new StringBuilder();
if (nmb == 0)
return "\r ";
spinner.Append($"[{nmb}%] [");
for (var i = 0; i < max; i++)
{
spinner.Append(i < nmb ? symbol : ".");
}
spinner.Append("]");
return spinner.ToString();
}
}
public static void Main(string[] args)
{
var progressBar= new ConsoleSpinner();
for (int i = 0; i < 1000; i++)
{
progressBar.Turn(Color.Aqua,100);
Thread.Sleep(1000);
}
}
Here is my take on s soosh's and 0xA3's answers.
It can update the console with user messages while updating the spinner and has an elapsed time indicator aswell.
public class ConsoleSpiner : IDisposable
{
private static readonly string INDICATOR = "/-\\|";
private static readonly string MASK = "\r{0} {1:c} {2}";
int counter;
Timer timer;
string message;
public ConsoleSpiner() {
counter = 0;
timer = new Timer(200);
timer.Elapsed += TimerTick;
}
public void Start() {
timer.Start();
}
public void Stop() {
timer.Stop();
counter = 0;
}
public string Message {
get { return message; }
set { message = value; }
}
private void TimerTick(object sender, ElapsedEventArgs e) {
Turn();
}
private void Turn() {
counter++;
var elapsed = TimeSpan.FromMilliseconds(counter * 200);
Console.Write(MASK, INDICATOR[counter % 4], elapsed, this.Message);
}
public void Dispose() {
Stop();
timer.Elapsed -= TimerTick;
this.timer.Dispose();
}
}
usage is something like this:
class Program
{
static void Main(string[] args)
{
using (var spinner = new ConsoleSpiner())
{
spinner.Start();
spinner.Message = "About to do some heavy staff :-)"
DoWork();
spinner.Message = "Now processing other staff".
OtherWork();
spinner.Stop();
}
Console.WriteLine("COMPLETED!!!!!\nPress any key to exit.");
}
}
The SetCursorPosition method works in multi-threading scenario, where the other two methods don't