I assume this is going to be done in depth in later videos but here is my first attempt (after a round of refactoring). It passes all the tests I have thrown at it, including checking the correct exceptions are thrown (e.g. too many rolls, values not between 0 and 10 etc…)
My logic took me down the road of converting the whole list first to strip out the ‘fake’ balls (namely ball 2 when ball 1 was a strike). This made it easier to just grab the bonuses. Thinking about how to do scoring by iterating the original List made my head hurt lol. It’s still a bit long-winded and nowhere near the target “19 extra lines” but it’s late and I need to get some sleep.
Please let me know what you think.
public class ScoreMaster
{
public static List<int> ScoreFrames(List<int> rolls)
{
int thisRoll = 0;
if (rolls.Count > 21) throw new UnityException("Invalid Roll List - too long!");
if (rolls.Count < 1) return new List<int>();
List<int> frameList = new List<int>();
List<int> actualRolls = new List<int>();
int i = 0;
int currentFrame = 0;
bool firstBallInFrame = true;
int frameScore = 0;
bool strikeBonusAvailable = false;
bool spareBonusAvailable = false;
// Convert 'Rolls' to Actual Rolls
do {
if(rolls[i]<0 || rolls[i] > 10) { throw new UnityException("Score out of range - bowl must be between 0 and 10"); }
actualRolls.Add(rolls[i]);
// For first 9 frames, skip virtual roll when STRIKE in first ball of frame
if (i < 18 && i % 2 == 0 && rolls[i] == 10) { i = i + 2; }
else i++;
} while (i < rolls.Count);
int rollCount = actualRolls.Count;
// Iterate through ACTUAL rolls
for (i = 0; currentFrame < 10; i++)
{
thisRoll = actualRolls[i];
strikeBonusAvailable = (rollCount >= i + 3);
spareBonusAvailable = (rollCount >= i + 2);
if (firstBallInFrame)
{
//STRIKE - see if bonus points exist
if (thisRoll >= 10 && strikeBonusAvailable)
{
frameList.Add( thisRoll + actualRolls[i + 1] + actualRolls[i + 2]);
currentFrame++;
}
else if (thisRoll >= 10 && !strikeBonusAvailable) { return frameList; }
//OPEN first roll - just store temporary score
else
{
frameScore = thisRoll;
firstBallInFrame = false;
}
}
else
{
// SPARE - see if bonus points exist
if (thisRoll + frameScore >= 10 && spareBonusAvailable)
{
frameList.Add( frameScore + thisRoll + actualRolls[i + 1]);
firstBallInFrame = true;
currentFrame++;
}
else if (thisRoll + frameScore >= 10 && !spareBonusAvailable) { return frameList; }
// OPEN second roll - just add temporary score to this roll
else
{
frameList.Add(frameScore + thisRoll);
firstBallInFrame = true;
currentFrame++;
}
}
}
return frameList;
}
}