In my original revieworiginal review of this program I mentioned I really wanted to re-do this in OO. So I have. I recommend you read my first review to get most of the reasons behind much of the code refactoring I did.
In my original review of this program I mentioned I really wanted to re-do this in OO. So I have. I recommend you read my first review to get most of the reasons behind much of the code refactoring I did.
In my original review of this program I mentioned I really wanted to re-do this in OO. So I have. I recommend you read my first review to get most of the reasons behind much of the code refactoring I did.
Right off the bat I created two
enums
which will be helpful in the rest of the program. aGesture
enum
which holds all possible playableGestures
, and aPerformance
enum
to communicate to a player how they did during aGame
.enum Gesture { Rock = 1, Paper = 2, Scissors = 3, Spock = 4, Lizard = 5 } enum Performance { Lost = -1, Tied = 0, Won = 1 }
I created an
abstract
Player
class
which handled the specific player data. From here I created aHuman
andComputer
class
, which inherits fromPlayer
, and implemented theirGetMove
methods, because that is the only place where these two types of players differ.abstract class Player { public uint Wins { get; private set; } public uint Loses { get; private set; } public uint Ties { get; private set; } public abstract Gesture GetMove(); public string GetScoreCard() { return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]"; } public void ClearScore() { Wins = Loses = Ties = 0; } public void GiveResult(Performance performance) { switch (performance) { case Performance.Lost: Loses++; break; case Performance.Tied: Ties++; break; case Performance.Won: Wins++; break; } } } class Human : Player { public override Gesture GetMove() { Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1); return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: "); } } class Computer : Player { public override Gesture GetMove() { return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length)); } }
I created a
static
Game
class
. This one isstatic
because I didn't think it was necessary to create a newGame
object
every-time you wanted to play. Instead I implemented aPlay
method which simply handled all the necessary logic. In theGame
class
, I enclosed things like, aList
of all theGestures
, aDictionary
ofRules
(what defeats what and why). APlay
method to simulate the RPSLS, aWhoWon
method which returns who the winner between two players was, and aGetReason
method which returns the reason a particular Gesture won over another.static class Game { public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture)); private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>() { {Tuple.Create<int,int>(1,3), "Crushes"}, {Tuple.Create<int,int>(1,5), "Crushes"}, {Tuple.Create<int,int>(2,1), "Covers"}, {Tuple.Create<int,int>(2,4), "Disproves"}, {Tuple.Create<int,int>(3,2), "Cuts"}, {Tuple.Create<int,int>(3,5), "Decapitates"}, {Tuple.Create<int,int>(4,3), "Smashes"}, {Tuple.Create<int,int>(4,1), "Vaporizes"}, {Tuple.Create<int,int>(5,2), "Eats"}, {Tuple.Create<int,int>(5,4), "Poisons"} }; public static void Play(Player player1, Player player2) { Gesture p1move = player1.GetMove(); Gesture p2move = player2.GetMove(); Console.Write("Player 1 Chose "); Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green); Console.Write("Player 2 Chose "); Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green); int result = WhoWon(p1move, p2move); switch (result) { case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break; case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break; case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break; } if (result == 0) Console.WriteLine("It was a tie!"); else Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move)); } private static int WhoWon(Gesture p1move, Gesture p2move) { return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2; } private static string GetReason(Gesture winner, Gesture loser) { return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser; } }
For any general utility methods, I enclosed those in a static Utils class
static class Utils { public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ") { int g; do { Console.Write(prompt); if (int.TryParse(Console.ReadLine(), out g)) { if (g >= min && g <= max) return g; Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max); } else Console.WriteLine("That is not a number. Please try again..."); } while (true); } public static void PrintMenu(List<string> values, int baseIndex = 0) { values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value)); } public static void WriteLineColored(string text, ConsoleColor color) { var curr = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(text); Console.ForegroundColor = curr; } }
This brings me to actually using these classes in a meaningful way to actually play the game. You must create the players you want to pit against each other, this could be 2 computers, 2 humans, or a combination of either. As-long as you hold a reference to these
objects
, you also are holding onto their scorecard (assuming they didn't clear it).class Program { static void Main(string[] args) { var endGameMenugameMenu = new string[] { "Play Again""Play", "Clear Score", "Quit" }; var me = new Human(); var computer = new Computer(); var playAgain = true; do { GameUtils.PlayWriteLineColored(me"Options:", computerConsoleColor.White); ConsoleUtils.WriteLinePrintMenu("Your scorecard: " + megameMenu.GetScoreCardToList()); intswitch(Utils.PromptForRangedInt(0, result;gameMenu.Length - 1, "Choose an Option: ")) do{ { case 0: Console.WriteLineClear("Options:"); Utils.PrintMenu(endGameMenu Game.ToListPlay()me, computer); result = Utils.PromptForRangedInt(0, endGameMenu Console.LengthWriteLine("Your -scorecard: 1," "Choose+ anme.GetScoreCard() Option:+ "Environment.NewLine); if (result == 1) break; {case 1: meConsole.ClearScoreClear(); Consoleme.ClearClearScore(); Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green); } break; } while case 2: Console.Clear(result); ! playAgain = 0false; && result Console.Write("Good bye, thanks for playing!=\nPress 2any Key to contine..."); Console.ClearReadKey(true); playAgain = result == 0; break; } } while (playAgain); } }
using System;
using System.Collections.Generic;
using System.Linq;
namespace RPSLS
{
class Program
{
static void Main(string[] args)
{
var endGameMenugameMenu = new string[] { "Play Again""Play", "Clear Score", "Quit" };
var me = new Human();
var computer = new Computer();
var playAgain = true;
do
{
GameUtils.PlayWriteLineColored(me"Options:", computerConsoleColor.White);
ConsoleUtils.WriteLinePrintMenu("Your scorecard: " + megameMenu.GetScoreCardToList());
int result;
switch(Utils.PromptForRangedInt(0, gameMenu.Length - 1, "Choose an Option: do"))
{
Console.WriteLine("Options:");
Utils.PrintMenu(endGameMenu.ToList());
result =case Utils.PromptForRangedInt(0, endGameMenu.Length - 1, "Choose an Option: ");
if (result == 1)
{
me.ClearScore();
Console.Clear();
Game.Play(me, computer);
Console.WriteLine("Your scorecard: " + me.GetScoreCard() + Environment.NewLine);
break;
case 1:
Console.Clear();
me.ClearScore();
Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green);
} break;
} while case 2:
Console.Clear(result);
! playAgain = 0false;
&& result Console.Write("Good bye, thanks for playing!=\nPress 2any Key to contine...");
Console.ClearReadKey(true);
playAgain = result == 0; break;
}
} while (playAgain);
}
}
enum Gesture
{
Rock = 1,
Paper = 2,
Scissors = 3,
Spock = 4,
Lizard = 5
}
enum Performance
{
Lost = -1,
Tied = 0,
Won = 1
}
abstract class Player
{
public uint Wins { get; private set; }
public uint Loses { get; private set; }
public uint Ties { get; private set; }
public abstract Gesture GetMove();
public string GetScoreCard()
{
return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]";
}
public void ClearScore()
{
Wins = Loses = Ties = 0;
}
public void GiveResult(Performance performance)
{
switch (performance)
{
case Performance.Lost: Loses++; break;
case Performance.Tied: Ties++; break;
case Performance.Won: Wins++; break;
}
}
}
class Human : Player
{
public override Gesture GetMove()
{
Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1);
return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: ");
}
}
class Computer : Player
{
public override Gesture GetMove()
{
return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length));
}
}
static class Game
{
public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture));
private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>()
{
{Tuple.Create<int,int>(1,3), "Crushes"},
{Tuple.Create<int,int>(1,5), "Crushes"},
{Tuple.Create<int,int>(2,1), "Covers"},
{Tuple.Create<int,int>(2,4), "Disproves"},
{Tuple.Create<int,int>(3,2), "Cuts"},
{Tuple.Create<int,int>(3,5), "Decapitates"},
{Tuple.Create<int,int>(4,3), "Smashes"},
{Tuple.Create<int,int>(4,1), "Vaporizes"},
{Tuple.Create<int,int>(5,2), "Eats"},
{Tuple.Create<int,int>(5,4), "Poisons"}
};
public static void Play(Player player1, Player player2)
{
Gesture p1move = player1.GetMove();
Gesture p2move = player2.GetMove();
Console.Write("Player 1 Chose ");
Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green);
Console.Write("Player 2 Chose ");
Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green);
int result = WhoWon(p1move, p2move);
switch (result)
{
case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break;
case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break;
case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break;
}
if (result == 0)
Console.WriteLine("It was a tie!");
else
Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move));
}
private static int WhoWon(Gesture p1move, Gesture p2move)
{
return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2;
}
private static string GetReason(Gesture winner, Gesture loser)
{
return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser;
}
}
static class Utils
{
public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ")
{
int g;
do
{
Console.Write(prompt);
if (int.TryParse(Console.ReadLine(), out g))
{
if (g >= min && g <= max)
return g;
Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max);
}
else
Console.WriteLine("That is not a number. Please try again...");
} while (true);
}
public static void PrintMenu(List<string> values, int baseIndex = 0)
{
values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value));
}
public static void WriteLineColored(string text, ConsoleColor color)
{
var curr = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ForegroundColor = curr;
}
}
}
Right off the bat I created two
enums
which will be helpful in the rest of the program. aGesture
enum
which holds all possible playableGestures
, and aPerformance
enum
to communicate to a player how they did during aGame
.enum Gesture { Rock = 1, Paper = 2, Scissors = 3, Spock = 4, Lizard = 5 } enum Performance { Lost = -1, Tied = 0, Won = 1 }
I created an
abstract
Player
class
which handled the specific player data. From here I created aHuman
andComputer
class
, which inherits fromPlayer
, and implemented theirGetMove
methods, because that is the only place where these two types of players differ.abstract class Player { public uint Wins { get; private set; } public uint Loses { get; private set; } public uint Ties { get; private set; } public abstract Gesture GetMove(); public string GetScoreCard() { return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]"; } public void ClearScore() { Wins = Loses = Ties = 0; } public void GiveResult(Performance performance) { switch (performance) { case Performance.Lost: Loses++; break; case Performance.Tied: Ties++; break; case Performance.Won: Wins++; break; } } } class Human : Player { public override Gesture GetMove() { Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1); return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: "); } } class Computer : Player { public override Gesture GetMove() { return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length)); } }
I created a
static
Game
class
. This one isstatic
because I didn't think it was necessary to create a newGame
object
every-time you wanted to play. Instead I implemented aPlay
method which simply handled all the necessary logic. In theGame
class
, I enclosed things like, aList
of all theGestures
, aDictionary
ofRules
(what defeats what and why). APlay
method to simulate the RPSLS, aWhoWon
method which returns who the winner between two players was, and aGetReason
method which returns the reason a particular Gesture won over another.static class Game { public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture)); private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>() { {Tuple.Create<int,int>(1,3), "Crushes"}, {Tuple.Create<int,int>(1,5), "Crushes"}, {Tuple.Create<int,int>(2,1), "Covers"}, {Tuple.Create<int,int>(2,4), "Disproves"}, {Tuple.Create<int,int>(3,2), "Cuts"}, {Tuple.Create<int,int>(3,5), "Decapitates"}, {Tuple.Create<int,int>(4,3), "Smashes"}, {Tuple.Create<int,int>(4,1), "Vaporizes"}, {Tuple.Create<int,int>(5,2), "Eats"}, {Tuple.Create<int,int>(5,4), "Poisons"} }; public static void Play(Player player1, Player player2) { Gesture p1move = player1.GetMove(); Gesture p2move = player2.GetMove(); Console.Write("Player 1 Chose "); Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green); Console.Write("Player 2 Chose "); Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green); int result = WhoWon(p1move, p2move); switch (result) { case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break; case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break; case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break; } if (result == 0) Console.WriteLine("It was a tie!"); else Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move)); } private static int WhoWon(Gesture p1move, Gesture p2move) { return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2; } private static string GetReason(Gesture winner, Gesture loser) { return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser; } }
For any general utility methods, I enclosed those in a static Utils class
static class Utils { public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ") { int g; do { Console.Write(prompt); if (int.TryParse(Console.ReadLine(), out g)) { if (g >= min && g <= max) return g; Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max); } else Console.WriteLine("That is not a number. Please try again..."); } while (true); } public static void PrintMenu(List<string> values, int baseIndex = 0) { values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value)); } public static void WriteLineColored(string text, ConsoleColor color) { var curr = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(text); Console.ForegroundColor = curr; } }
This brings me to actually using these classes in a meaningful way to actually play the game. You must create the players you want to pit against each other, this could be 2 computers, 2 humans, or a combination of either. As-long as you hold a reference to these
objects
, you also are holding onto their scorecard (assuming they didn't clear it).class Program { static void Main(string[] args) { var endGameMenu = new string[] { "Play Again", "Clear Score", "Quit" }; var me = new Human(); var computer = new Computer(); var playAgain = true; do { Game.Play(me, computer); Console.WriteLine("Your scorecard: " + me.GetScoreCard()); int result; do { Console.WriteLine("Options:"); Utils.PrintMenu(endGameMenu.ToList()); result = Utils.PromptForRangedInt(0, endGameMenu.Length - 1, "Choose an Option: "); if (result == 1) { me.ClearScore(); Console.Clear(); Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green); } } while (result != 0 && result != 2); Console.Clear(); playAgain = result == 0; } while (playAgain); } }
using System;
using System.Collections.Generic;
using System.Linq;
namespace RPSLS
{
class Program
{
static void Main(string[] args)
{
var endGameMenu = new string[] { "Play Again", "Clear Score", "Quit" };
var me = new Human();
var computer = new Computer();
var playAgain = true;
do
{
Game.Play(me, computer);
Console.WriteLine("Your scorecard: " + me.GetScoreCard());
int result;
do
{
Console.WriteLine("Options:");
Utils.PrintMenu(endGameMenu.ToList());
result = Utils.PromptForRangedInt(0, endGameMenu.Length - 1, "Choose an Option: ");
if (result == 1)
{
me.ClearScore();
Console.Clear();
Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green);
}
} while (result != 0 && result != 2);
Console.Clear();
playAgain = result == 0;
} while (playAgain);
}
}
enum Gesture
{
Rock = 1,
Paper = 2,
Scissors = 3,
Spock = 4,
Lizard = 5
}
enum Performance
{
Lost = -1,
Tied = 0,
Won = 1
}
abstract class Player
{
public uint Wins { get; private set; }
public uint Loses { get; private set; }
public uint Ties { get; private set; }
public abstract Gesture GetMove();
public string GetScoreCard()
{
return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]";
}
public void ClearScore()
{
Wins = Loses = Ties = 0;
}
public void GiveResult(Performance performance)
{
switch (performance)
{
case Performance.Lost: Loses++; break;
case Performance.Tied: Ties++; break;
case Performance.Won: Wins++; break;
}
}
}
class Human : Player
{
public override Gesture GetMove()
{
Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1);
return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: ");
}
}
class Computer : Player
{
public override Gesture GetMove()
{
return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length));
}
}
static class Game
{
public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture));
private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>()
{
{Tuple.Create<int,int>(1,3), "Crushes"},
{Tuple.Create<int,int>(1,5), "Crushes"},
{Tuple.Create<int,int>(2,1), "Covers"},
{Tuple.Create<int,int>(2,4), "Disproves"},
{Tuple.Create<int,int>(3,2), "Cuts"},
{Tuple.Create<int,int>(3,5), "Decapitates"},
{Tuple.Create<int,int>(4,3), "Smashes"},
{Tuple.Create<int,int>(4,1), "Vaporizes"},
{Tuple.Create<int,int>(5,2), "Eats"},
{Tuple.Create<int,int>(5,4), "Poisons"}
};
public static void Play(Player player1, Player player2)
{
Gesture p1move = player1.GetMove();
Gesture p2move = player2.GetMove();
Console.Write("Player 1 Chose ");
Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green);
Console.Write("Player 2 Chose ");
Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green);
int result = WhoWon(p1move, p2move);
switch (result)
{
case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break;
case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break;
case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break;
}
if (result == 0)
Console.WriteLine("It was a tie!");
else
Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move));
}
private static int WhoWon(Gesture p1move, Gesture p2move)
{
return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2;
}
private static string GetReason(Gesture winner, Gesture loser)
{
return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser;
}
}
static class Utils
{
public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ")
{
int g;
do
{
Console.Write(prompt);
if (int.TryParse(Console.ReadLine(), out g))
{
if (g >= min && g <= max)
return g;
Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max);
}
else
Console.WriteLine("That is not a number. Please try again...");
} while (true);
}
public static void PrintMenu(List<string> values, int baseIndex = 0)
{
values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value));
}
public static void WriteLineColored(string text, ConsoleColor color)
{
var curr = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ForegroundColor = curr;
}
}
}
Right off the bat I created two
enums
which will be helpful in the rest of the program. aGesture
enum
which holds all possible playableGestures
, and aPerformance
enum
to communicate to a player how they did during aGame
.enum Gesture { Rock = 1, Paper = 2, Scissors = 3, Spock = 4, Lizard = 5 } enum Performance { Lost = -1, Tied = 0, Won = 1 }
I created an
abstract
Player
class
which handled the specific player data. From here I created aHuman
andComputer
class
, which inherits fromPlayer
, and implemented theirGetMove
methods, because that is the only place where these two types of players differ.abstract class Player { public uint Wins { get; private set; } public uint Loses { get; private set; } public uint Ties { get; private set; } public abstract Gesture GetMove(); public string GetScoreCard() { return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]"; } public void ClearScore() { Wins = Loses = Ties = 0; } public void GiveResult(Performance performance) { switch (performance) { case Performance.Lost: Loses++; break; case Performance.Tied: Ties++; break; case Performance.Won: Wins++; break; } } } class Human : Player { public override Gesture GetMove() { Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1); return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: "); } } class Computer : Player { public override Gesture GetMove() { return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length)); } }
I created a
static
Game
class
. This one isstatic
because I didn't think it was necessary to create a newGame
object
every-time you wanted to play. Instead I implemented aPlay
method which simply handled all the necessary logic. In theGame
class
, I enclosed things like, aList
of all theGestures
, aDictionary
ofRules
(what defeats what and why). APlay
method to simulate the RPSLS, aWhoWon
method which returns who the winner between two players was, and aGetReason
method which returns the reason a particular Gesture won over another.static class Game { public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture)); private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>() { {Tuple.Create<int,int>(1,3), "Crushes"}, {Tuple.Create<int,int>(1,5), "Crushes"}, {Tuple.Create<int,int>(2,1), "Covers"}, {Tuple.Create<int,int>(2,4), "Disproves"}, {Tuple.Create<int,int>(3,2), "Cuts"}, {Tuple.Create<int,int>(3,5), "Decapitates"}, {Tuple.Create<int,int>(4,3), "Smashes"}, {Tuple.Create<int,int>(4,1), "Vaporizes"}, {Tuple.Create<int,int>(5,2), "Eats"}, {Tuple.Create<int,int>(5,4), "Poisons"} }; public static void Play(Player player1, Player player2) { Gesture p1move = player1.GetMove(); Gesture p2move = player2.GetMove(); Console.Write("Player 1 Chose "); Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green); Console.Write("Player 2 Chose "); Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green); int result = WhoWon(p1move, p2move); switch (result) { case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break; case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break; case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break; } if (result == 0) Console.WriteLine("It was a tie!"); else Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move)); } private static int WhoWon(Gesture p1move, Gesture p2move) { return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2; } private static string GetReason(Gesture winner, Gesture loser) { return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser; } }
For any general utility methods, I enclosed those in a static Utils class
static class Utils { public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ") { int g; do { Console.Write(prompt); if (int.TryParse(Console.ReadLine(), out g)) { if (g >= min && g <= max) return g; Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max); } else Console.WriteLine("That is not a number. Please try again..."); } while (true); } public static void PrintMenu(List<string> values, int baseIndex = 0) { values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value)); } public static void WriteLineColored(string text, ConsoleColor color) { var curr = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(text); Console.ForegroundColor = curr; } }
This brings me to actually using these classes in a meaningful way to actually play the game. You must create the players you want to pit against each other, this could be 2 computers, 2 humans, or a combination of either. As-long as you hold a reference to these
objects
, you also are holding onto their scorecard (assuming they didn't clear it).class Program { static void Main(string[] args) { var gameMenu = new string[] { "Play", "Clear Score", "Quit" }; var me = new Human(); var computer = new Computer(); var playAgain = true; do { Utils.WriteLineColored("Options:", ConsoleColor.White); Utils.PrintMenu(gameMenu.ToList()); switch(Utils.PromptForRangedInt(0, gameMenu.Length - 1, "Choose an Option: ")) { case 0: Console.Clear(); Game.Play(me, computer); Console.WriteLine("Your scorecard: " + me.GetScoreCard() + Environment.NewLine); break; case 1: Console.Clear(); me.ClearScore(); Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green); break; case 2: Console.Clear(); playAgain = false; Console.Write("Good bye, thanks for playing!\nPress any Key to contine..."); Console.ReadKey(true); break; } } while (playAgain); } }
using System;
using System.Collections.Generic;
using System.Linq;
namespace RPSLS
{
class Program
{
static void Main(string[] args)
{
var gameMenu = new string[] { "Play", "Clear Score", "Quit" };
var me = new Human();
var computer = new Computer();
var playAgain = true;
do
{
Utils.WriteLineColored("Options:", ConsoleColor.White);
Utils.PrintMenu(gameMenu.ToList());
switch(Utils.PromptForRangedInt(0, gameMenu.Length - 1, "Choose an Option: "))
{
case 0:
Console.Clear();
Game.Play(me, computer);
Console.WriteLine("Your scorecard: " + me.GetScoreCard() + Environment.NewLine);
break;
case 1:
Console.Clear();
me.ClearScore();
Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green);
break;
case 2:
Console.Clear();
playAgain = false;
Console.Write("Good bye, thanks for playing!\nPress any Key to contine...");
Console.ReadKey(true);
break;
}
} while (playAgain);
}
}
enum Gesture
{
Rock = 1,
Paper = 2,
Scissors = 3,
Spock = 4,
Lizard = 5
}
enum Performance
{
Lost = -1,
Tied = 0,
Won = 1
}
abstract class Player
{
public uint Wins { get; private set; }
public uint Loses { get; private set; }
public uint Ties { get; private set; }
public abstract Gesture GetMove();
public string GetScoreCard()
{
return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]";
}
public void ClearScore()
{
Wins = Loses = Ties = 0;
}
public void GiveResult(Performance performance)
{
switch (performance)
{
case Performance.Lost: Loses++; break;
case Performance.Tied: Ties++; break;
case Performance.Won: Wins++; break;
}
}
}
class Human : Player
{
public override Gesture GetMove()
{
Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1);
return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: ");
}
}
class Computer : Player
{
public override Gesture GetMove()
{
return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length));
}
}
static class Game
{
public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture));
private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>()
{
{Tuple.Create<int,int>(1,3), "Crushes"},
{Tuple.Create<int,int>(1,5), "Crushes"},
{Tuple.Create<int,int>(2,1), "Covers"},
{Tuple.Create<int,int>(2,4), "Disproves"},
{Tuple.Create<int,int>(3,2), "Cuts"},
{Tuple.Create<int,int>(3,5), "Decapitates"},
{Tuple.Create<int,int>(4,3), "Smashes"},
{Tuple.Create<int,int>(4,1), "Vaporizes"},
{Tuple.Create<int,int>(5,2), "Eats"},
{Tuple.Create<int,int>(5,4), "Poisons"}
};
public static void Play(Player player1, Player player2)
{
Gesture p1move = player1.GetMove();
Gesture p2move = player2.GetMove();
Console.Write("Player 1 Chose ");
Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green);
Console.Write("Player 2 Chose ");
Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green);
int result = WhoWon(p1move, p2move);
switch (result)
{
case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break;
case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break;
case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break;
}
if (result == 0)
Console.WriteLine("It was a tie!");
else
Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move));
}
private static int WhoWon(Gesture p1move, Gesture p2move)
{
return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2;
}
private static string GetReason(Gesture winner, Gesture loser)
{
return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser;
}
}
static class Utils
{
public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ")
{
int g;
do
{
Console.Write(prompt);
if (int.TryParse(Console.ReadLine(), out g))
{
if (g >= min && g <= max)
return g;
Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max);
}
else
Console.WriteLine("That is not a number. Please try again...");
} while (true);
}
public static void PrintMenu(List<string> values, int baseIndex = 0)
{
values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value));
}
public static void WriteLineColored(string text, ConsoleColor color)
{
var curr = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ForegroundColor = curr;
}
}
}
In my original review of this program I mentioned I really wanted to re-do this in OO. So I have. I recommend you read my first review to get most of the reasons behind much of the code refactoring I did.
Right off the bat I created two
enums
which will be helpful in the rest of the program. aGesture
enum
which holds all possible playableGestures
, and aPerformance
enum
to communicate to a player how they did during aGame
.enum Gesture { Rock = 1, Paper = 2, Scissors = 3, Spock = 4, Lizard = 5 } enum Performance { Lost = -1, Tied = 0, Won = 1 }
I created an
abstract
Player
class
which handled the specific player data. From here I created aHuman
andComputer
class
, which inherits fromPlayer
, and implemented theirGetMove
methods, because that is the only place where these two types of players differ.abstract class Player { public uint Wins { get; private set; } public uint Loses { get; private set; } public uint Ties { get; private set; } public abstract Gesture GetMove(); public string GetScoreCard() { return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]"; } public void ClearScore() { Wins = Loses = Ties = 0; } public void GiveResult(Performance performance) { switch (performance) { case Performance.Lost: Loses++; break; case Performance.Tied: Ties++; break; case Performance.Won: Wins++; break; } } } class Human : Player { public override Gesture GetMove() { Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1); return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: "); } } class Computer : Player { public override Gesture GetMove() { return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length)); } }
I created a
static
Game
class
. This one isstatic
because I didn't think it was necessary to create a newGame
object
every-time you wanted to play. Instead I implemented aPlay
method which simply handled all the necessary logic. In theGame
class
, I enclosed things like, aList
of all theGestures
, aDictionary
ofRules
(what defeats what and why). APlay
method to simulate the RPSLS, aWhoWon
method which returns who the winner between two players was, and aGetReason
method which returns the reason a particular Gesture won over another.static class Game { public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture)); private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>() { {Tuple.Create<int,int>(1,3), "Crushes"}, {Tuple.Create<int,int>(1,5), "Crushes"}, {Tuple.Create<int,int>(2,1), "Covers"}, {Tuple.Create<int,int>(2,4), "Disproves"}, {Tuple.Create<int,int>(3,2), "Cuts"}, {Tuple.Create<int,int>(3,5), "Decapitates"}, {Tuple.Create<int,int>(4,3), "Smashes"}, {Tuple.Create<int,int>(4,1), "Vaporizes"}, {Tuple.Create<int,int>(5,2), "Eats"}, {Tuple.Create<int,int>(5,4), "Poisons"} }; public static void Play(Player player1, Player player2) { Gesture p1move = player1.GetMove(); Gesture p2move = player2.GetMove(); Console.Write("Player 1 Chose "); Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green); Console.Write("Player 2 Chose "); Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green); int result = WhoWon(p1move, p2move); switch (result) { case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break; case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break; case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break; } if (result == 0) Console.WriteLine("It was a tie!"); else Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move)); } private static int WhoWon(Gesture p1move, Gesture p2move) { return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2; } private static string GetReason(Gesture winner, Gesture loser) { return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser; } }
For any general utility methods, I enclosed those in a static Utils class
static class Utils { public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ") { int g; do { Console.Write(prompt); if (int.TryParse(Console.ReadLine(), out g)) { if (g >= min && g <= max) return g; Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max); } else Console.WriteLine("That is not a number. Please try again..."); } while (true); } public static void PrintMenu(List<string> values, int baseIndex = 0) { values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value)); } public static void WriteLineColored(string text, ConsoleColor color) { var curr = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(text); Console.ForegroundColor = curr; } }
This brings me to actually using these classes in a meaningful way to actually play the game. You must create the players you want to pit against each other, this could be 2 computers, 2 humans, or a combination of either. As-long as you hold a reference to these
objects
, you also are holding onto their scorecard (assuming they didn't clear it).class Program { static void Main(string[] args) { var endGameMenu = new string[] { "Play Again", "Clear Score", "Quit" }; var me = new Human(); var computer = new Computer(); var playAgain = true; do { Game.Play(me, computer); Console.WriteLine("Your scorecard: " + me.GetScoreCard()); int result; do { Console.WriteLine("Options:"); Utils.PrintMenu(endGameMenu.ToList()); result = Utils.PromptForRangedInt(0, endGameMenu.Length - 1, "Choose an Option: "); if (result == 1) { me.ClearScore(); Console.Clear(); Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green); } } while (result != 0 && result != 2); Console.Clear(); playAgain = result == 0; } while (playAgain); } }
Here is the entire dump of the program:
using System;
using System.Collections.Generic;
using System.Linq;
namespace RPSLS
{
class Program
{
static void Main(string[] args)
{
var endGameMenu = new string[] { "Play Again", "Clear Score", "Quit" };
var me = new Human();
var computer = new Computer();
var playAgain = true;
do
{
Game.Play(me, computer);
Console.WriteLine("Your scorecard: " + me.GetScoreCard());
int result;
do
{
Console.WriteLine("Options:");
Utils.PrintMenu(endGameMenu.ToList());
result = Utils.PromptForRangedInt(0, endGameMenu.Length - 1, "Choose an Option: ");
if (result == 1)
{
me.ClearScore();
Console.Clear();
Utils.WriteLineColored("Your score has been cleared", ConsoleColor.Green);
}
} while (result != 0 && result != 2);
Console.Clear();
playAgain = result == 0;
} while (playAgain);
}
}
enum Gesture
{
Rock = 1,
Paper = 2,
Scissors = 3,
Spock = 4,
Lizard = 5
}
enum Performance
{
Lost = -1,
Tied = 0,
Won = 1
}
abstract class Player
{
public uint Wins { get; private set; }
public uint Loses { get; private set; }
public uint Ties { get; private set; }
public abstract Gesture GetMove();
public string GetScoreCard()
{
return "[Wins: " + Wins + "] [Loses " + Loses + "] [Ties " + Ties + "]";
}
public void ClearScore()
{
Wins = Loses = Ties = 0;
}
public void GiveResult(Performance performance)
{
switch (performance)
{
case Performance.Lost: Loses++; break;
case Performance.Tied: Ties++; break;
case Performance.Won: Wins++; break;
}
}
}
class Human : Player
{
public override Gesture GetMove()
{
Utils.PrintMenu(Game.Gestures.Select(g => g.ToString()).ToList(), 1);
return (Gesture)Utils.PromptForRangedInt((int)Game.Gestures.First(), (int)Game.Gestures.Last(), "Please choose your Gesture: ");
}
}
class Computer : Player
{
public override Gesture GetMove()
{
return (Gesture)Game.Gestures.GetValue(new Random().Next(Game.Gestures.Length));
}
}
static class Game
{
public static Gesture[] Gestures = (Gesture[])Enum.GetValues(typeof(Gesture));
private static Dictionary<Tuple<int, int>, string> Rules = new Dictionary<Tuple<int, int>, string>()
{
{Tuple.Create<int,int>(1,3), "Crushes"},
{Tuple.Create<int,int>(1,5), "Crushes"},
{Tuple.Create<int,int>(2,1), "Covers"},
{Tuple.Create<int,int>(2,4), "Disproves"},
{Tuple.Create<int,int>(3,2), "Cuts"},
{Tuple.Create<int,int>(3,5), "Decapitates"},
{Tuple.Create<int,int>(4,3), "Smashes"},
{Tuple.Create<int,int>(4,1), "Vaporizes"},
{Tuple.Create<int,int>(5,2), "Eats"},
{Tuple.Create<int,int>(5,4), "Poisons"}
};
public static void Play(Player player1, Player player2)
{
Gesture p1move = player1.GetMove();
Gesture p2move = player2.GetMove();
Console.Write("Player 1 Chose ");
Utils.WriteLineColored(p1move.ToString(), ConsoleColor.Green);
Console.Write("Player 2 Chose ");
Utils.WriteLineColored(p2move.ToString(), ConsoleColor.Green);
int result = WhoWon(p1move, p2move);
switch (result)
{
case 0: player1.GiveResult(Performance.Tied); player2.GiveResult(Performance.Tied); break;
case 1: player1.GiveResult(Performance.Won); player2.GiveResult(Performance.Lost); break;
case 2: player1.GiveResult(Performance.Lost); player2.GiveResult(Performance.Won); break;
}
if (result == 0)
Console.WriteLine("It was a tie!");
else
Console.WriteLine("Player {0} won, because {1}.", result, GetReason(result == 1 ? p1move : p2move, result == 1 ? p2move : p1move));
}
private static int WhoWon(Gesture p1move, Gesture p2move)
{
return p1move == p2move ? 0 : Rules.Keys.Where(key => key.Item1 == (int)p1move && key.Item2 == (int)p2move).FirstOrDefault() != null ? 1 : 2;
}
private static string GetReason(Gesture winner, Gesture loser)
{
return winner + " " + Rules[Tuple.Create((int)winner, (int)loser)] + " " + loser;
}
}
static class Utils
{
public static int PromptForRangedInt(int min = int.MinValue, int max = int.MaxValue, string prompt = "Please enter an Integer: ")
{
int g;
do
{
Console.Write(prompt);
if (int.TryParse(Console.ReadLine(), out g))
{
if (g >= min && g <= max)
return g;
Console.WriteLine("You entered {0}, but the input must be in the range of ({1} - {2}. Please try again...", g, min, max);
}
else
Console.WriteLine("That is not a number. Please try again...");
} while (true);
}
public static void PrintMenu(List<string> values, int baseIndex = 0)
{
values.ForEach(value => Console.WriteLine("{0}: {1}", baseIndex++, value));
}
public static void WriteLineColored(string text, ConsoleColor color)
{
var curr = Console.ForegroundColor;
Console.ForegroundColor = color;
Console.WriteLine(text);
Console.ForegroundColor = curr;
}
}
}