My in-laws taught me a dice game a couple years ago, and we play every once in a while. A recent excellent answer from @radarbob inspired me to proceed with translating the rules of that dice game into code.
Here's what came out of it:
So it works perfectly, and somehow I'm just as unlucky with this virtual version as in the real-life one (had to click dozens of time to get a freakin' opening roll; by that time my mother-in-law already has thousands of points, every time). At least in this version I can inject some CrookedDie
implementation if I want!
I'd like the CalculateRollScore
method reviewed, to see what could be improved.
It turns out I thought the code was working as it should. So I wrote a couple unit tests and... well this is where not writing unit tests has bitten me.
Well I had to change the rules anyway, since the rule for 4x 1's was just my wife being mixed-up. My mother-in-law and I agree that 3x 1's is 1000, 4x 1's is 2000 and 5x 1's is 3000.
So before I show any code, I'll show this:
Now here's the working code (view original code here), with only minor changes that don't mootinize any already posted answer:
namespace DiceGame
{
public interface IRollScoreRules
{
int CalculateRollScore(IEnumerable<IRollResult<int>> results);
}
public class GameRollScoreRules : IRollScoreRules
{
public virtual bool IsOpeningRoll(IEnumerable<IRollResult<int>> results)
{
return CalculateRollScore(results) >= 500;
}
public virtual int CalculateRollScore(IEnumerable<IRollResult<int>> results)
{
var score = 0;
// if less than 3 1's were rolled, each rolled 1 is 100pts:
score += results.GroupBy(e => e.Value)
.Where(g => g.Key == 1)
.Where(g => g.Count() < 3)
.Sum(g => g.Count() * 100);
// if less than 3 5's were rolled, each rolled 5 is 50pts:
score += results.GroupBy(e => e.Value)
.Where(g => g.Key == 5)
.Where(g => g.Count() < 3)
.Sum(g => g.Count() * 50);
// if more than 3 of anything other than 1 were rolled, determine number of "extra dice":
var extras = results.GroupBy(e => e.Value)
.Where(g => g.Key != 1 && g.Count() > 3)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Count() - 3);
var extraOnes = results.GroupBy(e => e.Value)
.Where(g => g.Key == 1 && g.Count() > 3)
.ToDictionary(kvp => kvp.Key, kvp => kvp.Count() - 3);
// any triplet is 100x nominal value; each extra die is another 100x nominal value:
score += results.GroupBy(e => e.Value)
.Where(g => (g.Key != 1 && g.Count() >= 3))
.Sum(g => (g.Key * 100) + (extras.ContainsKey(g.Key) ? extras[g.Key] : 0) * (g.Key * 100));
//score += results.GroupBy(e => e.Value)
// .Where(g => (g.Key == 1 && g.Count() >= 3))
// .Sum(g => (g.Key * 100) + (extraOnes.ContainsKey(g.Key) ? extraOnes[g.Key] : 0) * (g.Key * 100));
// 3x 1's is 1000x nominal value; each extra die is another 1000x nominal value:
score += results.GroupBy(e => e.Value)
.Where(g => g.Key == 1 && g.Count() >= 3)
.Sum(g => (g.Key * 1000) + (extraOnes.ContainsKey(g.Key) ? extraOnes[g.Key] : 0) * (g.Key * 1000));
return score;
}
}
}