Rock-Paper-Scissors-Lizard-Spock Challenge












40
















"Scissors cuts paper, paper covers rock,
rock crushes lizard, lizard poisons Spock,
Spock smashes scissors, scissors decapitate lizard,
lizard eats paper, paper disproves Spock,
Spock vaporizes rock. And as it always has, rock crushes scissors."



-- Dr. Sheldon Cooper




So these are the rules.



Building blocks



The first thing I thought was "I need a way to compare the possible selections" - this sounded like IComparable<T>, so I started by implementing that interface in a SelectionBase class. Now because I knew I'd derive Rock, Paper, Scissors, Lizard and Spock classes from this, I decided to include a Name property that returns the type name, and since I also needed a way to display different verbs depending on the type of the opponent's selection, I also included a GetWinningVerb method:



public abstract class SelectionBase : IComparable<SelectionBase>
{
public abstract int CompareTo(SelectionBase other);
public string Name { get { return GetType().Name; } }
public abstract string GetWinningVerb(SelectionBase other);
}


The base class is implemented by each of the possible selections:



public class Rock : SelectionBase
{
public override string GetWinningVerb(SelectionBase other)
{
if (other is Scissors) return "crushes";
if (other is Lizard) return "crushes";

throw new InvalidOperationException("Are we playing the same game?");
}

public override int CompareTo(SelectionBase other)
{
if (other is Rock) return 0;
if (other is Paper) return -1;
if (other is Scissors) return 1;
if (other is Lizard) return 1;
if (other is Spock) return -1;

throw new InvalidOperationException("Are we playing the same game?");
}
}

public class Paper : SelectionBase
{
public override string GetWinningVerb(SelectionBase other)
{
if (other is Rock) return "covers";
if (other is Spock) return "disproves";

throw new InvalidOperationException("Are we playing the same game?");
}

public override int CompareTo(SelectionBase other)
{
if (other is Rock) return 1;
if (other is Paper) return 0;
if (other is Scissors) return -1;
if (other is Lizard) return -1;
if (other is Spock) return 1;

throw new InvalidOperationException("Are we playing the same game?");
}
}

public class Scissors : SelectionBase
{
public override string GetWinningVerb(SelectionBase other)
{
if (other is Paper) return "cuts";
if (other is Lizard) return "decapitates";

throw new InvalidOperationException("Are we playing the same game?");
}

public override int CompareTo(SelectionBase other)
{
if (other is Rock) return -1;
if (other is Paper) return 1;
if (other is Scissors) return 0;
if (other is Lizard) return 1;
if (other is Spock) return -1;

throw new InvalidOperationException("Are we playing the same game?");
}
}

public class Lizard : SelectionBase
{
public override string GetWinningVerb(SelectionBase other)
{
if (other is Paper) return "eats";
if (other is Spock) return "poisons";

throw new InvalidOperationException("Are we playing the same game?");
}

public override int CompareTo(SelectionBase other)
{
if (other is Rock) return -1;
if (other is Paper) return 1;
if (other is Scissors) return -1;
if (other is Lizard) return 0;
if (other is Spock) return 1;

throw new InvalidOperationException("Are we playing the same game?");
}
}

public class Spock : SelectionBase
{
public override string GetWinningVerb(SelectionBase other)
{
if (other is Rock) return "vaporizes";
if (other is Scissors) return "smashes";

throw new InvalidOperationException("Are we playing the same game?");
}

public override int CompareTo(SelectionBase other)
{
if (other is Rock) return 1;
if (other is Paper) return -1;
if (other is Scissors) return 1;
if (other is Lizard) return -1;
if (other is Spock) return 0;

throw new InvalidOperationException("Are we playing the same game?");
}
}


User Input



Then I needed a way to get user input. I knew I was going to make a simple console app, but just so I could run unit tests I decided to create an IUserInputProvider interface - the first pass had all 3 methods in the interface, but since I wasn't using them all, I only kept one; I don't think getting rid of GetUserInput(string) would hurt:



public interface IUserInputProvider
{
string GetValidUserInput(string prompt, IEnumerable<string> validValues);
}

public class ConsoleUserInputProvider : IUserInputProvider
{
private string GetUserInput(string prompt)
{
Console.WriteLine(prompt);
return Console.ReadLine();
}

private string GetUserInput(string prompt, IEnumerable<string> validValues)
{
var input = GetUserInput(prompt);
var isValid = validValues.Select(v => v.ToLower()).Contains(input.ToLower());

return isValid ? input : string.Empty;
}

public string GetValidUserInput(string prompt, IEnumerable<string> validValues)
{
var input = string.Empty;
var isValid = false;
while (!isValid)
{
input = GetUserInput(prompt, validValues);
isValid = !string.IsNullOrEmpty(input) || validValues.Contains(string.Empty);
}

return input;
}
}


The actual program



class Program
{
/*
"Scissors cuts paper, paper covers rock,
rock crushes lizard, lizard poisons Spock,
Spock smashes scissors, scissors decapitate lizard,
lizard eats paper, paper disproves Spock,
Spock vaporizes rock. And as it always has, rock crushes scissors."

-- Dr. Sheldon Cooper
*/

static void Main(string args)
{
var consoleReader = new ConsoleUserInputProvider();
var consoleWriter = new ConsoleResultWriter();

var game = new Game(consoleReader, consoleWriter);
game.Run();
}
}


IResultWriter



public interface IResultWriter
{
void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon);
}

public class ConsoleResultWriter : IResultWriter
{
public void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon)
{
var resultActions = new Dictionary<int, Action<SelectionBase, SelectionBase>>
{
{ 1, OutputPlayerWinsResult },
{ -1, OutputPlayerLosesResult },
{ 0, OutputTieResult }
};

resultActions[comparisonResult].Invoke(player, sheldon);
}

private void OutputPlayerLosesResult(SelectionBase player, SelectionBase sheldon)
{
Console.WriteLine("ntSheldon says: "{0} {1} {2}. You lose!"n", sheldon.Name, sheldon.GetWinningVerb(player), player.Name);
}

private void OutputPlayerWinsResult(SelectionBase player, SelectionBase sheldon)
{
Console.WriteLine("ntSheldon says: "{0} {1} {2}. You win!"n", player.Name, player.GetWinningVerb(sheldon), sheldon.Name);
}

private void OutputTieResult(SelectionBase player, SelectionBase sheldon)
{
Console.WriteLine("ntSheldon says: "Meh. Tie!"n");
}


The actual game



I didn't bother with building a complex AI - we're playing against Sheldon Cooper here, and he systematically plays Spock.



public class Game
{
private readonly Dictionary<string, SelectionBase> _playable =
new Dictionary<string, SelectionBase>
{
{ "1", new Rock() },
{ "2", new Paper() },
{ "3", new Scissors() },
{ "4", new Lizard() },
{ "5", new Spock() }
};

private readonly IUserInputProvider _consoleInput;
private readonly IResultWriter _resultWriter;

public Game(IUserInputProvider console, IResultWriter resultWriter)
{
_consoleInput = console;
_resultWriter = resultWriter;
}

public void Run()
{
while (true)
{
LayoutGameScreen();

var player = GetUserSelection();
if (player == null) return;

var sheldon = new Spock();
var result = player.CompareTo(sheldon);

_resultWriter.OutputResult(result, player, sheldon);
Pause();
}
}

private void Pause()
{
Console.WriteLine("nPress <ENTER> to continue.");
Console.ReadLine();
}

private void LayoutGameScreen()
{
Console.Clear();
Console.WriteLine("Rock-Paper-Scissors-Lizard-Spock 1.0n{0}n", new string('=', 40));

foreach (var item in _playable)
Console.WriteLine("t[{0}] {1}", item.Key, item.Value.Name);

Console.WriteLine();
}

private SelectionBase GetUserSelection()
{
var values = _playable.Keys.ToList();
values.Add(string.Empty); // allows a non-selection

var input = _consoleInput.GetValidUserInput("Your selection? <ENTER> to quit.", values);
if (input == string.Empty) return null;

return _playable[input];
}
}




Output





  • Rock: "Spock vaporizes Rock. You lose!"


  • Paper: "Paper disproves Spock. You win!"


  • Scissors: "Spock smashes Scissors. You lose!"


  • Lizard: "Lizard poisons Spock. You win!"


  • Spock: "Meh. Tie!"










