Got it passing all tests within an hour :-p (my solution included)

Sorry but my coding wins are rare enough that I have to brag about them!

I found it easier to approach the problem head on as opposed to test by test, though was checking that the expected tests would pass as I added logic for the various conditions. Here’s what I came up with:

// returns a list of individual frame scores
public static List<int> ScoreFrames(List<int> rolls)
{
    List<int> frameScores = new List<int>();
    bool newFrame = true;
    for (int i = 0; i < rolls.Count; i++ )
    {            
        if(newFrame && rolls[i] == 10)  // Strike!
        {                
            if(rolls.Count > i + 2)     // If we have the next 2 rolls completed, add them as bonuses and complete score for frame (10 for strike plus scores for next frame)
            {
                frameScores.Add(10 + rolls[i + 1] + rolls[i + 2]);
            } 
        } else {
            if(!newFrame && frameScores.Count < 10) // find score for frame, checking that we're not in the end of game bonus section
            {
                int frameScore = rolls[i] + rolls[i - 1]; // frame complete, take score from this bowl and previous
                if (frameScore == 10 && rolls.Count > i + 1) frameScore += rolls[i + 1]; // if we have a spare and next roll is available, add next roll
                if (frameScore != 10 || rolls.Count > i + 1) frameScores.Add(frameScore); // add frame score, unless it's a spare and next row not yet available
            }
            newFrame = !newFrame; // toggle whether it's a new frame, not done if strike as next will be new
        }
    }
    return frameScores;
}
3 Likes

Man, that’s elegant!
It took me over three hours to come up with this:

	int frameTotal = 0;

	for (int i = 0, turn = 1; i < rolls.Count; i++, turn++) {
		if (rolls [i] == 10 && rolls.Count > i + 2) {
			frameTotal = rolls [i] + rolls [i + 1] + rolls [i + 2];
			turn++;
			if (i == 18) {
				frameList.Add (frameTotal);
				break;
			}
		} else {
			frameTotal += rolls [i];
		}

		if (turn % 2 == 0 && frameList.Count < 10) {
			if (frameTotal < 10 || (frameTotal > 10 && rolls.Count > i + 2)) {
				frameList.Add (frameTotal);
			} else if (frameTotal == 10 && rolls.Count > i + 1) {
				frameList.Add (frameTotal + rolls [i + 1]);
			}
			frameTotal = 0;
		}
	}

I agree with taking it on head-on. The toughest challenge was handling strikes, but when the basic structure is set up and you know how to handle frames and spares, then you’re halfway there.

Wow. Nice job guys. I was SOOO proud of my code after about 3 hours. Now I just feel ashamed. :smile:

int i=0, count = rolls.Count;
bool subtractOne = false, beforeFinalFrame = true;
foreach (int roll in rolls)
{
	if (i + 2 <= count)
	{
		if (rolls[i] == 10 && beforeFinalFrame)
		{
			if (i + 3 <= count && beforeFinalFrame)
			{
				frameList.Add(rolls[i] + rolls[i + 1] + rolls[i + 2]);
				subtractOne = true;
			}
		}
		else if (rolls[i] + rolls[i + 1] == 10 && beforeFinalFrame)
		{
			if (i + 3 <= count && beforeFinalFrame)
				frameList.Add(rolls[i] + rolls[i + 1] + rolls[i + 2]);
		}
		else if (beforeFinalFrame)
		{
			frameList.Add(rolls[i] + rolls[i + 1]);
		}
		i += 2;
		if (frameList.Count > 9) beforeFinalFrame = false;
		if (subtractOne) i--;
		subtractOne = false;
	}
}

I got my solution quickly as well, but, as you’ll see, it’s a lot longer and not as clean as the ideal. 45 lines instead of 18. I started writing it before seeing the sample test cases, and I’d planned to add zeroes after strikes so that all frames (besides the final one) would have two bowls. Guess that turned out to be more trouble than it was worth, as strikes ended up creating a lot of special cases

