I was a total coding noob when I started the course, so passing this ‘Epic TDD challenge’ was super-rewarding to me… or at least it was until I watched Ben’s awesome solution!
If I had known about this, my life would have been much easier:
- You can do ‘i+2’ for loops.
- You can look ahead in a list.
- You can get the length of a list.
…but I didn’t realise any of that, and this was my final solution!
The code checks whether a bowl is the first or the second one of a frame, and picks the correct action according to that:
- Spare bonus points are stored in a variable, only on second bowl of a frame.
- Strike bonus points are accumulated in a variable, only on first bowl of a frame.
- Spares are scored on first bowl of the next frame.
- Single-Strikes are scored on second bowl of the next frame (strike bonus points are equal to 10).
- Multi-Strikes are scored on first bowl of a frame (strike bonus points are equal to 20).
- Normal Score is added on second bowl, unless there are spare bonus points stored.
It takes way too many variables and most of the code is just handling those variables, but I might be holding some additional valuable info to show on the user interface, so I think I’m sticking to my solution for a while…
[Scroll to the right to see the comments]
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public static class ScoreMaster
{
private static List<int> frameList = new List<int>();
private static int spareBonus = 0;
private static int strikeBonus = 0;
private static bool frame9Strike = false;
private static int currentRoll = 0;
private static int currentRollInFrame = 1;
private static int frameScore = 0;
private static void Reset()
{
frameList = new List<int>();
spareBonus = 0;
strikeBonus = 0;
frame9Strike = false;
currentRoll = 0;
currentRollInFrame = 1;
frameScore = 0;
}
/// Used in the ScoreFrames Function bellow
private static void ScoreSpare() {
if (spareBonus == 10) {
frameList.Add(spareBonus + frameScore);
spareBonus -= 10;
}
}
private static void ScoreStrike(int threshold) {
if (strikeBonus >= threshold) {
frameList.Add(strikeBonus + frameScore);
strikeBonus -= 10;
}
}
// Returns List of Individual Frame Scores
public static List<int> ScoreFrames(List<int> rolls)
{
Reset();
foreach (int roll in rolls)
{
currentRoll++;
frameScore += roll;
if (currentRollInFrame == 1 || currentRoll >= 20) // FIRST BALL OF A FRAME + Balls 20-21
{
ScoreSpare(); // Score Spare (Always on First Ball of a frame)
ScoreStrike(20); // Score Multi-Strike (Always on First Ball of a frame)
if (currentRoll == 20 && frame9Strike == true) { // SPECIAL CASE: BALL 20
ScoreStrike(10); // Score Frame 9 in case of a Strike
}
if (currentRoll == 21 || (currentRoll == 20 && frameScore < 10)) { // SPECIAL CASE: LAST BALL
frameList.Add(frameScore); // Always Submit Score
}
if (roll == 10 && currentRoll < 19) { // STRIKE (Balls 1-18)
currentRoll++; // Skip the Second ball of the Frame
currentRollInFrame = 1; // Reset to Start a New Frame
frameScore = 0;
strikeBonus += 10; // Remember Strike Bonus Points
if(currentRoll == 18) { frame9Strike = true; } // Condition for Ball 20 Special Case
}
else { currentRollInFrame++; } // Next Ball will be The Second of the Frame (third in case of Ball 21)
}
else if (currentRollInFrame == 2 && currentRoll != 20) // SECOND BALL OF A FRAME (Ball 20 excluded)
{
ScoreStrike(10); // Score Normal Strike (Always on Second Ball of a frame)
if (frameScore == 10) { // SPARE (Balls 1-18)
spareBonus += 10; // Remember Spare Bonus Points
}
if (spareBonus == 0) { frameList.Add(frameScore); } // Don't Score yet in case of a Spare, Score otherwise
frameScore = 0; // Reset to Start a New Frame
currentRollInFrame = 1;
}
}
return frameList;
}
// Returns a List of Cumulative Scores like a Normal Score Card
public static List<int> ScoreCumulative(List<int> rolls)
{
List<int> cumulativeScores = new List<int>();
int runningTotal = 0;
foreach (int frameScore in ScoreFrames(rolls))
{
runningTotal += frameScore;
cumulativeScores.Add(runningTotal);
}
return cumulativeScores;
}
}