Rock, Paper, Scissors, Lizard, Spock (Without regex)#5
Rock, Paper, Scissors, Lizard, Spock (Without regex)#5andresmoschini wants to merge 31 commits intoMakingSense:masterfrom
Conversation
I am not following the fine granular TDD approach, and now that I am doing this commit I am thinking that it would be equally easy to apply a granular approach as I did with Draw. Well, it is already done so it tests a lot of scenarios together.
Since all test have the same structure, I think that it could be a good idea to merge them. Thoughts?
…ting multiples implementations I am stopping working for the moment, the following steps could be: * Add new referee implementation following [nickmcdowall's Stage 2](https://github.com/nickmcdowall/Rock-Paper-Scissors-Kata/blob/master/README.md#stage-2) * Add support for Spock and Lizard as it is described in [nickmcdowall's Stage 3](https://github.com/nickmcdowall/Rock-Paper-Scissors-Kata/blob/master/README.md#stage-3) * Add richer result object with the action that winner choice does on the looser, for example _rocks **crushes** scissors_
…Referee **Attention!** something is wrong: All test passed when they should not. See the next commit for more information.
Since in commit 9975412 I made together the changes in the tests and the new implementation, I did not verify that test failed. **Lesson learned** Always verify that tests fails before fix them.
I have removed redundant rules, but I am still not happy with OOP approach, I still prefer rule based one.
|
Added a new "explanation" feature. I wanted to check how refactoring painless is each approach. |
AlphaGit
left a comment
There was a problem hiding this comment.
I like the solution! It took me a bit to figure out why the testing solution required so many classes, but when I understood it, it was clearly the best approach.
Regarding the game logic, I left some questions about naming and responsibility delegation. I think that the game itself is fairly simple but trying to abstract it may have made the game a little bit more difficult to follow.
However, this is an open-ended discussion. Let me know what you think!
| { | ||
| public class ExtensionRulesGameReferee : IGameReferee | ||
| { | ||
| private (GameChoice player1Choice, GameChoice player2Choice, GameResult result)[] _rules = new[] |
There was a problem hiding this comment.
Is this an array of tuples? It does look like them but I didn't know each tuple could have named values.
Or are these anonymous objects? In that case, I did not know they could be assigned to a class-level variable.
Please, enlight me!
Regardless, do you think that the complexity of the structure would have required a data structure? Something like a GamePrediction class?
There was a problem hiding this comment.
Is this an array of tuples? It does look like them but I didn't know each tuple could have named values.
Or are these anonymous objects? In that case, I did not know they could be assigned to a class-level variable.
It is an array of ValueTuple, you can see a detailed explanation in Mark Zhou's Tech Blog, for example:
You can even give a name to every element in the value tuple, like this:
public (string name, int age) GetStudentInfo(string id) { // Search by ID and find the student. return (name: "Annie", age: 25); } public void Test() { (string name, int age) info = GetStudentInfo("100-000-1000"); Console.WriteLine($"Name: {info.name}, Age: {info.age}"); }
Regardless, do you think that the complexity of the structure would have required a data structure? Something like a
GamePredictionclass?
You agree with you, a class would be better, but since I was using it internally I did not pay attention to that detail. Maybe the name should be related to rule in place of prediction.
There was a problem hiding this comment.
Oh, I knew about ValueTypes, I did not know you could declare an array of value tuples with names in them. That blew my mind.
I probably (wrongly) assumed that ValueTuples where just associated objects without structure (like pretty much other language treat tuples) but it seems not to be the case.
Thank you!
| public abstract class GameRefereeTest<TSut> : GameRefereeTest | ||
| where TSut : IGameReferee, new() | ||
| { | ||
| protected override IGameReferee GetSut() => new TSut(); |
There was a problem hiding this comment.
What does Sut stand for? "System under test"?
There was a problem hiding this comment.
Yes, it is System Under Test, It is common to see this name in Mark Seemann's posts
| { | ||
| TheWinnerIsPlayer1 = player1Won; | ||
| TheWinnerIsPlayer2 = player2Won; | ||
| ItIsADraw = player1Won == player2Won; |
There was a problem hiding this comment.
The game, conceptually, has 3 possible result states: Player1 wins, Player2 wins, the game is a Draw. Each of these 3 possible states is mutually exclusive from all the others.
The class definition has independent Player1Won (boolean: 2 states), Player2Won (boolean: 2 states), ItIsADraw (boolean: 2 states). As such, the class allows for 8 distinct states.
I'm thinking maybe the GameResult class was not a good idea as a class with state, but rather it should have been into a wrapper of an enum (GameResult) and an explanation string.
There was a problem hiding this comment.
I understand your distrust about boolean properties, but in my opinion, they represent the result in a simpler way. To avoid illegal states and inconsistencies, I make these properties read-only and used named constructors (Player1Won(), Player2Won() and Draw()) to generate the result, then the class does not allow for 8 distinct states.
Also, GameResult does not have state because all properties are read-only.
| } | ||
|
|
||
| // This method is only necessary to adapt this new implementation to previous | ||
| // interface. If we decided to use this implementation definitivaly, it should |
| } | ||
|
|
||
| protected GameResult Draw() => GameResult.Draw(ToGameChoice()); | ||
| public GameResult IWin(string verb, Choice looser) => _resultFactoryIfItWins(ToGameChoice(), verb, looser.ToGameChoice()); |
| ?? GameResult.Draw(gameSetup.Player1Choice); | ||
| } | ||
|
|
||
| private GameResult Evaluate(Func<GameChoice, string, GameChoice, GameResult> playerWon, GameChoice playerAChoice, GameChoice playerBChoice) |
There was a problem hiding this comment.
Minor: throughout most of the classes, players had been named player and player2, but in here, they're named playerA and playerB. It is generally better to keep a same denomination across the code for consistency.
| public GameResult Evaluate(GameSetup gameSetup) | ||
| { | ||
| return Evaluate(GameResult.Player1Won, gameSetup.Player1Choice, gameSetup.Player2Choice) | ||
| ?? Evaluate(GameResult.Player2Won, gameSetup.Player2Choice, gameSetup.Player1Choice) |
There was a problem hiding this comment.
Why does Evaluate need to be called twice, otherwise, the result is a Draw?
My first instinct tells me (because of the inverted parameters) that each evaluates if player1 won or player2 won, so I'd imagine that a better signature could have been PlayerWins(Player.Player1, gameSetup).
But then, the signature of the private Evaluate confuses me again: why does it receive a Func as the first parameter? Since this class is the referee -- what behavior is it delegating? Maybe renaming the arguments could help clear it up -- or maybe even a named interface would clear it up.
There was a problem hiding this comment.
You are right. Since this Evaluate() overload is private, I did not pay enough attention to the name and semantics, but I will review it. Thanks!
I'd say that's absolutely right. As long as you credit the source and there are no legal issues with it, feel free to do so! |
|
Thanks for the review @AlphaGit ! I really appreciate it. I just come back from vacations so I will be checking your comments during some time, maybe a comment per day. |
Well, because of a confusion, I have worked in a different kata than specified in the repo.
Anyway, I think that it could be useful, to enrich this repository. It is pending to add the specification, but in general, it is based on https://gist.github.com/trikitrok/63bfb5226f8cc2e8f6c3 and https://ajlopez.wordpress.com/2013/04/26/tdd-kata-1-piedra-papel-tijera-lagarto-spock/
I would like to some advice about how to include it in the repo, maybe it is enough adding a new readme.md, but I would like to hear Sensei's (@DanielAltamirano) opinion, or if he is not available, @AlphaGit 's one. I also would like to see your reviews.
Also, I still have to recover information about the time that I have spent on this.