share|improve this question





























    40
















    "Scissors cuts paper, paper covers rock,
    rock crushes lizard, lizard poisons Spock,
    Spock smashes scissors, scissors decapitate lizard,
    lizard eats paper, paper disproves Spock,
    Spock vaporizes rock. And as it always has, rock crushes scissors."



    -- Dr. Sheldon Cooper




    So these are the rules.



    Building blocks



    The first thing I thought was "I need a way to compare the possible selections" - this sounded like IComparable<T>, so I started by implementing that interface in a SelectionBase class. Now because I knew I'd derive Rock, Paper, Scissors, Lizard and Spock classes from this, I decided to include a Name property that returns the type name, and since I also needed a way to display different verbs depending on the type of the opponent's selection, I also included a GetWinningVerb method:



    public abstract class SelectionBase : IComparable<SelectionBase>
    {
    public abstract int CompareTo(SelectionBase other);
    public string Name { get { return GetType().Name; } }
    public abstract string GetWinningVerb(SelectionBase other);
    }


    The base class is implemented by each of the possible selections:



    public class Rock : SelectionBase
    {
    public override string GetWinningVerb(SelectionBase other)
    {
    if (other is Scissors) return "crushes";
    if (other is Lizard) return "crushes";

    throw new InvalidOperationException("Are we playing the same game?");
    }

    public override int CompareTo(SelectionBase other)
    {
    if (other is Rock) return 0;
    if (other is Paper) return -1;
    if (other is Scissors) return 1;
    if (other is Lizard) return 1;
    if (other is Spock) return -1;

    throw new InvalidOperationException("Are we playing the same game?");
    }
    }

    public class Paper : SelectionBase
    {
    public override string GetWinningVerb(SelectionBase other)
    {
    if (other is Rock) return "covers";
    if (other is Spock) return "disproves";

    throw new InvalidOperationException("Are we playing the same game?");
    }

    public override int CompareTo(SelectionBase other)
    {
    if (other is Rock) return 1;
    if (other is Paper) return 0;
    if (other is Scissors) return -1;
    if (other is Lizard) return -1;
    if (other is Spock) return 1;

    throw new InvalidOperationException("Are we playing the same game?");
    }
    }

    public class Scissors : SelectionBase
    {
    public override string GetWinningVerb(SelectionBase other)
    {
    if (other is Paper) return "cuts";
    if (other is Lizard) return "decapitates";

    throw new InvalidOperationException("Are we playing the same game?");
    }

    public override int CompareTo(SelectionBase other)
    {
    if (other is Rock) return -1;
    if (other is Paper) return 1;
    if (other is Scissors) return 0;
    if (other is Lizard) return 1;
    if (other is Spock) return -1;

    throw new InvalidOperationException("Are we playing the same game?");
    }
    }

    public class Lizard : SelectionBase
    {
    public override string GetWinningVerb(SelectionBase other)
    {
    if (other is Paper) return "eats";
    if (other is Spock) return "poisons";

    throw new InvalidOperationException("Are we playing the same game?");
    }

    public override int CompareTo(SelectionBase other)
    {
    if (other is Rock) return -1;
    if (other is Paper) return 1;
    if (other is Scissors) return -1;
    if (other is Lizard) return 0;
    if (other is Spock) return 1;

    throw new InvalidOperationException("Are we playing the same game?");
    }
    }

    public class Spock : SelectionBase
    {
    public override string GetWinningVerb(SelectionBase other)
    {
    if (other is Rock) return "vaporizes";
    if (other is Scissors) return "smashes";

    throw new InvalidOperationException("Are we playing the same game?");
    }

    public override int CompareTo(SelectionBase other)
    {
    if (other is Rock) return 1;
    if (other is Paper) return -1;
    if (other is Scissors) return 1;
    if (other is Lizard) return -1;
    if (other is Spock) return 0;

    throw new InvalidOperationException("Are we playing the same game?");
    }
    }


    User Input



    Then I needed a way to get user input. I knew I was going to make a simple console app, but just so I could run unit tests I decided to create an IUserInputProvider interface - the first pass had all 3 methods in the interface, but since I wasn't using them all, I only kept one; I don't think getting rid of GetUserInput(string) would hurt:



    public interface IUserInputProvider
    {
    string GetValidUserInput(string prompt, IEnumerable<string> validValues);
    }

    public class ConsoleUserInputProvider : IUserInputProvider
    {
    private string GetUserInput(string prompt)
    {
    Console.WriteLine(prompt);
    return Console.ReadLine();
    }

    private string GetUserInput(string prompt, IEnumerable<string> validValues)
    {
    var input = GetUserInput(prompt);
    var isValid = validValues.Select(v => v.ToLower()).Contains(input.ToLower());

    return isValid ? input : string.Empty;
    }

    public string GetValidUserInput(string prompt, IEnumerable<string> validValues)
    {
    var input = string.Empty;
    var isValid = false;
    while (!isValid)
    {
    input = GetUserInput(prompt, validValues);
    isValid = !string.IsNullOrEmpty(input) || validValues.Contains(string.Empty);
    }

    return input;
    }
    }


    The actual program



    class Program
    {
    /*
    "Scissors cuts paper, paper covers rock,
    rock crushes lizard, lizard poisons Spock,
    Spock smashes scissors, scissors decapitate lizard,
    lizard eats paper, paper disproves Spock,
    Spock vaporizes rock. And as it always has, rock crushes scissors."

    -- Dr. Sheldon Cooper
    */

    static void Main(string args)
    {
    var consoleReader = new ConsoleUserInputProvider();
    var consoleWriter = new ConsoleResultWriter();

    var game = new Game(consoleReader, consoleWriter);
    game.Run();
    }
    }


    IResultWriter



    public interface IResultWriter
    {
    void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon);
    }

    public class ConsoleResultWriter : IResultWriter
    {
    public void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon)
    {
    var resultActions = new Dictionary<int, Action<SelectionBase, SelectionBase>>
    {
    { 1, OutputPlayerWinsResult },
    { -1, OutputPlayerLosesResult },
    { 0, OutputTieResult }
    };

    resultActions[comparisonResult].Invoke(player, sheldon);
    }

    private void OutputPlayerLosesResult(SelectionBase player, SelectionBase sheldon)
    {
    Console.WriteLine("ntSheldon says: "{0} {1} {2}. You lose!"n", sheldon.Name, sheldon.GetWinningVerb(player), player.Name);
    }

    private void OutputPlayerWinsResult(SelectionBase player, SelectionBase sheldon)
    {
    Console.WriteLine("ntSheldon says: "{0} {1} {2}. You win!"n", player.Name, player.GetWinningVerb(sheldon), sheldon.Name);
    }

    private void OutputTieResult(SelectionBase player, SelectionBase sheldon)
    {
    Console.WriteLine("ntSheldon says: "Meh. Tie!"n");
    }


    The actual game



    I didn't bother with building a complex AI - we're playing against Sheldon Cooper here, and he systematically plays Spock.



    public class Game
    {
    private readonly Dictionary<string, SelectionBase> _playable =
    new Dictionary<string, SelectionBase>
    {
    { "1", new Rock() },
    { "2", new Paper() },
    { "3", new Scissors() },
    { "4", new Lizard() },
    { "5", new Spock() }
    };

    private readonly IUserInputProvider _consoleInput;
    private readonly IResultWriter _resultWriter;

    public Game(IUserInputProvider console, IResultWriter resultWriter)
    {
    _consoleInput = console;
    _resultWriter = resultWriter;
    }

    public void Run()
    {
    while (true)
    {
    LayoutGameScreen();

    var player = GetUserSelection();
    if (player == null) return;

    var sheldon = new Spock();
    var result = player.CompareTo(sheldon);

    _resultWriter.OutputResult(result, player, sheldon);
    Pause();
    }
    }

    private void Pause()
    {
    Console.WriteLine("nPress <ENTER> to continue.");
    Console.ReadLine();
    }

    private void LayoutGameScreen()
    {
    Console.Clear();
    Console.WriteLine("Rock-Paper-Scissors-Lizard-Spock 1.0n{0}n", new string('=', 40));

    foreach (var item in _playable)
    Console.WriteLine("t[{0}] {1}", item.Key, item.Value.Name);

    Console.WriteLine();
    }

    private SelectionBase GetUserSelection()
    {
    var values = _playable.Keys.ToList();
    values.Add(string.Empty); // allows a non-selection

    var input = _consoleInput.GetValidUserInput("Your selection? <ENTER> to quit.", values);
    if (input == string.Empty) return null;

    return _playable[input];
    }
    }




    Output





    • Rock: "Spock vaporizes Rock. You lose!"


    • Paper: "Paper disproves Spock. You win!"


    • Scissors: "Spock smashes Scissors. You lose!"


    • Lizard: "Lizard poisons Spock. You win!"


    • Spock: "Meh. Tie!"










    share|improve this question



























      40












      40








      40


      13







      "Scissors cuts paper, paper covers rock,
      rock crushes lizard, lizard poisons Spock,
      Spock smashes scissors, scissors decapitate lizard,
      lizard eats paper, paper disproves Spock,
      Spock vaporizes rock. And as it always has, rock crushes scissors."



      -- Dr. Sheldon Cooper




      So these are the rules.



      Building blocks



      The first thing I thought was "I need a way to compare the possible selections" - this sounded like IComparable<T>, so I started by implementing that interface in a SelectionBase class. Now because I knew I'd derive Rock, Paper, Scissors, Lizard and Spock classes from this, I decided to include a Name property that returns the type name, and since I also needed a way to display different verbs depending on the type of the opponent's selection, I also included a GetWinningVerb method:



      public abstract class SelectionBase : IComparable<SelectionBase>
      {
      public abstract int CompareTo(SelectionBase other);
      public string Name { get { return GetType().Name; } }
      public abstract string GetWinningVerb(SelectionBase other);
      }


      The base class is implemented by each of the possible selections:



      public class Rock : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Scissors) return "crushes";
      if (other is Lizard) return "crushes";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 0;
      if (other is Paper) return -1;
      if (other is Scissors) return 1;
      if (other is Lizard) return 1;
      if (other is Spock) return -1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Paper : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Rock) return "covers";
      if (other is Spock) return "disproves";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 1;
      if (other is Paper) return 0;
      if (other is Scissors) return -1;
      if (other is Lizard) return -1;
      if (other is Spock) return 1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Scissors : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Paper) return "cuts";
      if (other is Lizard) return "decapitates";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return -1;
      if (other is Paper) return 1;
      if (other is Scissors) return 0;
      if (other is Lizard) return 1;
      if (other is Spock) return -1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Lizard : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Paper) return "eats";
      if (other is Spock) return "poisons";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return -1;
      if (other is Paper) return 1;
      if (other is Scissors) return -1;
      if (other is Lizard) return 0;
      if (other is Spock) return 1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Spock : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Rock) return "vaporizes";
      if (other is Scissors) return "smashes";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 1;
      if (other is Paper) return -1;
      if (other is Scissors) return 1;
      if (other is Lizard) return -1;
      if (other is Spock) return 0;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }


      User Input



      Then I needed a way to get user input. I knew I was going to make a simple console app, but just so I could run unit tests I decided to create an IUserInputProvider interface - the first pass had all 3 methods in the interface, but since I wasn't using them all, I only kept one; I don't think getting rid of GetUserInput(string) would hurt:



      public interface IUserInputProvider
      {
      string GetValidUserInput(string prompt, IEnumerable<string> validValues);
      }

      public class ConsoleUserInputProvider : IUserInputProvider
      {
      private string GetUserInput(string prompt)
      {
      Console.WriteLine(prompt);
      return Console.ReadLine();
      }

      private string GetUserInput(string prompt, IEnumerable<string> validValues)
      {
      var input = GetUserInput(prompt);
      var isValid = validValues.Select(v => v.ToLower()).Contains(input.ToLower());

      return isValid ? input : string.Empty;
      }

      public string GetValidUserInput(string prompt, IEnumerable<string> validValues)
      {
      var input = string.Empty;
      var isValid = false;
      while (!isValid)
      {
      input = GetUserInput(prompt, validValues);
      isValid = !string.IsNullOrEmpty(input) || validValues.Contains(string.Empty);
      }

      return input;
      }
      }


      The actual program



      class Program
      {
      /*
      "Scissors cuts paper, paper covers rock,
      rock crushes lizard, lizard poisons Spock,
      Spock smashes scissors, scissors decapitate lizard,
      lizard eats paper, paper disproves Spock,
      Spock vaporizes rock. And as it always has, rock crushes scissors."

      -- Dr. Sheldon Cooper
      */

      static void Main(string args)
      {
      var consoleReader = new ConsoleUserInputProvider();
      var consoleWriter = new ConsoleResultWriter();

      var game = new Game(consoleReader, consoleWriter);
      game.Run();
      }
      }


      IResultWriter



      public interface IResultWriter
      {
      void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon);
      }

      public class ConsoleResultWriter : IResultWriter
      {
      public void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon)
      {
      var resultActions = new Dictionary<int, Action<SelectionBase, SelectionBase>>
      {
      { 1, OutputPlayerWinsResult },
      { -1, OutputPlayerLosesResult },
      { 0, OutputTieResult }
      };

      resultActions[comparisonResult].Invoke(player, sheldon);
      }

      private void OutputPlayerLosesResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "{0} {1} {2}. You lose!"n", sheldon.Name, sheldon.GetWinningVerb(player), player.Name);
      }

      private void OutputPlayerWinsResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "{0} {1} {2}. You win!"n", player.Name, player.GetWinningVerb(sheldon), sheldon.Name);
      }

      private void OutputTieResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "Meh. Tie!"n");
      }


      The actual game



      I didn't bother with building a complex AI - we're playing against Sheldon Cooper here, and he systematically plays Spock.



      public class Game
      {
      private readonly Dictionary<string, SelectionBase> _playable =
      new Dictionary<string, SelectionBase>
      {
      { "1", new Rock() },
      { "2", new Paper() },
      { "3", new Scissors() },
      { "4", new Lizard() },
      { "5", new Spock() }
      };

      private readonly IUserInputProvider _consoleInput;
      private readonly IResultWriter _resultWriter;

      public Game(IUserInputProvider console, IResultWriter resultWriter)
      {
      _consoleInput = console;
      _resultWriter = resultWriter;
      }

      public void Run()
      {
      while (true)
      {
      LayoutGameScreen();

      var player = GetUserSelection();
      if (player == null) return;

      var sheldon = new Spock();
      var result = player.CompareTo(sheldon);

      _resultWriter.OutputResult(result, player, sheldon);
      Pause();
      }
      }

      private void Pause()
      {
      Console.WriteLine("nPress <ENTER> to continue.");
      Console.ReadLine();
      }

      private void LayoutGameScreen()
      {
      Console.Clear();
      Console.WriteLine("Rock-Paper-Scissors-Lizard-Spock 1.0n{0}n", new string('=', 40));

      foreach (var item in _playable)
      Console.WriteLine("t[{0}] {1}", item.Key, item.Value.Name);

      Console.WriteLine();
      }

      private SelectionBase GetUserSelection()
      {
      var values = _playable.Keys.ToList();
      values.Add(string.Empty); // allows a non-selection

      var input = _consoleInput.GetValidUserInput("Your selection? <ENTER> to quit.", values);
      if (input == string.Empty) return null;

      return _playable[input];
      }
      }




      Output





      • Rock: "Spock vaporizes Rock. You lose!"


      • Paper: "Paper disproves Spock. You win!"


      • Scissors: "Spock smashes Scissors. You lose!"


      • Lizard: "Lizard poisons Spock. You win!"


      • Spock: "Meh. Tie!"










      share|improve this question

















      "Scissors cuts paper, paper covers rock,
      rock crushes lizard, lizard poisons Spock,
      Spock smashes scissors, scissors decapitate lizard,
      lizard eats paper, paper disproves Spock,
      Spock vaporizes rock. And as it always has, rock crushes scissors."



      -- Dr. Sheldon Cooper




      So these are the rules.



      Building blocks



      The first thing I thought was "I need a way to compare the possible selections" - this sounded like IComparable<T>, so I started by implementing that interface in a SelectionBase class. Now because I knew I'd derive Rock, Paper, Scissors, Lizard and Spock classes from this, I decided to include a Name property that returns the type name, and since I also needed a way to display different verbs depending on the type of the opponent's selection, I also included a GetWinningVerb method:



      public abstract class SelectionBase : IComparable<SelectionBase>
      {
      public abstract int CompareTo(SelectionBase other);
      public string Name { get { return GetType().Name; } }
      public abstract string GetWinningVerb(SelectionBase other);
      }


      The base class is implemented by each of the possible selections:



      public class Rock : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Scissors) return "crushes";
      if (other is Lizard) return "crushes";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 0;
      if (other is Paper) return -1;
      if (other is Scissors) return 1;
      if (other is Lizard) return 1;
      if (other is Spock) return -1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Paper : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Rock) return "covers";
      if (other is Spock) return "disproves";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 1;
      if (other is Paper) return 0;
      if (other is Scissors) return -1;
      if (other is Lizard) return -1;
      if (other is Spock) return 1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Scissors : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Paper) return "cuts";
      if (other is Lizard) return "decapitates";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return -1;
      if (other is Paper) return 1;
      if (other is Scissors) return 0;
      if (other is Lizard) return 1;
      if (other is Spock) return -1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Lizard : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Paper) return "eats";
      if (other is Spock) return "poisons";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return -1;
      if (other is Paper) return 1;
      if (other is Scissors) return -1;
      if (other is Lizard) return 0;
      if (other is Spock) return 1;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }

      public class Spock : SelectionBase
      {
      public override string GetWinningVerb(SelectionBase other)
      {
      if (other is Rock) return "vaporizes";
      if (other is Scissors) return "smashes";

      throw new InvalidOperationException("Are we playing the same game?");
      }

      public override int CompareTo(SelectionBase other)
      {
      if (other is Rock) return 1;
      if (other is Paper) return -1;
      if (other is Scissors) return 1;
      if (other is Lizard) return -1;
      if (other is Spock) return 0;

      throw new InvalidOperationException("Are we playing the same game?");
      }
      }


      User Input



      Then I needed a way to get user input. I knew I was going to make a simple console app, but just so I could run unit tests I decided to create an IUserInputProvider interface - the first pass had all 3 methods in the interface, but since I wasn't using them all, I only kept one; I don't think getting rid of GetUserInput(string) would hurt:



      public interface IUserInputProvider
      {
      string GetValidUserInput(string prompt, IEnumerable<string> validValues);
      }

      public class ConsoleUserInputProvider : IUserInputProvider
      {
      private string GetUserInput(string prompt)
      {
      Console.WriteLine(prompt);
      return Console.ReadLine();
      }

      private string GetUserInput(string prompt, IEnumerable<string> validValues)
      {
      var input = GetUserInput(prompt);
      var isValid = validValues.Select(v => v.ToLower()).Contains(input.ToLower());

      return isValid ? input : string.Empty;
      }

      public string GetValidUserInput(string prompt, IEnumerable<string> validValues)
      {
      var input = string.Empty;
      var isValid = false;
      while (!isValid)
      {
      input = GetUserInput(prompt, validValues);
      isValid = !string.IsNullOrEmpty(input) || validValues.Contains(string.Empty);
      }

      return input;
      }
      }


      The actual program



      class Program
      {
      /*
      "Scissors cuts paper, paper covers rock,
      rock crushes lizard, lizard poisons Spock,
      Spock smashes scissors, scissors decapitate lizard,
      lizard eats paper, paper disproves Spock,
      Spock vaporizes rock. And as it always has, rock crushes scissors."

      -- Dr. Sheldon Cooper
      */

      static void Main(string args)
      {
      var consoleReader = new ConsoleUserInputProvider();
      var consoleWriter = new ConsoleResultWriter();

      var game = new Game(consoleReader, consoleWriter);
      game.Run();
      }
      }


      IResultWriter



      public interface IResultWriter
      {
      void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon);
      }

      public class ConsoleResultWriter : IResultWriter
      {
      public void OutputResult(int comparisonResult, SelectionBase player, SelectionBase sheldon)
      {
      var resultActions = new Dictionary<int, Action<SelectionBase, SelectionBase>>
      {
      { 1, OutputPlayerWinsResult },
      { -1, OutputPlayerLosesResult },
      { 0, OutputTieResult }
      };

      resultActions[comparisonResult].Invoke(player, sheldon);
      }

      private void OutputPlayerLosesResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "{0} {1} {2}. You lose!"n", sheldon.Name, sheldon.GetWinningVerb(player), player.Name);
      }

      private void OutputPlayerWinsResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "{0} {1} {2}. You win!"n", player.Name, player.GetWinningVerb(sheldon), sheldon.Name);
      }

      private void OutputTieResult(SelectionBase player, SelectionBase sheldon)
      {
      Console.WriteLine("ntSheldon says: "Meh. Tie!"n");
      }


      The actual game



      I didn't bother with building a complex AI - we're playing against Sheldon Cooper here, and he systematically plays Spock.



      public class Game
      {
      private readonly Dictionary<string, SelectionBase> _playable =
      new Dictionary<string, SelectionBase>
      {
      { "1", new Rock() },
      { "2", new Paper() },
      { "3", new Scissors() },
      { "4", new Lizard() },
      { "5", new Spock() }
      };

      private readonly IUserInputProvider _consoleInput;
      private readonly IResultWriter _resultWriter;

      public Game(IUserInputProvider console, IResultWriter resultWriter)
      {
      _consoleInput = console;
      _resultWriter = resultWriter;
      }

      public void Run()
      {
      while (true)
      {
      LayoutGameScreen();

      var player = GetUserSelection();
      if (player == null) return;

      var sheldon = new Spock();
      var result = player.CompareTo(sheldon);

      _resultWriter.OutputResult(result, player, sheldon);
      Pause();
      }
      }

      private void Pause()
      {
      Console.WriteLine("nPress <ENTER> to continue.");
      Console.ReadLine();
      }

      private void LayoutGameScreen()
      {
      Console.Clear();
      Console.WriteLine("Rock-Paper-Scissors-Lizard-Spock 1.0n{0}n", new string('=', 40));

      foreach (var item in _playable)
      Console.WriteLine("t[{0}] {1}", item.Key, item.Value.Name);

      Console.WriteLine();
      }

      private SelectionBase GetUserSelection()
      {
      var values = _playable.Keys.ToList();
      values.Add(string.Empty); // allows a non-selection

      var input = _consoleInput.GetValidUserInput("Your selection? <ENTER> to quit.", values);
      if (input == string.Empty) return null;

      return _playable[input];
      }
      }




      Output





      • Rock: "Spock vaporizes Rock. You lose!"


      • Paper: "Paper disproves Spock. You win!"


      • Scissors: "Spock smashes Scissors. You lose!"


      • Lizard: "Lizard poisons Spock. You win!"


      • Spock: "Meh. Tie!"







      c# game dependency-injection community-challenge rock-paper-scissors






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Sep 21 '14 at 17:41









      Simon Forsberg

      48.5k7129286




      48.5k7129286










      asked Nov 30 '13 at 4:38









      Mathieu GuindonMathieu Guindon

      60.5k12147416




      60.5k12147416






















          4 Answers
          4






          active

          oldest

          votes


















          43














          Let's look at your code from an extensibility point of view:



          If Sheldon decides to add a new item to the game then you have to go to n classes to adjust the comparisons and winning verbs. I usually try to avoid such designs because whenever you require a developer to change stuff in n places when something new is added then he/she is bound to forget one place.



          So how can we change the design? Well, a game seems to be suited for a rules approach especially since the rules are fairly simple and always of the same structure in this case:



          enum Item
          {
          Rock, Paper, Scissors, Lizard, Spock
          }

          class Rule
          {
          public readonly Item Winner;
          public readonly Item Loser;
          public readonly string WinningPhrase;

          public Rule(item winner, string winningPhrase, item loser)
          {
          Winner = winner;
          Loser = loser;
          WinningPhrase = winningPhrase;
          }

          public override string ToString()
          {
          return string.Format("{0} {1} {2}", Winner, WinningPhrase, Loser);
          }
          }


          Assuming you have a list of rules somewhere:



              static List<Rule> Rules = new List<Rule> {
          new Rule(Item.Rock, "crushes", Item.Scissors),
          new Rule(Item.Spock, "vaporizes", Item.Rock),
          new Rule(Item.Paper, "disproves", Item.Spock),
          new Rule(Item.Lizard, "eats", Item.Paper),
          new Rule(Item.Scissors, "decapitate", Item.Lizard),
          new Rule(Item.Spock, "smashes", Item.Scissors),
          new Rule(Item.Lizard, "poisons", Item.Spock),
          new Rule(Item.Rock, "crushes", Item.Lizard),
          new Rule(Item.Paper, "covers", Item.Rock),
          new Rule(Item.Scissors, "cut", Item.Paper)
          }


          You now can make a decision:



          class Decision
          {
          private bool? _HasPlayerWon;
          private Rule _WinningRule;

          private Decision(bool? hasPlayerWon, Rule winningRule)
          {
          _HasPlayerWon = hasPlayerWon;
          _WinningRule = winningRule;
          }

          public static Decision Decide(item player, item sheldon)
          {
          var rule = FindWinningRule(player, sheldon);
          if (rule != null)
          {
          return new Decision(true, rule);
          }

          rule = FindWinningRule(sheldon, player);
          if (rule != null)
          {
          return new Decision(false, rule);
          }

          return new Decision(null, null);
          }

          private static Rule FindWinningRule(item player, item opponent)
          {
          return Rules.FirstOrDefault(r => r.Winner == player && r.Loser == opponent);
          }

          public override string ToString()
          {
          if (_HasPlayerWon == null)
          {
          return "Meh. Tie!";
          }
          else if (_HasPlayerWon == true)
          {
          return string.Format("{0}. You win!", _WinningRule);
          }
          else
          {
          return string.Format("{0}. You lose!", _WinningRule);
          }
          }
          }


          If you want to add another item to the game then you add another entry into the enum and some additional rules and you are done.



          One thing to improve with my version is that rules just effectively define winning rules and all other cases are implicitly ties which in the context of this game makes sense but could be made more explicit.






          share|improve this answer





















          • 1





            @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

            – ChrisWue
            Nov 30 '13 at 8:50











          • This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

            – Karl Damgaard Asmussen
            Nov 30 '13 at 11:04



















          14















          this sounded like IComparable<T>




          But it's not. The documentation of Compare() states that the relation has to be transitive:




          If A.CompareTo(B) returns a value x that is not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) is required to return a value of the same sign as x and y.




          This isn't true in your case, which means that your types shouldn't implement IComparable<T> and that ideally, the comparing method shouldn't be called CompareTo() to avoid confusion.






          share|improve this answer































            7














            We could perhaps do away with the bulk of the children class code by implementing a bit of logic in the base class.



            A starting point for discussion would be something along the lines of:



            public abstract class SelectionBase : IComparable<SelectionBase>
            {
            private readonly List<WinningPlay> _winsAgainst;

            protected SelectionBase(List<WinningPlay> winsAgainst)
            {
            _winsAgainst = winsAgainst;
            }

            public virtual int CompareTo(SelectionBase other)
            {
            if (GetType() == other.GetType()) return 0; // draws against itself

            if (_winsAgainst.Any(p => p.Winner == other.GetType())) return 1; // wins

            return -1; // otherwise loses.
            }

            public virtual string Name { get { return GetType().Name; } }

            public virtual string GetWinningVerb(SelectionBase other)
            {
            var winner = _winsAgainst.SingleOrDefault(p => p.Winner == other.GetType());

            if (winner == null)
            throw new InvalidOperationException("Are we playing the same game?");
            else
            return winner.Verb;
            }

            protected class WinningPlay
            {
            public Type Winner { get; private set; }
            public string Verb { get; private set; }

            public WinningPlay(Type type, string verb)
            {
            Winner = type;
            Verb = verb;
            }
            }


            And the children classes become:



            public class Rock : SelectionBase
            {

            public Rock()
            : base(new List<WinningPlay>
            {
            new WinningPlay(typeof(Scissors), "crushes"),
            new WinningPlay(typeof(Lizard), "crushes")
            })
            {
            }
            }

            public class Paper : SelectionBase
            {
            public Paper()
            : base(new List<WinningPlay>
            {
            new WinningPlay(typeof (Rock), "covers"),
            new WinningPlay(typeof (Spock), "disproves")
            })
            {
            }
            }

            public class Scissors : SelectionBase
            {
            public Scissors()
            : base(new List<WinningPlay>
            {
            new WinningPlay(typeof (Rock), "cuts"),
            new WinningPlay(typeof (Spock), "decapitates")
            })
            {
            }
            }

            public class Lizard : SelectionBase
            {
            public Lizard()
            : base(new List<WinningPlay>
            {
            new WinningPlay(typeof (Paper), "eats"),
            new WinningPlay(typeof (Spock), "poisons")
            })
            {
            }
            }

            public class Spock : SelectionBase
            {
            public Spock()
            : base(new List<WinningPlay>
            {
            new WinningPlay(typeof (Rock), "Vaporizes"),
            new WinningPlay(typeof (Scissors), "smashes")
            })
            {
            }
            }





            share|improve this answer
























            • Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

              – Mathieu Guindon
              Nov 30 '13 at 7:55













            • @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

              – dreza
              Nov 30 '13 at 8:17



















            4














            I'm somewhat old-school in that I don't think OO is the right answer to every problem. Here's my effort:



            void Main()
            {
            foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
            foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
            Result result;
            string report;
            Play(left, right, out result, out report);
            Console.WriteLine(left + " vs " + right + ": " + report + " -- " + result);
            }
            }
            }

            enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

            static string[,] Defeats;

            static void InitDefeats()
            {
            Defeats = new string[(int)A.Count, (int)A.Count];
            Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
            rule(A.Rock, "crushes", A.Lizard);
            rule(A.Rock, "blunts", A.Scissors);
            rule(A.Paper, "wraps", A.Rock);
            rule(A.Paper, "disproves", A.Spock);
            rule(A.Scissors, "cut", A.Paper);
            rule(A.Scissors, "decapitates", A.Lizard);
            rule(A.Lizard, "poisons", A.Spock);
            rule(A.Lizard, "eats", A.Paper);
            rule(A.Spock, "smashes", A.Scissors);
            rule(A.Spock, "vaporizes", A.Rock);
            }

            enum Result { LeftWins, Tie, RightWins };

            static void Play(A left, A right, out Result result, out string report)
            {
            if (Defeats == null) InitDefeats();
            var lr = Defeats[(int)left, (int)right];
            var rl = Defeats[(int)right, (int)left];
            if (lr != null) {
            result = Result.LeftWins;
            report = (left + " " + lr + " " + right).ToLower();
            return;
            }
            if (rl != null) {
            result = Result.RightWins;
            report = (right + " " + rl + " " + left).ToLower();
            return;
            }
            result = Result.Tie;
            report = (left + " vs " + right + " is a tie").ToLower();
            }


            Which prints out:



            Rock vs Rock: rock vs rock is a tie -- Tie
            Rock vs Paper: paper wraps rock -- RightWins
            Rock vs Scissors: rock blunts scissors -- LeftWins
            Rock vs Lizard: rock crushes lizard -- LeftWins
            Rock vs Spock: spock vaporizes rock -- RightWins
            Paper vs Rock: paper wraps rock -- LeftWins
            Paper vs Paper: paper vs paper is a tie -- Tie
            Paper vs Scissors: scissors cut paper -- RightWins
            Paper vs Lizard: lizard eats paper -- RightWins
            Paper vs Spock: paper disproves spock -- LeftWins
            Scissors vs Rock: rock blunts scissors -- RightWins
            Scissors vs Paper: scissors cut paper -- LeftWins
            Scissors vs Scissors: scissors vs scissors is a tie -- Tie
            Scissors vs Lizard: scissors decapitates lizard -- LeftWins
            Scissors vs Spock: spock smashes scissors -- RightWins
            Lizard vs Rock: rock crushes lizard -- RightWins
            Lizard vs Paper: lizard eats paper -- LeftWins
            Lizard vs Scissors: scissors decapitates lizard -- RightWins
            Lizard vs Lizard: lizard vs lizard is a tie -- Tie
            Lizard vs Spock: lizard poisons spock -- LeftWins
            Spock vs Rock: spock vaporizes rock -- LeftWins
            Spock vs Paper: paper disproves spock -- RightWins
            Spock vs Scissors: spock smashes scissors -- LeftWins
            Spock vs Lizard: lizard poisons spock -- RightWins
            Spock vs Spock: spock vs spock is a tie -- Tie


            EDIT 14-Jul-2014: in response to Malachi's comment, I've rewritten the Play method to return an object rather than two out parameters. The code is the same length and it's arguable whether it's any clearer. Here's the updated version:



            void Main()
            {
            foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
            foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
            var outcome = Play(left, right);
            Console.WriteLine(left + " vs " + right + ": " + outcome.Report + " -- " + outcome.Result);
            }
            }
            }

            enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

            static string[,] Defeats;

            static void InitDefeats()
            {
            Defeats = new string[(int)A.Count, (int)A.Count];
            Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
            rule(A.Rock, "crushes", A.Lizard);
            rule(A.Rock, "blunts", A.Scissors);
            rule(A.Paper, "wraps", A.Rock);
            rule(A.Paper, "disproves", A.Spock);
            rule(A.Scissors, "cut", A.Paper);
            rule(A.Scissors, "decapitates", A.Lizard);
            rule(A.Lizard, "poisons", A.Spock);
            rule(A.Lizard, "eats", A.Paper);
            rule(A.Spock, "smashes", A.Scissors);
            rule(A.Spock, "vaporizes", A.Rock);
            }

            class Outcome {
            internal string Report;
            internal Result Result;
            internal Outcome(A left, A right, string lr, string rl)
            {
            Report = ( lr != null ? left + " " + lr + " " + right
            : rl != null ? right + " " + rl + " " + left
            : left + " vs " + right + " is a tie"
            ).ToLower();
            Result = ( lr != null ? Result.LeftWins
            : rl != null ? Result.RightWins
            : Result.Tie
            );
            }
            }

            enum Result { LeftWins, Tie, RightWins };

            static Outcome Play(A left, A right)
            {
            if (Defeats == null) InitDefeats();
            var lr = Defeats[(int)left, (int)right];
            var rl = Defeats[(int)right, (int)left];
            return new Outcome(left, right, lr, rl);
            }





            share|improve this answer





















            • 1





              I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

              – Mathieu Guindon
              Feb 4 '14 at 2:53











            • @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

              – Rafe
              Feb 4 '14 at 3:37











            • @Rafe This is not a forum, this is a Q&A site.

              – syb0rg
              Feb 4 '14 at 3:43











            • @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

              – Rafe
              Feb 4 '14 at 3:59











            • @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

              – syb0rg
              Feb 4 '14 at 4:01











            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f36395%2frock-paper-scissors-lizard-spock-challenge%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            4 Answers
            4






            active

            oldest

            votes








            4 Answers
            4






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            43














            Let's look at your code from an extensibility point of view:



            If Sheldon decides to add a new item to the game then you have to go to n classes to adjust the comparisons and winning verbs. I usually try to avoid such designs because whenever you require a developer to change stuff in n places when something new is added then he/she is bound to forget one place.



            So how can we change the design? Well, a game seems to be suited for a rules approach especially since the rules are fairly simple and always of the same structure in this case:



            enum Item
            {
            Rock, Paper, Scissors, Lizard, Spock
            }

            class Rule
            {
            public readonly Item Winner;
            public readonly Item Loser;
            public readonly string WinningPhrase;

            public Rule(item winner, string winningPhrase, item loser)
            {
            Winner = winner;
            Loser = loser;
            WinningPhrase = winningPhrase;
            }

            public override string ToString()
            {
            return string.Format("{0} {1} {2}", Winner, WinningPhrase, Loser);
            }
            }


            Assuming you have a list of rules somewhere:



                static List<Rule> Rules = new List<Rule> {
            new Rule(Item.Rock, "crushes", Item.Scissors),
            new Rule(Item.Spock, "vaporizes", Item.Rock),
            new Rule(Item.Paper, "disproves", Item.Spock),
            new Rule(Item.Lizard, "eats", Item.Paper),
            new Rule(Item.Scissors, "decapitate", Item.Lizard),
            new Rule(Item.Spock, "smashes", Item.Scissors),
            new Rule(Item.Lizard, "poisons", Item.Spock),
            new Rule(Item.Rock, "crushes", Item.Lizard),
            new Rule(Item.Paper, "covers", Item.Rock),
            new Rule(Item.Scissors, "cut", Item.Paper)
            }


            You now can make a decision:



            class Decision
            {
            private bool? _HasPlayerWon;
            private Rule _WinningRule;

            private Decision(bool? hasPlayerWon, Rule winningRule)
            {
            _HasPlayerWon = hasPlayerWon;
            _WinningRule = winningRule;
            }

            public static Decision Decide(item player, item sheldon)
            {
            var rule = FindWinningRule(player, sheldon);
            if (rule != null)
            {
            return new Decision(true, rule);
            }

            rule = FindWinningRule(sheldon, player);
            if (rule != null)
            {
            return new Decision(false, rule);
            }

            return new Decision(null, null);
            }

            private static Rule FindWinningRule(item player, item opponent)
            {
            return Rules.FirstOrDefault(r => r.Winner == player && r.Loser == opponent);
            }

            public override string ToString()
            {
            if (_HasPlayerWon == null)
            {
            return "Meh. Tie!";
            }
            else if (_HasPlayerWon == true)
            {
            return string.Format("{0}. You win!", _WinningRule);
            }
            else
            {
            return string.Format("{0}. You lose!", _WinningRule);
            }
            }
            }


            If you want to add another item to the game then you add another entry into the enum and some additional rules and you are done.



            One thing to improve with my version is that rules just effectively define winning rules and all other cases are implicitly ties which in the context of this game makes sense but could be made more explicit.






            share|improve this answer





















            • 1





              @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

              – ChrisWue
              Nov 30 '13 at 8:50











            • This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

              – Karl Damgaard Asmussen
              Nov 30 '13 at 11:04
















            43














            Let's look at your code from an extensibility point of view:



            If Sheldon decides to add a new item to the game then you have to go to n classes to adjust the comparisons and winning verbs. I usually try to avoid such designs because whenever you require a developer to change stuff in n places when something new is added then he/she is bound to forget one place.



            So how can we change the design? Well, a game seems to be suited for a rules approach especially since the rules are fairly simple and always of the same structure in this case:



            enum Item
            {
            Rock, Paper, Scissors, Lizard, Spock
            }

            class Rule
            {
            public readonly Item Winner;
            public readonly Item Loser;
            public readonly string WinningPhrase;

            public Rule(item winner, string winningPhrase, item loser)
            {
            Winner = winner;
            Loser = loser;
            WinningPhrase = winningPhrase;
            }

            public override string ToString()
            {
            return string.Format("{0} {1} {2}", Winner, WinningPhrase, Loser);
            }
            }


            Assuming you have a list of rules somewhere:



                static List<Rule> Rules = new List<Rule> {
            new Rule(Item.Rock, "crushes", Item.Scissors),
            new Rule(Item.Spock, "vaporizes", Item.Rock),
            new Rule(Item.Paper, "disproves", Item.Spock),
            new Rule(Item.Lizard, "eats", Item.Paper),
            new Rule(Item.Scissors, "decapitate", Item.Lizard),
            new Rule(Item.Spock, "smashes", Item.Scissors),
            new Rule(Item.Lizard, "poisons", Item.Spock),
            new Rule(Item.Rock, "crushes", Item.Lizard),
            new Rule(Item.Paper, "covers", Item.Rock),
            new Rule(Item.Scissors, "cut", Item.Paper)
            }


            You now can make a decision:



            class Decision
            {
            private bool? _HasPlayerWon;
            private Rule _WinningRule;

            private Decision(bool? hasPlayerWon, Rule winningRule)
            {
            _HasPlayerWon = hasPlayerWon;
            _WinningRule = winningRule;
            }

            public static Decision Decide(item player, item sheldon)
            {
            var rule = FindWinningRule(player, sheldon);
            if (rule != null)
            {
            return new Decision(true, rule);
            }

            rule = FindWinningRule(sheldon, player);
            if (rule != null)
            {
            return new Decision(false, rule);
            }

            return new Decision(null, null);
            }

            private static Rule FindWinningRule(item player, item opponent)
            {
            return Rules.FirstOrDefault(r => r.Winner == player && r.Loser == opponent);
            }

            public override string ToString()
            {
            if (_HasPlayerWon == null)
            {
            return "Meh. Tie!";
            }
            else if (_HasPlayerWon == true)
            {
            return string.Format("{0}. You win!", _WinningRule);
            }
            else
            {
            return string.Format("{0}. You lose!", _WinningRule);
            }
            }
            }


            If you want to add another item to the game then you add another entry into the enum and some additional rules and you are done.



            One thing to improve with my version is that rules just effectively define winning rules and all other cases are implicitly ties which in the context of this game makes sense but could be made more explicit.






            share|improve this answer





















            • 1





              @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

              – ChrisWue
              Nov 30 '13 at 8:50











            • This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

              – Karl Damgaard Asmussen
              Nov 30 '13 at 11:04














            43












            43








            43







            Let's look at your code from an extensibility point of view:



            If Sheldon decides to add a new item to the game then you have to go to n classes to adjust the comparisons and winning verbs. I usually try to avoid such designs because whenever you require a developer to change stuff in n places when something new is added then he/she is bound to forget one place.



            So how can we change the design? Well, a game seems to be suited for a rules approach especially since the rules are fairly simple and always of the same structure in this case:



            enum Item
            {
            Rock, Paper, Scissors, Lizard, Spock
            }

            class Rule
            {
            public readonly Item Winner;
            public readonly Item Loser;
            public readonly string WinningPhrase;

            public Rule(item winner, string winningPhrase, item loser)
            {
            Winner = winner;
            Loser = loser;
            WinningPhrase = winningPhrase;
            }

            public override string ToString()
            {
            return string.Format("{0} {1} {2}", Winner, WinningPhrase, Loser);
            }
            }


            Assuming you have a list of rules somewhere:



                static List<Rule> Rules = new List<Rule> {
            new Rule(Item.Rock, "crushes", Item.Scissors),
            new Rule(Item.Spock, "vaporizes", Item.Rock),
            new Rule(Item.Paper, "disproves", Item.Spock),
            new Rule(Item.Lizard, "eats", Item.Paper),
            new Rule(Item.Scissors, "decapitate", Item.Lizard),
            new Rule(Item.Spock, "smashes", Item.Scissors),
            new Rule(Item.Lizard, "poisons", Item.Spock),
            new Rule(Item.Rock, "crushes", Item.Lizard),
            new Rule(Item.Paper, "covers", Item.Rock),
            new Rule(Item.Scissors, "cut", Item.Paper)
            }


            You now can make a decision:



            class Decision
            {
            private bool? _HasPlayerWon;
            private Rule _WinningRule;

            private Decision(bool? hasPlayerWon, Rule winningRule)
            {
            _HasPlayerWon = hasPlayerWon;
            _WinningRule = winningRule;
            }

            public static Decision Decide(item player, item sheldon)
            {
            var rule = FindWinningRule(player, sheldon);
            if (rule != null)
            {
            return new Decision(true, rule);
            }

            rule = FindWinningRule(sheldon, player);
            if (rule != null)
            {
            return new Decision(false, rule);
            }

            return new Decision(null, null);
            }

            private static Rule FindWinningRule(item player, item opponent)
            {
            return Rules.FirstOrDefault(r => r.Winner == player && r.Loser == opponent);
            }

            public override string ToString()
            {
            if (_HasPlayerWon == null)
            {
            return "Meh. Tie!";
            }
            else if (_HasPlayerWon == true)
            {
            return string.Format("{0}. You win!", _WinningRule);
            }
            else
            {
            return string.Format("{0}. You lose!", _WinningRule);
            }
            }
            }


            If you want to add another item to the game then you add another entry into the enum and some additional rules and you are done.



            One thing to improve with my version is that rules just effectively define winning rules and all other cases are implicitly ties which in the context of this game makes sense but could be made more explicit.






            share|improve this answer















            Let's look at your code from an extensibility point of view:



            If Sheldon decides to add a new item to the game then you have to go to n classes to adjust the comparisons and winning verbs. I usually try to avoid such designs because whenever you require a developer to change stuff in n places when something new is added then he/she is bound to forget one place.



            So how can we change the design? Well, a game seems to be suited for a rules approach especially since the rules are fairly simple and always of the same structure in this case:



            enum Item
            {
            Rock, Paper, Scissors, Lizard, Spock
            }

            class Rule
            {
            public readonly Item Winner;
            public readonly Item Loser;
            public readonly string WinningPhrase;

            public Rule(item winner, string winningPhrase, item loser)
            {
            Winner = winner;
            Loser = loser;
            WinningPhrase = winningPhrase;
            }

            public override string ToString()
            {
            return string.Format("{0} {1} {2}", Winner, WinningPhrase, Loser);
            }
            }


            Assuming you have a list of rules somewhere:



                static List<Rule> Rules = new List<Rule> {
            new Rule(Item.Rock, "crushes", Item.Scissors),
            new Rule(Item.Spock, "vaporizes", Item.Rock),
            new Rule(Item.Paper, "disproves", Item.Spock),
            new Rule(Item.Lizard, "eats", Item.Paper),
            new Rule(Item.Scissors, "decapitate", Item.Lizard),
            new Rule(Item.Spock, "smashes", Item.Scissors),
            new Rule(Item.Lizard, "poisons", Item.Spock),
            new Rule(Item.Rock, "crushes", Item.Lizard),
            new Rule(Item.Paper, "covers", Item.Rock),
            new Rule(Item.Scissors, "cut", Item.Paper)
            }


            You now can make a decision:



            class Decision
            {
            private bool? _HasPlayerWon;
            private Rule _WinningRule;

            private Decision(bool? hasPlayerWon, Rule winningRule)
            {
            _HasPlayerWon = hasPlayerWon;
            _WinningRule = winningRule;
            }

            public static Decision Decide(item player, item sheldon)
            {
            var rule = FindWinningRule(player, sheldon);
            if (rule != null)
            {
            return new Decision(true, rule);
            }

            rule = FindWinningRule(sheldon, player);
            if (rule != null)
            {
            return new Decision(false, rule);
            }

            return new Decision(null, null);
            }

            private static Rule FindWinningRule(item player, item opponent)
            {
            return Rules.FirstOrDefault(r => r.Winner == player && r.Loser == opponent);
            }

            public override string ToString()
            {
            if (_HasPlayerWon == null)
            {
            return "Meh. Tie!";
            }
            else if (_HasPlayerWon == true)
            {
            return string.Format("{0}. You win!", _WinningRule);
            }
            else
            {
            return string.Format("{0}. You lose!", _WinningRule);
            }
            }
            }


            If you want to add another item to the game then you add another entry into the enum and some additional rules and you are done.



            One thing to improve with my version is that rules just effectively define winning rules and all other cases are implicitly ties which in the context of this game makes sense but could be made more explicit.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 30 '13 at 11:06

























            answered Nov 30 '13 at 8:37









            ChrisWueChrisWue

            19.1k333100




            19.1k333100








            • 1





              @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

              – ChrisWue
              Nov 30 '13 at 8:50











            • This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

              – Karl Damgaard Asmussen
              Nov 30 '13 at 11:04














            • 1





              @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

              – ChrisWue
              Nov 30 '13 at 8:50











            • This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

              – Karl Damgaard Asmussen
              Nov 30 '13 at 11:04








            1




            1





            @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

            – ChrisWue
            Nov 30 '13 at 8:50





            @dreza: Yeah, first I had the constructor as (winner, loser, phrase) but then I was writing the rules and thought, well that would read better if swapped :)

            – ChrisWue
            Nov 30 '13 at 8:50













            This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

            – Karl Damgaard Asmussen
            Nov 30 '13 at 11:04





            This is pretty clearly a better factoring of the problem than OP and dreza's answers. It makes the decision complex the operative entity, separate from the choice data.

            – Karl Damgaard Asmussen
            Nov 30 '13 at 11:04













            14















            this sounded like IComparable<T>




            But it's not. The documentation of Compare() states that the relation has to be transitive:




            If A.CompareTo(B) returns a value x that is not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) is required to return a value of the same sign as x and y.




            This isn't true in your case, which means that your types shouldn't implement IComparable<T> and that ideally, the comparing method shouldn't be called CompareTo() to avoid confusion.






            share|improve this answer




























              14















              this sounded like IComparable<T>




              But it's not. The documentation of Compare() states that the relation has to be transitive:




              If A.CompareTo(B) returns a value x that is not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) is required to return a value of the same sign as x and y.




              This isn't true in your case, which means that your types shouldn't implement IComparable<T> and that ideally, the comparing method shouldn't be called CompareTo() to avoid confusion.






              share|improve this answer


























                14












                14








                14








                this sounded like IComparable<T>




                But it's not. The documentation of Compare() states that the relation has to be transitive:




                If A.CompareTo(B) returns a value x that is not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) is required to return a value of the same sign as x and y.




                This isn't true in your case, which means that your types shouldn't implement IComparable<T> and that ideally, the comparing method shouldn't be called CompareTo() to avoid confusion.






                share|improve this answer














                this sounded like IComparable<T>




                But it's not. The documentation of Compare() states that the relation has to be transitive:




                If A.CompareTo(B) returns a value x that is not equal to zero, and B.CompareTo(C) returns a value y of the same sign as x, then A.CompareTo(C) is required to return a value of the same sign as x and y.




                This isn't true in your case, which means that your types shouldn't implement IComparable<T> and that ideally, the comparing method shouldn't be called CompareTo() to avoid confusion.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 30 '13 at 11:50









                svicksvick

                22.7k43879




                22.7k43879























                    7














                    We could perhaps do away with the bulk of the children class code by implementing a bit of logic in the base class.



                    A starting point for discussion would be something along the lines of:



                    public abstract class SelectionBase : IComparable<SelectionBase>
                    {
                    private readonly List<WinningPlay> _winsAgainst;

                    protected SelectionBase(List<WinningPlay> winsAgainst)
                    {
                    _winsAgainst = winsAgainst;
                    }

                    public virtual int CompareTo(SelectionBase other)
                    {
                    if (GetType() == other.GetType()) return 0; // draws against itself

                    if (_winsAgainst.Any(p => p.Winner == other.GetType())) return 1; // wins

                    return -1; // otherwise loses.
                    }

                    public virtual string Name { get { return GetType().Name; } }

                    public virtual string GetWinningVerb(SelectionBase other)
                    {
                    var winner = _winsAgainst.SingleOrDefault(p => p.Winner == other.GetType());

                    if (winner == null)
                    throw new InvalidOperationException("Are we playing the same game?");
                    else
                    return winner.Verb;
                    }

                    protected class WinningPlay
                    {
                    public Type Winner { get; private set; }
                    public string Verb { get; private set; }

                    public WinningPlay(Type type, string verb)
                    {
                    Winner = type;
                    Verb = verb;
                    }
                    }


                    And the children classes become:



                    public class Rock : SelectionBase
                    {

                    public Rock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof(Scissors), "crushes"),
                    new WinningPlay(typeof(Lizard), "crushes")
                    })
                    {
                    }
                    }

                    public class Paper : SelectionBase
                    {
                    public Paper()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "covers"),
                    new WinningPlay(typeof (Spock), "disproves")
                    })
                    {
                    }
                    }

                    public class Scissors : SelectionBase
                    {
                    public Scissors()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "cuts"),
                    new WinningPlay(typeof (Spock), "decapitates")
                    })
                    {
                    }
                    }

                    public class Lizard : SelectionBase
                    {
                    public Lizard()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Paper), "eats"),
                    new WinningPlay(typeof (Spock), "poisons")
                    })
                    {
                    }
                    }

                    public class Spock : SelectionBase
                    {
                    public Spock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "Vaporizes"),
                    new WinningPlay(typeof (Scissors), "smashes")
                    })
                    {
                    }
                    }





                    share|improve this answer
























                    • Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                      – Mathieu Guindon
                      Nov 30 '13 at 7:55













                    • @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                      – dreza
                      Nov 30 '13 at 8:17
















                    7














                    We could perhaps do away with the bulk of the children class code by implementing a bit of logic in the base class.



                    A starting point for discussion would be something along the lines of:



                    public abstract class SelectionBase : IComparable<SelectionBase>
                    {
                    private readonly List<WinningPlay> _winsAgainst;

                    protected SelectionBase(List<WinningPlay> winsAgainst)
                    {
                    _winsAgainst = winsAgainst;
                    }

                    public virtual int CompareTo(SelectionBase other)
                    {
                    if (GetType() == other.GetType()) return 0; // draws against itself

                    if (_winsAgainst.Any(p => p.Winner == other.GetType())) return 1; // wins

                    return -1; // otherwise loses.
                    }

                    public virtual string Name { get { return GetType().Name; } }

                    public virtual string GetWinningVerb(SelectionBase other)
                    {
                    var winner = _winsAgainst.SingleOrDefault(p => p.Winner == other.GetType());

                    if (winner == null)
                    throw new InvalidOperationException("Are we playing the same game?");
                    else
                    return winner.Verb;
                    }

                    protected class WinningPlay
                    {
                    public Type Winner { get; private set; }
                    public string Verb { get; private set; }

                    public WinningPlay(Type type, string verb)
                    {
                    Winner = type;
                    Verb = verb;
                    }
                    }


                    And the children classes become:



                    public class Rock : SelectionBase
                    {

                    public Rock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof(Scissors), "crushes"),
                    new WinningPlay(typeof(Lizard), "crushes")
                    })
                    {
                    }
                    }

                    public class Paper : SelectionBase
                    {
                    public Paper()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "covers"),
                    new WinningPlay(typeof (Spock), "disproves")
                    })
                    {
                    }
                    }

                    public class Scissors : SelectionBase
                    {
                    public Scissors()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "cuts"),
                    new WinningPlay(typeof (Spock), "decapitates")
                    })
                    {
                    }
                    }

                    public class Lizard : SelectionBase
                    {
                    public Lizard()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Paper), "eats"),
                    new WinningPlay(typeof (Spock), "poisons")
                    })
                    {
                    }
                    }

                    public class Spock : SelectionBase
                    {
                    public Spock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "Vaporizes"),
                    new WinningPlay(typeof (Scissors), "smashes")
                    })
                    {
                    }
                    }





                    share|improve this answer
























                    • Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                      – Mathieu Guindon
                      Nov 30 '13 at 7:55













                    • @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                      – dreza
                      Nov 30 '13 at 8:17














                    7












                    7








                    7







                    We could perhaps do away with the bulk of the children class code by implementing a bit of logic in the base class.



                    A starting point for discussion would be something along the lines of:



                    public abstract class SelectionBase : IComparable<SelectionBase>
                    {
                    private readonly List<WinningPlay> _winsAgainst;

                    protected SelectionBase(List<WinningPlay> winsAgainst)
                    {
                    _winsAgainst = winsAgainst;
                    }

                    public virtual int CompareTo(SelectionBase other)
                    {
                    if (GetType() == other.GetType()) return 0; // draws against itself

                    if (_winsAgainst.Any(p => p.Winner == other.GetType())) return 1; // wins

                    return -1; // otherwise loses.
                    }

                    public virtual string Name { get { return GetType().Name; } }

                    public virtual string GetWinningVerb(SelectionBase other)
                    {
                    var winner = _winsAgainst.SingleOrDefault(p => p.Winner == other.GetType());

                    if (winner == null)
                    throw new InvalidOperationException("Are we playing the same game?");
                    else
                    return winner.Verb;
                    }

                    protected class WinningPlay
                    {
                    public Type Winner { get; private set; }
                    public string Verb { get; private set; }

                    public WinningPlay(Type type, string verb)
                    {
                    Winner = type;
                    Verb = verb;
                    }
                    }


                    And the children classes become:



                    public class Rock : SelectionBase
                    {

                    public Rock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof(Scissors), "crushes"),
                    new WinningPlay(typeof(Lizard), "crushes")
                    })
                    {
                    }
                    }

                    public class Paper : SelectionBase
                    {
                    public Paper()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "covers"),
                    new WinningPlay(typeof (Spock), "disproves")
                    })
                    {
                    }
                    }

                    public class Scissors : SelectionBase
                    {
                    public Scissors()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "cuts"),
                    new WinningPlay(typeof (Spock), "decapitates")
                    })
                    {
                    }
                    }

                    public class Lizard : SelectionBase
                    {
                    public Lizard()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Paper), "eats"),
                    new WinningPlay(typeof (Spock), "poisons")
                    })
                    {
                    }
                    }

                    public class Spock : SelectionBase
                    {
                    public Spock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "Vaporizes"),
                    new WinningPlay(typeof (Scissors), "smashes")
                    })
                    {
                    }
                    }





                    share|improve this answer













                    We could perhaps do away with the bulk of the children class code by implementing a bit of logic in the base class.



                    A starting point for discussion would be something along the lines of:



                    public abstract class SelectionBase : IComparable<SelectionBase>
                    {
                    private readonly List<WinningPlay> _winsAgainst;

                    protected SelectionBase(List<WinningPlay> winsAgainst)
                    {
                    _winsAgainst = winsAgainst;
                    }

                    public virtual int CompareTo(SelectionBase other)
                    {
                    if (GetType() == other.GetType()) return 0; // draws against itself

                    if (_winsAgainst.Any(p => p.Winner == other.GetType())) return 1; // wins

                    return -1; // otherwise loses.
                    }

                    public virtual string Name { get { return GetType().Name; } }

                    public virtual string GetWinningVerb(SelectionBase other)
                    {
                    var winner = _winsAgainst.SingleOrDefault(p => p.Winner == other.GetType());

                    if (winner == null)
                    throw new InvalidOperationException("Are we playing the same game?");
                    else
                    return winner.Verb;
                    }

                    protected class WinningPlay
                    {
                    public Type Winner { get; private set; }
                    public string Verb { get; private set; }

                    public WinningPlay(Type type, string verb)
                    {
                    Winner = type;
                    Verb = verb;
                    }
                    }


                    And the children classes become:



                    public class Rock : SelectionBase
                    {

                    public Rock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof(Scissors), "crushes"),
                    new WinningPlay(typeof(Lizard), "crushes")
                    })
                    {
                    }
                    }

                    public class Paper : SelectionBase
                    {
                    public Paper()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "covers"),
                    new WinningPlay(typeof (Spock), "disproves")
                    })
                    {
                    }
                    }

                    public class Scissors : SelectionBase
                    {
                    public Scissors()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "cuts"),
                    new WinningPlay(typeof (Spock), "decapitates")
                    })
                    {
                    }
                    }

                    public class Lizard : SelectionBase
                    {
                    public Lizard()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Paper), "eats"),
                    new WinningPlay(typeof (Spock), "poisons")
                    })
                    {
                    }
                    }

                    public class Spock : SelectionBase
                    {
                    public Spock()
                    : base(new List<WinningPlay>
                    {
                    new WinningPlay(typeof (Rock), "Vaporizes"),
                    new WinningPlay(typeof (Scissors), "smashes")
                    })
                    {
                    }
                    }






                    share|improve this answer












                    share|improve this answer



                    share|improve this answer










                    answered Nov 30 '13 at 7:51









                    drezadreza

                    5,9372241




                    5,9372241













                    • Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                      – Mathieu Guindon
                      Nov 30 '13 at 7:55













                    • @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                      – dreza
                      Nov 30 '13 at 8:17



















                    • Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                      – Mathieu Guindon
                      Nov 30 '13 at 7:55













                    • @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                      – dreza
                      Nov 30 '13 at 8:17

















                    Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                    – Mathieu Guindon
                    Nov 30 '13 at 7:55







                    Amazing. I was exactly 2 seconds away from submitting an edit that would have totally mootinated this superb answer! I mean, this is almost exactly the code I was just about to commit - it's the next logical refactoring step. I actually also got rid of GetWinningVerb and all these useless exceptions. You're reading my mind!

                    – Mathieu Guindon
                    Nov 30 '13 at 7:55















                    @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                    – dreza
                    Nov 30 '13 at 8:17





                    @retailcoder cheers. it's a start. I guess good think about refactoring is doing it once, taking a step back and then doing it again. I sort of wonder. Do you even need children classes. Would a simple Enum do?

                    – dreza
                    Nov 30 '13 at 8:17











                    4














                    I'm somewhat old-school in that I don't think OO is the right answer to every problem. Here's my effort:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    Result result;
                    string report;
                    Play(left, right, out result, out report);
                    Console.WriteLine(left + " vs " + right + ": " + report + " -- " + result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static void Play(A left, A right, out Result result, out string report)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    if (lr != null) {
                    result = Result.LeftWins;
                    report = (left + " " + lr + " " + right).ToLower();
                    return;
                    }
                    if (rl != null) {
                    result = Result.RightWins;
                    report = (right + " " + rl + " " + left).ToLower();
                    return;
                    }
                    result = Result.Tie;
                    report = (left + " vs " + right + " is a tie").ToLower();
                    }


                    Which prints out:



                    Rock vs Rock: rock vs rock is a tie -- Tie
                    Rock vs Paper: paper wraps rock -- RightWins
                    Rock vs Scissors: rock blunts scissors -- LeftWins
                    Rock vs Lizard: rock crushes lizard -- LeftWins
                    Rock vs Spock: spock vaporizes rock -- RightWins
                    Paper vs Rock: paper wraps rock -- LeftWins
                    Paper vs Paper: paper vs paper is a tie -- Tie
                    Paper vs Scissors: scissors cut paper -- RightWins
                    Paper vs Lizard: lizard eats paper -- RightWins
                    Paper vs Spock: paper disproves spock -- LeftWins
                    Scissors vs Rock: rock blunts scissors -- RightWins
                    Scissors vs Paper: scissors cut paper -- LeftWins
                    Scissors vs Scissors: scissors vs scissors is a tie -- Tie
                    Scissors vs Lizard: scissors decapitates lizard -- LeftWins
                    Scissors vs Spock: spock smashes scissors -- RightWins
                    Lizard vs Rock: rock crushes lizard -- RightWins
                    Lizard vs Paper: lizard eats paper -- LeftWins
                    Lizard vs Scissors: scissors decapitates lizard -- RightWins
                    Lizard vs Lizard: lizard vs lizard is a tie -- Tie
                    Lizard vs Spock: lizard poisons spock -- LeftWins
                    Spock vs Rock: spock vaporizes rock -- LeftWins
                    Spock vs Paper: paper disproves spock -- RightWins
                    Spock vs Scissors: spock smashes scissors -- LeftWins
                    Spock vs Lizard: lizard poisons spock -- RightWins
                    Spock vs Spock: spock vs spock is a tie -- Tie


                    EDIT 14-Jul-2014: in response to Malachi's comment, I've rewritten the Play method to return an object rather than two out parameters. The code is the same length and it's arguable whether it's any clearer. Here's the updated version:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    var outcome = Play(left, right);
                    Console.WriteLine(left + " vs " + right + ": " + outcome.Report + " -- " + outcome.Result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    class Outcome {
                    internal string Report;
                    internal Result Result;
                    internal Outcome(A left, A right, string lr, string rl)
                    {
                    Report = ( lr != null ? left + " " + lr + " " + right
                    : rl != null ? right + " " + rl + " " + left
                    : left + " vs " + right + " is a tie"
                    ).ToLower();
                    Result = ( lr != null ? Result.LeftWins
                    : rl != null ? Result.RightWins
                    : Result.Tie
                    );
                    }
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static Outcome Play(A left, A right)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    return new Outcome(left, right, lr, rl);
                    }





                    share|improve this answer





















                    • 1





                      I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                      – Mathieu Guindon
                      Feb 4 '14 at 2:53











                    • @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                      – Rafe
                      Feb 4 '14 at 3:37











                    • @Rafe This is not a forum, this is a Q&A site.

                      – syb0rg
                      Feb 4 '14 at 3:43











                    • @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                      – Rafe
                      Feb 4 '14 at 3:59











                    • @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                      – syb0rg
                      Feb 4 '14 at 4:01
















                    4














                    I'm somewhat old-school in that I don't think OO is the right answer to every problem. Here's my effort:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    Result result;
                    string report;
                    Play(left, right, out result, out report);
                    Console.WriteLine(left + " vs " + right + ": " + report + " -- " + result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static void Play(A left, A right, out Result result, out string report)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    if (lr != null) {
                    result = Result.LeftWins;
                    report = (left + " " + lr + " " + right).ToLower();
                    return;
                    }
                    if (rl != null) {
                    result = Result.RightWins;
                    report = (right + " " + rl + " " + left).ToLower();
                    return;
                    }
                    result = Result.Tie;
                    report = (left + " vs " + right + " is a tie").ToLower();
                    }


                    Which prints out:



                    Rock vs Rock: rock vs rock is a tie -- Tie
                    Rock vs Paper: paper wraps rock -- RightWins
                    Rock vs Scissors: rock blunts scissors -- LeftWins
                    Rock vs Lizard: rock crushes lizard -- LeftWins
                    Rock vs Spock: spock vaporizes rock -- RightWins
                    Paper vs Rock: paper wraps rock -- LeftWins
                    Paper vs Paper: paper vs paper is a tie -- Tie
                    Paper vs Scissors: scissors cut paper -- RightWins
                    Paper vs Lizard: lizard eats paper -- RightWins
                    Paper vs Spock: paper disproves spock -- LeftWins
                    Scissors vs Rock: rock blunts scissors -- RightWins
                    Scissors vs Paper: scissors cut paper -- LeftWins
                    Scissors vs Scissors: scissors vs scissors is a tie -- Tie
                    Scissors vs Lizard: scissors decapitates lizard -- LeftWins
                    Scissors vs Spock: spock smashes scissors -- RightWins
                    Lizard vs Rock: rock crushes lizard -- RightWins
                    Lizard vs Paper: lizard eats paper -- LeftWins
                    Lizard vs Scissors: scissors decapitates lizard -- RightWins
                    Lizard vs Lizard: lizard vs lizard is a tie -- Tie
                    Lizard vs Spock: lizard poisons spock -- LeftWins
                    Spock vs Rock: spock vaporizes rock -- LeftWins
                    Spock vs Paper: paper disproves spock -- RightWins
                    Spock vs Scissors: spock smashes scissors -- LeftWins
                    Spock vs Lizard: lizard poisons spock -- RightWins
                    Spock vs Spock: spock vs spock is a tie -- Tie


                    EDIT 14-Jul-2014: in response to Malachi's comment, I've rewritten the Play method to return an object rather than two out parameters. The code is the same length and it's arguable whether it's any clearer. Here's the updated version:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    var outcome = Play(left, right);
                    Console.WriteLine(left + " vs " + right + ": " + outcome.Report + " -- " + outcome.Result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    class Outcome {
                    internal string Report;
                    internal Result Result;
                    internal Outcome(A left, A right, string lr, string rl)
                    {
                    Report = ( lr != null ? left + " " + lr + " " + right
                    : rl != null ? right + " " + rl + " " + left
                    : left + " vs " + right + " is a tie"
                    ).ToLower();
                    Result = ( lr != null ? Result.LeftWins
                    : rl != null ? Result.RightWins
                    : Result.Tie
                    );
                    }
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static Outcome Play(A left, A right)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    return new Outcome(left, right, lr, rl);
                    }





                    share|improve this answer





















                    • 1





                      I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                      – Mathieu Guindon
                      Feb 4 '14 at 2:53











                    • @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                      – Rafe
                      Feb 4 '14 at 3:37











                    • @Rafe This is not a forum, this is a Q&A site.

                      – syb0rg
                      Feb 4 '14 at 3:43











                    • @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                      – Rafe
                      Feb 4 '14 at 3:59











                    • @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                      – syb0rg
                      Feb 4 '14 at 4:01














                    4












                    4








                    4







                    I'm somewhat old-school in that I don't think OO is the right answer to every problem. Here's my effort:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    Result result;
                    string report;
                    Play(left, right, out result, out report);
                    Console.WriteLine(left + " vs " + right + ": " + report + " -- " + result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static void Play(A left, A right, out Result result, out string report)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    if (lr != null) {
                    result = Result.LeftWins;
                    report = (left + " " + lr + " " + right).ToLower();
                    return;
                    }
                    if (rl != null) {
                    result = Result.RightWins;
                    report = (right + " " + rl + " " + left).ToLower();
                    return;
                    }
                    result = Result.Tie;
                    report = (left + " vs " + right + " is a tie").ToLower();
                    }


                    Which prints out:



                    Rock vs Rock: rock vs rock is a tie -- Tie
                    Rock vs Paper: paper wraps rock -- RightWins
                    Rock vs Scissors: rock blunts scissors -- LeftWins
                    Rock vs Lizard: rock crushes lizard -- LeftWins
                    Rock vs Spock: spock vaporizes rock -- RightWins
                    Paper vs Rock: paper wraps rock -- LeftWins
                    Paper vs Paper: paper vs paper is a tie -- Tie
                    Paper vs Scissors: scissors cut paper -- RightWins
                    Paper vs Lizard: lizard eats paper -- RightWins
                    Paper vs Spock: paper disproves spock -- LeftWins
                    Scissors vs Rock: rock blunts scissors -- RightWins
                    Scissors vs Paper: scissors cut paper -- LeftWins
                    Scissors vs Scissors: scissors vs scissors is a tie -- Tie
                    Scissors vs Lizard: scissors decapitates lizard -- LeftWins
                    Scissors vs Spock: spock smashes scissors -- RightWins
                    Lizard vs Rock: rock crushes lizard -- RightWins
                    Lizard vs Paper: lizard eats paper -- LeftWins
                    Lizard vs Scissors: scissors decapitates lizard -- RightWins
                    Lizard vs Lizard: lizard vs lizard is a tie -- Tie
                    Lizard vs Spock: lizard poisons spock -- LeftWins
                    Spock vs Rock: spock vaporizes rock -- LeftWins
                    Spock vs Paper: paper disproves spock -- RightWins
                    Spock vs Scissors: spock smashes scissors -- LeftWins
                    Spock vs Lizard: lizard poisons spock -- RightWins
                    Spock vs Spock: spock vs spock is a tie -- Tie


                    EDIT 14-Jul-2014: in response to Malachi's comment, I've rewritten the Play method to return an object rather than two out parameters. The code is the same length and it's arguable whether it's any clearer. Here's the updated version:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    var outcome = Play(left, right);
                    Console.WriteLine(left + " vs " + right + ": " + outcome.Report + " -- " + outcome.Result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    class Outcome {
                    internal string Report;
                    internal Result Result;
                    internal Outcome(A left, A right, string lr, string rl)
                    {
                    Report = ( lr != null ? left + " " + lr + " " + right
                    : rl != null ? right + " " + rl + " " + left
                    : left + " vs " + right + " is a tie"
                    ).ToLower();
                    Result = ( lr != null ? Result.LeftWins
                    : rl != null ? Result.RightWins
                    : Result.Tie
                    );
                    }
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static Outcome Play(A left, A right)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    return new Outcome(left, right, lr, rl);
                    }





                    share|improve this answer















                    I'm somewhat old-school in that I don't think OO is the right answer to every problem. Here's my effort:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    Result result;
                    string report;
                    Play(left, right, out result, out report);
                    Console.WriteLine(left + " vs " + right + ": " + report + " -- " + result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static void Play(A left, A right, out Result result, out string report)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    if (lr != null) {
                    result = Result.LeftWins;
                    report = (left + " " + lr + " " + right).ToLower();
                    return;
                    }
                    if (rl != null) {
                    result = Result.RightWins;
                    report = (right + " " + rl + " " + left).ToLower();
                    return;
                    }
                    result = Result.Tie;
                    report = (left + " vs " + right + " is a tie").ToLower();
                    }


                    Which prints out:



                    Rock vs Rock: rock vs rock is a tie -- Tie
                    Rock vs Paper: paper wraps rock -- RightWins
                    Rock vs Scissors: rock blunts scissors -- LeftWins
                    Rock vs Lizard: rock crushes lizard -- LeftWins
                    Rock vs Spock: spock vaporizes rock -- RightWins
                    Paper vs Rock: paper wraps rock -- LeftWins
                    Paper vs Paper: paper vs paper is a tie -- Tie
                    Paper vs Scissors: scissors cut paper -- RightWins
                    Paper vs Lizard: lizard eats paper -- RightWins
                    Paper vs Spock: paper disproves spock -- LeftWins
                    Scissors vs Rock: rock blunts scissors -- RightWins
                    Scissors vs Paper: scissors cut paper -- LeftWins
                    Scissors vs Scissors: scissors vs scissors is a tie -- Tie
                    Scissors vs Lizard: scissors decapitates lizard -- LeftWins
                    Scissors vs Spock: spock smashes scissors -- RightWins
                    Lizard vs Rock: rock crushes lizard -- RightWins
                    Lizard vs Paper: lizard eats paper -- LeftWins
                    Lizard vs Scissors: scissors decapitates lizard -- RightWins
                    Lizard vs Lizard: lizard vs lizard is a tie -- Tie
                    Lizard vs Spock: lizard poisons spock -- LeftWins
                    Spock vs Rock: spock vaporizes rock -- LeftWins
                    Spock vs Paper: paper disproves spock -- RightWins
                    Spock vs Scissors: spock smashes scissors -- LeftWins
                    Spock vs Lizard: lizard poisons spock -- RightWins
                    Spock vs Spock: spock vs spock is a tie -- Tie


                    EDIT 14-Jul-2014: in response to Malachi's comment, I've rewritten the Play method to return an object rather than two out parameters. The code is the same length and it's arguable whether it's any clearer. Here's the updated version:



                    void Main()
                    {
                    foreach (var left in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    foreach (var right in Enumerable.Range(0, (int)A.Count).Cast<A>()) {
                    var outcome = Play(left, right);
                    Console.WriteLine(left + " vs " + right + ": " + outcome.Report + " -- " + outcome.Result);
                    }
                    }
                    }

                    enum A { Rock, Paper, Scissors, Lizard, Spock, Count };

                    static string[,] Defeats;

                    static void InitDefeats()
                    {
                    Defeats = new string[(int)A.Count, (int)A.Count];
                    Action<A, string, A> rule = (x, verb, y) => { Defeats[(int)x, (int)y] = verb; };
                    rule(A.Rock, "crushes", A.Lizard);
                    rule(A.Rock, "blunts", A.Scissors);
                    rule(A.Paper, "wraps", A.Rock);
                    rule(A.Paper, "disproves", A.Spock);
                    rule(A.Scissors, "cut", A.Paper);
                    rule(A.Scissors, "decapitates", A.Lizard);
                    rule(A.Lizard, "poisons", A.Spock);
                    rule(A.Lizard, "eats", A.Paper);
                    rule(A.Spock, "smashes", A.Scissors);
                    rule(A.Spock, "vaporizes", A.Rock);
                    }

                    class Outcome {
                    internal string Report;
                    internal Result Result;
                    internal Outcome(A left, A right, string lr, string rl)
                    {
                    Report = ( lr != null ? left + " " + lr + " " + right
                    : rl != null ? right + " " + rl + " " + left
                    : left + " vs " + right + " is a tie"
                    ).ToLower();
                    Result = ( lr != null ? Result.LeftWins
                    : rl != null ? Result.RightWins
                    : Result.Tie
                    );
                    }
                    }

                    enum Result { LeftWins, Tie, RightWins };

                    static Outcome Play(A left, A right)
                    {
                    if (Defeats == null) InitDefeats();
                    var lr = Defeats[(int)left, (int)right];
                    var rl = Defeats[(int)right, (int)left];
                    return new Outcome(left, right, lr, rl);
                    }






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Jul 14 '14 at 3:25

























                    answered Feb 4 '14 at 1:10









                    RafeRafe

                    1,025811




                    1,025811








                    • 1





                      I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                      – Mathieu Guindon
                      Feb 4 '14 at 2:53











                    • @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                      – Rafe
                      Feb 4 '14 at 3:37











                    • @Rafe This is not a forum, this is a Q&A site.

                      – syb0rg
                      Feb 4 '14 at 3:43











                    • @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                      – Rafe
                      Feb 4 '14 at 3:59











                    • @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                      – syb0rg
                      Feb 4 '14 at 4:01














                    • 1





                      I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                      – Mathieu Guindon
                      Feb 4 '14 at 2:53











                    • @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                      – Rafe
                      Feb 4 '14 at 3:37











                    • @Rafe This is not a forum, this is a Q&A site.

                      – syb0rg
                      Feb 4 '14 at 3:43











                    • @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                      – Rafe
                      Feb 4 '14 at 3:59











                    • @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                      – syb0rg
                      Feb 4 '14 at 4:01








                    1




                    1





                    I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                    – Mathieu Guindon
                    Feb 4 '14 at 2:53





                    I +1'd because I find your answer brings an interesting point of view. However a downvote was also compelling, because this answer could just as well be posted as a new question for reviewing - it's not a review, it's a rewrite... and FWIW I really don't like the enum type being called A.

                    – Mathieu Guindon
                    Feb 4 '14 at 2:53













                    @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                    – Rafe
                    Feb 4 '14 at 3:37





                    @lol.upvote, fair comment -- today I've been rattling off code on this forum in coffee breaks, which is perhaps against the point! I did have a point to make, though, which is that the usual OO approach (classes, interfaces, design pattern, etc.) can lead you away from the simplest/er solutions. That's what I was trying to demonstrate. Re: A, I chose A to read nicely, albeit tongue in cheek.

                    – Rafe
                    Feb 4 '14 at 3:37













                    @Rafe This is not a forum, this is a Q&A site.

                    – syb0rg
                    Feb 4 '14 at 3:43





                    @Rafe This is not a forum, this is a Q&A site.

                    – syb0rg
                    Feb 4 '14 at 3:43













                    @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                    – Rafe
                    Feb 4 '14 at 3:59





                    @syb0rg, I see no guidelines on this site indicating that replies should not present alternative approaches for consideration. If such guidelines exist and I have missed them, then you have my apologies. If not, then I think my contribution is valid.

                    – Rafe
                    Feb 4 '14 at 3:59













                    @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                    – syb0rg
                    Feb 4 '14 at 4:01





                    @Rafe You pointed out in a comment that you've been "rattling off code on this forum in coffee breaks". I was just pointing out that this is a Q&A site. Also, extended conversations should be held in the chatroom, not in the comments.

                    – syb0rg
                    Feb 4 '14 at 4:01


















                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f36395%2frock-paper-scissors-lizard-spock-challenge%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Список кардиналов, возведённых папой римским Каликстом III

                    Deduzione

                    Mysql.sock missing - “Can't connect to local MySQL server through socket”