public static List<int> GetFrameScores(List<int> bowls)
{
    int totalPins = 10;
    int totalFrames = 10;
    List<int> frameScores = new List<int>();
    
    for (int i = 1; i < bowls.Count; i += 2) //Iterate through each frame
    {
        if ((i+1)/2 == totalFrames-1 && bowls[i - 1] == totalPins)   //If it's the second-to-last frame and it's a strike
        {
            if (bowls.Count -1 >= i+2)  //If the two bowls in the last frame exist
            {
                frameScores.Add(bowls[i - 1] + bowls[i + 1] + bowls[i + 2]);
            }
        }
        else if ((i+1)/2 == totalFrames && bowls[i-1] + bowls[i] >= 10)  //If it's the last frame and it's strike or spare
        {
            if (bowls.Count == (totalFrames * 2) + 1)   //If the bonus frame has been bowled
            {
                frameScores.Add(bowls[i - 1] + bowls[i] + bowls[i + 1]);
            }
        }
        //Normal frames
        else if (bowls[i-1] == totalPins)   //Strike
        {
            if (bowls.Count - 1 >= i + 2)    //If the next two bowls exist
            {
                if (bowls[i+1] == totalPins)   //If the next bowl was also a strike
                {
                    if (bowls.Count - 1 >= i + 3)    //If the bowl after the second strike exists
                    {
                        frameScores.Add(bowls[i - 1] + bowls[i + 1] + bowls[i + 3]);
                    }
                }
                else  //Just one strike
                {
                    frameScores.Add(bowls[i - 1] + bowls[i + 1] + bowls[i + 2]);
                }
            }
        }
        else if (bowls[i - 1] + bowls[i] == totalPins)     //Spare
        {
            if (bowls.Count - 1 >= i+1) //If the next bowl exists
            {
                    frameScores.Add(bowls[i - 1] + bowls[i] + bowls[i + 1]);
            }
        }
        else   //No strike or spare
        {
            frameScores.Add(bowls[i] + bowls[i - 1]);
        }
    }
    return frameScores;
}

But I still wouldn’t call it messy, and it passes all of the test cases (once I modified them to fit my format). Let me know if anyone thinks it’s an unintelligible travesty though. I’m also curious about how much more elegant it can be without changing the assumption I made about the data

Congrats Clem, that is indeed a very elegant solution. I also did it fast on the second attempt, but I think the trick here is to use a for loop instead of foreach, which was the one I used in my case:

public static List<int> ScoreFrames (List<int> rolls)
    {
        List<int> frameList = new List<int>();
        int rollNumberWithinFrame = 0;
        int rollNumber = 0;
        foreach (int roll in rolls)
        {
            rollNumber++;
            rollNumberWithinFrame++;
            //This identifies the end of the frame
            if (rollNumberWithinFrame % 2 == 0)
            {
                int result = rolls[rollNumber - 2] + roll;
                //This identifies a non spare
                if(frameList.Count < 10)
                {
                    if (result < 10) { frameList.Add(result); }
                    else //this is a spare
                    {
                        if (rolls.Count > rollNumber) { frameList.Add(10 + rolls[rollNumber]); }
                    }
                }
                rollNumberWithinFrame = 0;
            }
            else //this is the beginning of the frame
            {
                //this is a strike
                if (roll == 10)
                {
                    if (rolls.Count > rollNumber + 1) { frameList.Add(10 + rolls[rollNumber] + rolls[rollNumber + 1]); }
                    rollNumberWithinFrame = 0;
                }
            }
        }
        return frameList;
     }

because with a for loop you have immediate access to the current index of the list, as Clem shows on his code. In my case I had to create a variable to hold the current number of roll.
but it works!!!

thank you very much Ben

I reply with my own solution. A recursive solution. I had to add a second parameter which I set to 0 at first call of the method.

Thanks to Ben and staff behind this site/course for these challenges and for this excellent course.

public static List<int> ScoreFrames (List<int> rolls, int frameSize)
{
    //This solution takes the first two scores and call itself to calculate the next points

    List<int> frameList = new List<int>();
    // The rolls list dimension
    int count = rolls.Count;
    // The actual points
    int points = 0;

    //Teh frameSize is used to exclude points after 20 frame (see T21StrikeInLastFrame Test) (the base case of recursion)
    if (frameSize >= 10)
        return frameList;

    // If the rolls list dimension is less or equal than one, there are no points (the base case of recursion)
    if (count <= 1)
        return frameList;

    if(count >= 2) // A set of rules that reduce all other cases toward the base case
    {
        
        points = rolls[0] + rolls[1]; // Sum of the points in a frame
        if (points < 10) frameList.Add(points); // if points are less than 10 create a list with first result of frame
        if (count == 2) return frameList; // ... and if the list dimension is equal 2 returns the points

        if(points >= 10) // Strike or spare
        {
            frameList.Add(points + rolls[2]); // sum the third point 
            if(points > 10 || (rolls[0] == 0 || rolls[1] == 0)) // if the points are more than 10 or the first or second scores are equal to 0
                return frameList.Concat(ScoreFrames(rolls.GetRange(1, count - 1), ++frameSize)).ToList<int>();  // Concat the actual points with the list [1] -> [the end of list] - After a STRIKE
        }
    
        return frameList.Concat(ScoreFrames(rolls.GetRange(2, count - 2), ++frameSize)).ToList<int>(); // Concat the actual points with the list [2] -> [the end of list] // After a SPARE
    }
     
    return frameList;// (the base case of recursion)
}

