New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Bowling Game Kata C#
1.
2. Adapted for C# 4.0 (With Visual Studio 2010) By Dan Stewart dan@stewshack.com
3. Scoring Bowling. The game consists of 10 frames as shown above. In each frame the player has two opportunities to knock down 10 pins. The score for the frame is the total number of pins knocked down, plus bonuses for strikes and spares. A spare is when the player knocks down all 10 pins in two tries. The bonus for that frame is the number of pins knocked down by the next roll. So in frame 3 above, the score is 10 (the total number knocked down) plus a bonus of 5 (the number of pins knocked down on the next roll.) A strike is when the player knocks down all 10 pins on his first try. The bonus for that frame is the value of the next two balls rolled. In the tenth frame a player who rolls a spare or strike is allowed to roll the extra balls to complete the frame. However no more than three balls can be rolled in tenth frame.
7. A quick design session The tenth frame has two or three rolls. It is different from all the other frames.
8. A quick design session The score function must include all the frames, and calculate all their scores.
9. A quick design session The score for a spare or a strike depends on the frame’s successor
10. Begin. Create a console application named BowlingGame Add a MSTest project named BowlingGameTest to the solution
11. Begin. Add a unit test named GameTest to the project using Microsoft.VisualStudio.TestTools.UnitTesting; namespace BowlingGameTest { [TestClass] publicclassGameTest { privateTestContext testContextInstance; publicTestContext TestContext { get { return testContextInstance; } set { testContextInstance = value; } } [TestMethod] publicvoid TestMethod1() { } } }
12. The first test. [TestMethod] publicvoidTestGutterGame() { Game g = new Game(); }
13. The first test. namespaceBowlingGame { publicclassGame { } } [TestMethod] publicvoidTestGutterGame() { Game g = new Game(); }
14. The first test. [TestMethod] publicvoidTestGutterGame() { Game g = new Game(); } publicclassGame { }
15. The first test. [TestMethod] publicvoidTestGutterGame() { Gameg = newGame(); for(int i = 0; i < 20; i++) { g.Roll(0); } } publicclassGame { }
16. The first test. public class Game { publicvoid Roll(int p) { thrownewSystem.NotImplementedException(); } } [TestMethod] publicvoidTestGutterGame() { Gameg = newGame(); for(int i = 0; i < 20; i++) { g.Roll(0); } }
17. The first test. [TestMethod] publicvoidTestGutterGame() { Gameg = newGame(); for(int i = 0; i < 20; i++) { g.Roll(0); } Assert.AreEqual(0, g.Score()); } public class Game { publicvoid Roll(int p) { thrownewSystem.NotImplementedException(); } }
29. The Third test. - ugly comment in test. publicclassGame { privateint score; publicvoid Roll(int pins) { score += pins; } publicint Score() { return score; } } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score()); } tempted to use flag to remember previous roll. So design must be wrong. Failed Assert.AreEqual Expected:<16>. Actual:<13>
30. The Third test. - ugly comment in test. Roll() calculates score, but name does not imply that. publicclassGame { privateint score; publicvoid Roll(int pins) { score += pins; } publicint Score() { return score; } } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score); } Score() does not calculate score, but name implies that it does. Design is wrong. Responsibilities are misplaced. Failed Assert.AreEqual Expected:<16>. Actual:<13>
33. The Third test. - ugly comment in test. publicclassGame { privateint[] rolls = newint[21]; privateint currentRoll; publicvoid Roll(int pins) { rolls[currentRoll++] = pins; } publicint Score() { int score = 0; for (int i = 0; i < rolls.Length; i++) { score += rolls[i]; } return score; } } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.AreEqual(16, g.Score()); Assert.Inconclusive(); }
34. The Third test. - ugly comment in test. [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score()); } publicclassGame { privateint[] rolls = newint[21]; privateint currentRoll; publicvoid Roll(int pins) { rolls[currentRoll++] = pins; } publicint Score() { int score = 0; for (int i = 0; i < rolls.Length; i++) { score += rolls[i]; } return score; } } Failed Assert.AreEqual Expected:<16>. Actual:<13>
35. The Third test. - ugly comment in test. publicint Score() { int score = 0; for (int i = 0; i < rolls.Length; i++) { // spare if(rolls[i] + rolls[i+1] == 10) { score += ... } score += rolls[i]; } return score; } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score()); } This isn’t going to work because i might not refer to the first ball of the frame. Design is still wrong. Need to walk through array two balls (one frame) at a time. Failed Assert.AreEqual Expected:<16>. Actual:<13>
36. The Third test. - ugly comment in test. publicclassGame { privateint[] rolls = newint[21]; privateint currentRoll; publicvoid Roll(int pins) { rolls[currentRoll++] = pins; } publicint Score() { int score = 0; for (int i = 0; i < rolls.Length; i++) { score += rolls[i]; } return score; } } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.AreEqual(16, g.Score()); Assert.Inconclusive(); }
37. The Third test. - ugly comment in test. publicint Score() { int score = 0; int roll = 0; for (int frame = 0; frame < 10; frame++) { score += rolls[roll] + rolls[roll + 1]; roll += 2; } returnscore; } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); //Assert.AreEqual(16, g.Score()); Assert.Inconclusive(); }
38. The Third test. - ugly comment in test. [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score()); } publicint Score() { int score = 0; int roll = 0; for (int frame = 0; frame < 10; frame++) { score += rolls[roll] + rolls[roll + 1]; roll += 2; } returnscore; } Failed Assert.AreEqual Expected:<16>. Actual:<13>
39. The Third test. - ugly comment in test. publicint Score() { int score = 0; int roll = 0; for (int frame = 0; frame < 10; frame++) { // spare if (rolls[roll] + rolls[roll + 1] == 10) { score += 10 + rolls[roll + 2]; } else { score += rolls[roll] + rolls[roll + 1]; } roll += 2; } return score; } [TestMethod] publicvoid TestOneSpare() { g.Roll(5); g.Roll(5); // spare g.Roll(3); RollMany(17, 0); Assert.AreEqual(16, g.Score()); }