I recently interviewed for a software engineering job that requires at least one year of C# and experience with the Microsoft stack. Even though I made it clear at the outset that I had zero experience with C#, they still wanted me to take their engineering test, which led me to believe that they were willing to give me a shot because I am really good at learning new programming languages and am otherwise a solid developer. Here are the details for the "bowling exercise" test they gave me:
To complete this coding exercise, you will need to write a class that implements the interface below and provides logic to score a bowling game according to the bowling scoring rules. If you are not familiar with the rules of bowling, we encourage you to briefly research the intricacies of the scoring rules. You can write your implementation in either C# or VB.NET.
You do not need to handle invalid input in your class. Your code does not need to have a user interface.
C# Interface
public interface ISimpleBowlingGame
{
// Called when a player completes a frame.
// This method will be called 10 times for a bowling game.
// The throws parameter provides the number of pins knocked down on each throw in the frame being recorded.
// The 10th frame may have 3 values.
void RecordFrame(params int[] throws);
// Called at the end of the game to get the final score.
int Score { get; }
}
That's it. That is all of the instruction they provided for this exercise.
So I worked on it for three nights and a whole weekend, and in the end thought I had nailed it. I learned how to implement an interface, set up classes, developed the logic, and included test cases that I thought proved that I had succeeded with this. All of the scoring tests I ran produced the correct final scores.
So I turned in the following code:
using System;
using System.Collections.Generic;
using System.Linq;
namespace bowlingScore
{
public interface ISimpleBowlingGame
{
// Called when a player completes a frame.
// This method will be called 10 times for a bowling game.
// The throws parameter provides the number of pins knocked down on each throw in the frame being recorded.
// The 10th frame may have 3 values.
void RecordFrame(params int[] throws);
// Called at the end of the game to get the final score.
int Score { get; }
}
public class BowlingGame : ISimpleBowlingGame
{
public int Score { get; set; }
public int frame = 0; // The current frame
public int frameScore = 0; // The total of first two throws in a normal frame
public int[] throws; // Input
public int throw1; // First throw in a frame
public int throw2; // Second throw in a frame
public int throw3; // Third throw in the 10th frame
public List<int> tenFrameScores = new List<int>();
public int[][] tenFrames = new int[10][]; // a JaggedArray (to allow for three numbers in the 10th frame)
// RecordFrame records all frames to a data container, to allow for easy scoring
public void RecordFrame(params int[] throws)
{
throw1 = throws[0];
// In case there is no second number (0) provided when someone throws a strike
throw2 = (throws.Length == 1) ? 0 : throws[1];
throw3 = (throws.Length == 3) ? throws[2] : 0;
frameScore = throw1 + throw2;
// Store the frame scores in a list as they come in, to be updated with strike and spare bonuses
tenFrameScores.Add(frameScore);
// Dynamically populate the JaggedArray tenFrames
if (throws.Length == 3)
{
tenFrames[frame] = new[] { throw1, throw2, throw3 };
}
else
{
tenFrames[frame] = new[] { throw1, throw2 };
}
Console.WriteLine("Throws: " + string.Join(",", throws));
// Get the number of non-null elements in the tenFrames array
int numberFramesSoFar = tenFrames.Count(s => s != null);
//Console.WriteLine("Number of frames so far: " + numberFramesSoFar);
frame++;
if (numberFramesSoFar == 10) // Ready to calculate a score
{
Console.WriteLine("Ten frames of data have been received. Ready to score the game.");
for (int i = 0; i < 10; i++) // iterate through the data container of throws
{
Console.WriteLine("tenFrames[" + i + "]: " + string.Join(",", tenFrames[i]));
throw1 = tenFrames[i][0];
throw2 = tenFrames[i][1];
frameScore = tenFrameScores[i];
//Console.WriteLine("Frame total: " + tenFrameScores[i]);
// Strikes and Spares in frames 1-9
if (frameScore == 10 && i < 9)
{
int nextFrameScore = tenFrameScores[i+1];
if (throw1 == 10) // it's a strike
{
ColorMessage(ConsoleColor.Green, "STRIKE!");
// Check to see if the next throw was also a strike
if (tenFrames[i+1][0] == 10) // it's a strike
{
// bonus = 10 + the first throw of the next frame
if (i < 8)
{
int strikeBonus = 10 + tenFrames[i+2][0];
Console.WriteLine("\tNext frame is a strike strikeBonus: " + strikeBonus);
tenFrameScores[i] += strikeBonus;
}
else // frame 9, look at just the first two throws of frame 10
{
int strikeBonus = tenFrames[i+1][0] + tenFrames[i+1][1];
Console.WriteLine("\tNext frame is a strike strikeBonus: " + strikeBonus);
tenFrameScores[i] += strikeBonus;
}
}
else
{
// bonus = the total of the two throws in the next frame (nextFrameScore)
Console.WriteLine("\tstrikeBonus: " + nextFrameScore);
tenFrameScores[i] += nextFrameScore;
}
}
else // spare
{
ColorMessage(ConsoleColor.Cyan, "SPARE!");
Console.WriteLine("\tspareBonus: " + tenFrames[i+1][0]);
tenFrameScores[i] += tenFrames[i+1][0];
}
}
else // Tenth frame bonuses
{
if (throw1 == 10) // strike in the tenth frame
{
ColorMessage(ConsoleColor.Green, "STRIKE! (in the 10th)");
// bonus = throw2 + throw3 (throw2 is already included in tenFrameScores[i])
Console.WriteLine("\tstrikeBonus: " + (throw2 + throw3));
tenFrameScores[i] += throw3;
}
else if (frameScore == 10) // spare in the first two throws of the 10th frame
{
ColorMessage(ConsoleColor.Cyan, "SPARE (in the 10th)!");
// bonus = last throw of the 10th frame
Console.WriteLine("\tspareBonus: " + throw3);
tenFrameScores[i] += throw3;
}
}
}
for (int i = 0; i < 10; i++)
{
Score += tenFrameScores[i];
Console.WriteLine(tenFrameScores[i]);
}
}
}
public void ColorMessage(ConsoleColor color, string message)
{
// Set text color
Console.ForegroundColor = color;
// Write message
Console.WriteLine(message);
// Reset text color
Console.ResetColor();
}
}
public class Test
{
public static void Main()
{
BowlingGame newGame = new BowlingGame();
// Test first game - SUCCESS! (190)
//newGame.RecordFrame(4, 6);
//newGame.RecordFrame(3, 6);
//newGame.RecordFrame(2, 1);
//newGame.RecordFrame(10); // 30
//newGame.RecordFrame(10); // 30
//newGame.RecordFrame(10); // 21
//newGame.RecordFrame(10); // 20
//newGame.RecordFrame(1, 9);
//newGame.RecordFrame(10,0);
//newGame.RecordFrame(10, 4, 6);
// Test perfect game - SUCCESS! (300)
//newGame.RecordFrame(10);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10);
//newGame.RecordFrame(10, 10, 10);
// Test all zeroes
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(0, 0);
// Test successful (111)
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(2, 8);
//newGame.RecordFrame(4, 3);
//newGame.RecordFrame(0, 8);
//newGame.RecordFrame(0, 5);
//newGame.RecordFrame(7, 0);
//newGame.RecordFrame(1, 9);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(0, 0);
//newGame.RecordFrame(10, 10, 0);
// Test successful (236)
//newGame.RecordFrame(3, 7);
//newGame.RecordFrame(9, 1);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(8, 2);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(9, 1);
//newGame.RecordFrame(10, 0);
//newGame.RecordFrame(10, 10, 10);
// All spares + bonus in 10th (150)
newGame.RecordFrame(1, 9);
newGame.RecordFrame(2, 8);
newGame.RecordFrame(3, 7);
newGame.RecordFrame(4, 6);
newGame.RecordFrame(5, 5);
newGame.RecordFrame(6, 4);
newGame.RecordFrame(7, 3);
newGame.RecordFrame(8, 2);
newGame.RecordFrame(9, 1);
newGame.RecordFrame(0, 10, 6);
string finalScore = "\nThe FINAL score is : " + newGame.Score;
newGame.ColorMessage(ConsoleColor.Red, finalScore);
}
}
}
A couple days later I heard feedback from them that basically they were disappointed with the code that I turned in, but they didn't say more than that.
Can someone tell me why? Did I fail to understand what they were really looking for? I thought my code was good because it met all of their stated requirements and does what it's supposed to.
What am I missing?