Rock-Paper-Scissors-Lizard-Spock Challenge
"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
add a comment |
"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
add a comment |
"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
"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
c# game dependency-injection community-challenge rock-paper-scissors
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
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
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.
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
add a comment |
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, andB.CompareTo(C)
returns a value y of the same sign as x, thenA.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.
add a comment |
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")
})
{
}
}
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 ofGetWinningVerb
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
add a comment |
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);
}
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 calledA
.
– 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 choseA
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
|
show 6 more comments
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
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
add a comment |
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.
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
add a comment |
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.
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.
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
add a comment |
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
add a comment |
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, andB.CompareTo(C)
returns a value y of the same sign as x, thenA.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.
add a comment |
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, andB.CompareTo(C)
returns a value y of the same sign as x, thenA.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.
add a comment |
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, andB.CompareTo(C)
returns a value y of the same sign as x, thenA.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.
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, andB.CompareTo(C)
returns a value y of the same sign as x, thenA.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.
answered Nov 30 '13 at 11:50
svicksvick
22.7k43879
22.7k43879
add a comment |
add a comment |
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")
})
{
}
}
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 ofGetWinningVerb
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
add a comment |
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")
})
{
}
}
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 ofGetWinningVerb
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
add a comment |
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")
})
{
}
}
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")
})
{
}
}
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 ofGetWinningVerb
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
add a comment |
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 ofGetWinningVerb
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
add a comment |
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);
}
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 calledA
.
– 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 choseA
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
|
show 6 more comments
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);
}
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 calledA
.
– 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 choseA
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
|
show 6 more comments
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);
}
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);
}
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 calledA
.
– 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 choseA
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
|
show 6 more comments
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 calledA
.
– 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 choseA
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
|
show 6 more comments
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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