Good job to every one. It tooks me one hour and a half to pass all tests. This is my code:

public static List<int> ScoreFrames (List<int> rolls) {
    List<int> frameList = new List<int> ();
    int frameScore = -1;

    for(int nRoll = 0; nRoll < rolls.Count; nRoll++) {
        int roll = rolls[nRoll];            
        if(frameScore == -1) {
            frameScore = roll;
            if (frameScore == 10) {
                if (rolls.Count < (nRoll + 3)) { break; } //We need 2 more rolls for this frame                    
                frameList.Add(frameScore + rolls[nRoll + 1] + rolls[nRoll + 2]);
                if(frameList.Count == 10) { break; }
                frameScore = -1;                    
            }
        } else {
            frameScore += roll;
            if (frameScore == 10) {
                if (rolls.Count < (nRoll + 2)) { break; } //We need 1 more rolls for this frame
                frameScore += rolls[nRoll + 1];
            }
            frameList.Add(frameScore);
            frameScore = -1;
        }
    }

    return frameList;
}

Oh man, nice solution!

Took me a day, 160 lines and about 4 complete re-writes to finally get mine working… If only I’d known that you could use a for-loop on a list… Not knowing this and feeling tied to a foreach left me struggling to figure out which bowl we were on (so added a counter), which frame we were on (another counter) - and really struggling to add values to previous frames or to find the next bowl value…

I instead used my foreach loop to build up a 2-D array (10x3) to store which individual bowl scores needed to be added to each frame score.

Then a second pass to go through my array and total up the actual values of those bowls for each frame (if they were available yet).

Waaay overcomplicated!!

A good learning exercise though!

Hey all, I was also proud of myself for four hours, until I saw the others. but I thought it was worth posting anyway because its quite different than the others.

>  public static List<int> ScoreFrames(List<int> rolls){
>         List<int> frameList = new List<int>();
>         for (int i = 0; i < rolls.Count; i++){            
>                 if ((i % 2) == 0){
>                 if (i > 1){
>                     if ((rolls[i - 1] + rolls[i - 2]) == 10 && (rolls[i - 1]) != 0 && (rolls[i - 2] != 0)){
>                         frameList.Add(rolls[i] + 10);
>                     }
>                 }
>                 if (i + 1 < rolls.Count){                    
>                     if (i > 1){
>                         if ((rolls[i - 2]) == 10 || (rolls[i - 1]) == 10){
>                             frameList.Add(rolls[i] + rolls[i + 1] + 10);
>                         }
>                     }
>                     if (rolls[i] == 10){
>                         rolls.Insert(i + 1, 0);
>                         i++;
>                         continue;
>                     }
>                     if (rolls[i] + rolls[i + 1] == 10){
>                         continue;
>                     }
>                     if(frameList.Count >= 10){
>                         continue;
>                     }
>                     frameList.Add(rolls[i] + rolls[i + 1]);                
>                 }
>             }          
>         }        
>         return frameList;
>     }

Since this seems to be the thread where most people are sharing their code, I’ll post here. My approach is to keep track of the current bowl the user is on depending on if the turn is odd or even.

public static List<int> ScoreFrames(List<int> rolls)
{
    List<int> frameList = new List<int>();
    int currentBowl = 1;

    for (int i = 0; i < rolls.Count; i++)
    { 
        if (OddTurn(currentBowl))
        {
            // Got a strike
            if (rolls[i] == 10)
            {
                if (rolls.Count >= 3 && currentBowl <= 19)
                {
                    frameList.Add(rolls[i] + rolls[i + 1] + rolls[i + 2]);

                    currentBowl += 2;
                    if (currentBowl >= 21) { break; }
                }
            }

            // Carry on
            else 
            {
                currentBowl++;
            }
        }

        else if (EvenTurn(currentBowl))
        {
            // Got a spare
            if (rolls[i - 1] + rolls[i] == 10)
            {
                if (rolls.Count >= i + 2)
                {
                    frameList.Add(rolls[i - 1] + rolls[i] + rolls[i + 1]);
                }
            }

            // Tally frame as normal
            else
            {
                frameList.Add(rolls[i - 1] + rolls[i]);
            }

            // Carry on
            currentBowl++;
        }
    }

    return frameList;
}

private static bool OddTurn(int currentRoll)
{
    return currentRoll % 2 != 0;
}

private static bool EvenTurn(int currentRoll)
{
    return currentRoll % 2 == 0;
}

Privacy & Terms