Skip to main content
replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

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.

Updated with better Main method
Source Link
BenVlodgi
  • 4.3k
  • 2
  • 20
  • 47
  1. Right off the bat I created two enums which will be helpful in the rest of the program. a Gesture enum which holds all possible playable Gestures, and a Performance enum to communicate to a player how they did during a Game.

     enum Gesture
     {
         Rock = 1,
         Paper = 2,
         Scissors = 3,
         Spock = 4,
         Lizard = 5
     }
    
     enum Performance
     {
         Lost = -1,
         Tied = 0,
         Won = 1
     }
    
  2. I created an abstract Player class which handled the specific player data. From here I created a Human and Computer class, which inherits from Player, and implemented their GetMove 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));
         }
     }
    
  3. I created a static Game class. This one is static because I didn't think it was necessary to create a new Game object every-time you wanted to play. Instead I implemented a Play method which simply handled all the necessary logic. In the Game class, I enclosed things like, a List of all the Gestures, a Dictionary of Rules (what defeats what and why). A Play method to simulate the RPSLS, a WhoWon method which returns who the winner between two players was, and a GetReason 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;
         }
     }
    
  4. 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;
         }
     }
    
  5. 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;
        }
    }
}
  1. Right off the bat I created two enums which will be helpful in the rest of the program. a Gesture enum which holds all possible playable Gestures, and a Performance enum to communicate to a player how they did during a Game.

     enum Gesture
     {
         Rock = 1,
         Paper = 2,
         Scissors = 3,
         Spock = 4,
         Lizard = 5
     }
    
     enum Performance
     {
         Lost = -1,
         Tied = 0,
         Won = 1
     }
    
  2. I created an abstract Player class which handled the specific player data. From here I created a Human and Computer class, which inherits from Player, and implemented their GetMove 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));
         }
     }
    
  3. I created a static Game class. This one is static because I didn't think it was necessary to create a new Game object every-time you wanted to play. Instead I implemented a Play method which simply handled all the necessary logic. In the Game class, I enclosed things like, a List of all the Gestures, a Dictionary of Rules (what defeats what and why). A Play method to simulate the RPSLS, a WhoWon method which returns who the winner between two players was, and a GetReason 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;
         }
     }
    
  4. 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;
         }
     }
    
  5. 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;
        }
    }
}
  1. Right off the bat I created two enums which will be helpful in the rest of the program. a Gesture enum which holds all possible playable Gestures, and a Performance enum to communicate to a player how they did during a Game.

     enum Gesture
     {
         Rock = 1,
         Paper = 2,
         Scissors = 3,
         Spock = 4,
         Lizard = 5
     }
    
     enum Performance
     {
         Lost = -1,
         Tied = 0,
         Won = 1
     }
    
  2. I created an abstract Player class which handled the specific player data. From here I created a Human and Computer class, which inherits from Player, and implemented their GetMove 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));
         }
     }
    
  3. I created a static Game class. This one is static because I didn't think it was necessary to create a new Game object every-time you wanted to play. Instead I implemented a Play method which simply handled all the necessary logic. In the Game class, I enclosed things like, a List of all the Gestures, a Dictionary of Rules (what defeats what and why). A Play method to simulate the RPSLS, a WhoWon method which returns who the winner between two players was, and a GetReason 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;
         }
     }
    
  4. 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;
         }
     }
    
  5. 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;
        }
    }
}
Source Link
BenVlodgi
  • 4.3k
  • 2
  • 20
  • 47

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.

  1. Right off the bat I created two enums which will be helpful in the rest of the program. a Gesture enum which holds all possible playable Gestures, and a Performance enum to communicate to a player how they did during a Game.

     enum Gesture
     {
         Rock = 1,
         Paper = 2,
         Scissors = 3,
         Spock = 4,
         Lizard = 5
     }
    
     enum Performance
     {
         Lost = -1,
         Tied = 0,
         Won = 1
     }
    
  2. I created an abstract Player class which handled the specific player data. From here I created a Human and Computer class, which inherits from Player, and implemented their GetMove 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));
         }
     }
    
  3. I created a static Game class. This one is static because I didn't think it was necessary to create a new Game object every-time you wanted to play. Instead I implemented a Play method which simply handled all the necessary logic. In the Game class, I enclosed things like, a List of all the Gestures, a Dictionary of Rules (what defeats what and why). A Play method to simulate the RPSLS, a WhoWon method which returns who the winner between two players was, and a GetReason 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;
         }
     }
    
  4. 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;
         }
     }
    
  5. 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;
        }
    }
}