I have threeI've got a few suggestions for improvements to improve your application. I would suggest using a few functions to clean up the organization of your code. This will also allow you to move some off your logic away from your main application logic which will help to remove duplicate code.
UseFirst main change I would suggest is to move away from the array of strings to represent your choices. Right now you have to hard code string throughout your application. This will make it hard to make changes in the future and could cause errors if you misspell any of the values. Instead you could either use constant variable, which would allow you to change the value in a single place. See below:
// Initial string representation
public const string Rock = "Rock";
public const string Paper = "Paper";
public const string Scissor = "Scissor";
// Later could be changed to this.
public const int Rock = 1;
public const int Paper = 2;
public const int Scissor = 3;
Instead of using const variable we can use enums to store the player's actions as well as the applications state. This will also give us a couple benefits that will be shown below. The following code will be using these enums.
public enum Action { Rock, Paper, Scissor };
public enum Result { Tie, PlayerWon, ComputerWon }
When taking input from the user, you should be prepared for an incorrect input. The quick and easy way to handle parsing inputs is to use the 'TryParse' method found on the type you are trying to convert to. We can use the 'TryParse' method for both reading the amount of rounds the player desires and their hand choice.
public static int GetDesiredRounds() {
int result;
do {
var input = Console.ReadLine();
if(Int32.TryParse(input, out result)) {
return result;
} else {
Console.WriteLine("Invalid input. Please input a number.");
}
} while(true);
}
Enums can also be parsed from a string using the 'TryParse' method. It also has an option to allow for case-insensitive parsing, which will allow the user to input either 'Rock' or 'rock' to get the same action.
public static Action GetPlayerAction() {
Action result;
do {
var input = Console.ReadLine();
// The second param tells code to ignore case of string string
if(Action.TryParse(input, true, out result)) {
return result;
} else {
Console.WriteLine("Invalid action {0}. Please input 'Rock', 'Paper' or 'Scissors'.", input);
}
} while(true);
}
Enum's have an underlying numeric type. Their values default to 0, 1, 2, 3... from the first enum value to the last {} else if(unless they are manually assigned, see enum reference below) {} else {}. You can cast an Enum to and from their underlying type. Below we have to cast the enum value to its underlying type in order to use it with the random.Next method, then we simply cast it back to an Action.
public static Action GetComputerAction() {
// Gets a random number between 0 (Action.Rock) and 3 (Action.Scissor + 1)
// This will result in the values 1 (Rock), 2 (Paper), or 3 (Scissor)
// We use (Scissor + 1) since the 'Next' method does not include the max value
return (Action)rand.Next((int)Action.Rock, (int)Action.Scissor + 1);
}
Use default: break; inWith both the player and computer's choices, we then calculate the result. With the limited options, we could simply list out all the results using nested switch statements. One thing to observe is the throwing of an Exception. This exception should be never reached, since all the enum's values were used inside the switch. The compiler will complain if you remove the exception. (For a bit more information on this behavior see the reference link below).
public static Result CalculateResult(Action player, Action computer) {
switch(player) {
case Action.Rock:
switch (computer) ) {
case Action.Rock: return Result.Tie;
case Action.Paper: return Result.ComputerWon;
case Action.Scissor: return Result.PlayerWon;
}
break;
case Action.Paper:
switch (computer) ) {
case Action.Rock: return Result.PlayerWon;
case Action.Paper: return Result.Tie;
case Action.Scissor: return Result.ComputerWon;
}
break;
case Action.Scissor:
switch (computer) ) {
case Action.Rock: return Result.ComputerWon;
case Action.Paper: return Result.PlayerWon;
case Action.Scissor: return Result.Tie;
}
break;
}
// This should never be reached
throw new Exception(string.Format("Unhandled action pair occured: {0}, {1}", player, computer) ));
}
Finally we can use our created functions to build the main body of the application. Another thing to observe is since we pulled the logic of calculating results out from the printing and point code, there is less code to repeat.
public static void Main() {
// static variable used in GetComputerChoice
rand = new Random();
int playerPoints = 0;
int computerPoints = 0;
Console.WriteLine("How many rounds would you like to play?");
int rounds = GetDesiredRounds();
for(var round = 1; round <= rounds; round++) {
Console.WriteLine("Round {0} Begins", round);
Console.WriteLine("Which hand do you choose(Rock, Paper, Scissor)? ");
var playerAction = GetPlayerAction();
Console.WriteLine("You picked: {0}", playerAction.ToString());
var computerAction = GetComputerAction();
Console.WriteLine("Your opponent picked: {0}", computerAction.ToString());
switch(CalculateResult(playerAction, computerAction)) {
case Result.PlayerWon:
Console.WriteLine("You won the round! You gained a point.");
playerPoints++;
break;
case Result.ComputerWon:
Console.WriteLine("Computer won the round! Computer gained a point.");
computerPoints++;
break;
case Result.Tie:
Console.WriteLine("Round tied. You and the computer gained a point.");
playerPoints++;
computerPoints++;
break;
}
Console.WriteLine();
}
Console.WriteLine("Results - Player {0}, Computer {1}", playerPoints, computerPoints);
if (playerPoints == computerPoints) {
Console.WriteLine("Tie Game");
} else {
bool isPlayerWinner = playerPoints > computerPoints;
Console.WriteLine("{0} won the game!", isPlayerWinner ? "Player" : "Computer");
}
}
References
Ins and Outs of enums in C#
Enums in switch statements
Demo
The full example code can be found and ran here