Your SlideShare is downloading. ×
Bowling Game Kata in C# Adapted
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Bowling Game Kata in C# Adapted

3,396
views

Published on


1 Comment
2 Likes
Statistics
Notes
No Downloads
Views
Total Views
3,396
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
55
Comments
1
Likes
2
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • I would like to thank Uncle Bob Martin and Object Mentor for the bowling game kata.
  • Transcript

    • 1. Bowling Game KataAdapted for C# and nUnit
      By Dan Stewart (with modifications by Mike Clement)
      dan@stewshack.com
      mike@softwareontheside.com
    • 2. 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.
    • 3. A quick design session
      Clearly we need the Game class.
    • 4. A quick design session
      A game has 10 frames.
    • 5. A quick design session
      A frame has 1 or two rolls.
    • 6. A quick design session
      The tenth frame has two or three rolls.
      It is different from all the other frames.
    • 7. A quick design session
      The score function must
      include all the frames,
      and calculate all their scores.
    • 8. A quick design session
      The score for a spare or a strike depends on the frame’s successor
    • 9. Begin.
      Create a class library named BowlingGameKata
    • 10. Begin.
      Add a test fixture named BowlingGameTests to the project
      usingNUnit.Framework;
      namespaceBowlingGameKata
      {
      [TestFixture]
      publicclassBowlingGameTests
      {
      [Test]
      publicvoidGutterGameTest()
      {
      }
      }
      }
    • 11. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Game g = new Game();
      }
    • 12. The first test.
      namespaceBowlingGameKata
      {
      publicclassGame
      {
      }
      }
      [Test]
      publicvoidGutterGameTest()
      {
      Game g = new Game();
      }
    • 13. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Game g = new Game();
      }
      publicclassGame
      {
      }
    • 14. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      }
      publicclassGame
      {
      }
    • 15. The first test.
      public class Game
      {
      publicvoid Roll(int p)
      {
      thrownewSystem.NotImplementedException();
      }
      }
      [Test]
      publicvoidGutterGameTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      }
    • 16. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      public class Game
      {
      publicvoid Roll(int p)
      {
      thrownewSystem.NotImplementedException();
      }
      }
    • 17. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      publicclassGame
      {
      publicvoid Roll(int p)
      {
      thrownew System.NotImplementedException();
      }
      publicobject Score()
      {
      thrownew System.NotImplementedException();
      }
      }
      Failed GutterGameTestthrew exception
    • 18. The first test.
      [Test]
      publicvoidGutterGameTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      }
      publicintScore()
      {
      returnscore;
      }
      }
    • 19. The Second test.
      [Test]
      publicvoidAllOnesTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(1);
      }
      Assert.That(g.Score(), Is.EqualTo(20));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      }
      publicintScore()
      {
      returnscore;
      }
      }
    • 20. The Second test.
      - Game creation is duplicated
      - roll loop is duplicated
      [Test]
      publicvoidAllOnesTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(1);
      }
      Assert.That(g.Score(), Is.EqualTo(20));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      }
      publicintScore()
      {
      returnscore;
      }
      }
    • 21. The Second test.
      - Game creation is duplicated
      - roll loop is duplicated
      [Test]
      publicvoidAllOnesTest()
      {
      Gameg = newGame();
      for(int i = 0; i < 20; i++)
      {
      g.Roll(1);
      }
      Assert.That(g.Score(), Is.EqualTo(20));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      }
      publicintScore()
      {
      return score;
      }
      }
      Assert.AreEqual failed. Expected:<20>. Actual:<0>.
    • 22. The Second test.
      - roll loop is duplicated
      privateGame g;
      [SetUp]
      publicvoidSetup()
      {
      g = newGame();
      }
      [Test]
      publicvoidGutterGameTest()
      {
      for(int i = 0; i < 20; i++)
      {
      g.Roll(0);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      [Test]
      publicvoidAllOnesTest()
      {
      for(int i = 0; i < 20; i++)
      {
      g.Roll(1);
      }
      Assert.That(g.Score(), Is.EqualTo(20));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
    • 23. The Second test.
      - roll loop is duplicated
      [Test]
      publicvoidGutterGameTest()
      {
      int rolls = 20;
      intpins = 0;
      for(int i = 0; i < rolls; i++)
      {
      g.Roll(pins);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
    • 24. The Second test.
      - roll loop is duplicated
      [Test]
      publicvoidGutterGameTest()
      {
      int rolls = 20;
      intpins = 0;
      RollMany(rolls, pins);
      for(int i = 0; i < rolls; i++)
      {
      g.Roll(pins);
      }
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
    • 25. The Second test.
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
      [Test]
      publicvoidGutterGameTest()
      {
      RollMany(20, 0);
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      privatevoidRollMany(introlls, int pins)
      {
      for(int i = 0; i < rolls; i++)
      {
      g.Roll(pins);
      }
      }
    • 26. The Second test.
      [Test]
      publicvoidGutterGameTest()
      {
      RollMany(20, 0);
      Assert.That(g.Score(), Is.EqualTo(0));
      }
      [Test]
      publicvoidAllOnesTest()
      {
      RollMany(20, 1);
      Assert.That(g.Score(), Is.EqualTo(20));
      }
      privatevoidRollMany(int rolls, int pins)
      {
      for(int i = 0; i < rolls; i++)
      {
      g.Roll(pins);
      }
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
    • 27. The Third test.
      - ugly comment in test.
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
      Failed Assert.AreEqual Expected:<16>. Actual:<13>
    • 28. The Third test.
      - ugly comment in test.
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      tempted to use flag to remember previous roll. So design must be wrong.
      Failed Assert.AreEqual Expected:<16>. Actual:<13>
    • 29. 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;
      }
      }
      [Test]
      publicvoidOneSpareTest()
      {
      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>
    • 30. The Third test.
      - ugly comment in test.
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      //Assert.That(g.Score(), Is.EqualTo(16));
      }
      publicclassGame
      {
      privateint score;
      publicvoid Roll(int pins)
      {
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
    • 31. The Third test.
      - ugly comment in test.
      publicclassGame
      {
      privateint score;
      privateint[] rolls = newint[21];
      privateint currentRoll;
      publicvoid Roll(int pins)
      {
      rolls[currentRoll++] = pins;
      score += pins;
      }
      publicint Score()
      {
      return score;
      }
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      //Assert.That(g.Score(), Is.EqualTo(16));
      }
    • 32. 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;
      }
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      //Assert.That(g.Score(), Is.EqualTo(16));
      }
    • 33. The Third test.
      - ugly comment in test.
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      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>
    • 34. 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;
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      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>
    • 35. 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;
      }
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      //Assert.That(g.Score(), Is.EqualTo(16));
      Assert.Inconclusive();
      }
    • 36. The Third test.
      - ugly comment in test.
      publicint Score()
      {
      int score = 0;
      inti = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      score += rolls[i] + rolls[i + 1];
      i+= 2;
      }
      returnscore;
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      //Assert.That(g.Score(), Is.EqualTo(16));
      Assert.Inconclusive();
      }
    • 37. The Third test.
      - ugly comment in test.
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      publicint Score()
      {
      int score = 0;
      int i = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      score += rolls[i] + rolls[i + 1];
      i += 2;
      }
      return score;
      }
      Failed Assert.AreEqual Expected:<16>. Actual:<13>
    • 38. The Third test.
      - ugly comment in test.
      publicint Score()
      {
      int score = 0;
      inti = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      // spare
      if (rolls[i] + rolls[i + 1] == 10)
      {
      score += 10 + rolls[i + 2];
      i += 2;
      }
      else
      {
      score += rolls[i] + rolls[i + 1];
      i += 2;
      }
      }
      return score;
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
    • 39. The Third test.
      • ugly comment in test.
      • 40. ugly comment in conditional.
      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];
      roll += 2;
      }
      else
      {
      score += rolls[roll] + rolls[roll + 1];
      roll += 2;
      }
      }
      return score;
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
    • 41. The Third test.
      - ugly comment in test.
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsSpare(roll))
      {
      score += 10 + rolls[roll + 2];
      roll += 2;
      }
      else
      {
      score += rolls[roll] + rolls[roll + 1];
      roll += 2;
      }
      }
      returnscore;
      }
      privateboolIsSpare(int roll)
      {
      returnrolls[roll] +
      rolls[roll + 1] == 10;
      }
      [Test]
      publicvoidOneSpareTest()
      {
      g.Roll(5);
      g.Roll(5); // spare
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
    • 42. The Third test.
      [Test]
      publicvoidOneSpareTest()
      {
      RollSpare();
      g.Roll(3);
      RollMany(17, 0);
      Assert.That(g.Score(), Is.EqualTo(16));
      }
      privatevoidRollSpare()
      {
      g.Roll(5);
      g.Roll(5);
      }
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsSpare(roll))
      {
      score += 10 + rolls[roll + 2];
      roll += 2;
      }
      else
      {
      score += rolls[roll] + rolls[roll + 1];
      roll += 2;
      }
      }
      returnscore;
      }
      privateboolIsSpare(int roll)
      {
      returnrolls[roll] +
      rolls[roll + 1] == 10;
      }
    • 43. The Fourth test.
      - ugly comment in OneStrikeTest.
      [Test]
      publicvoidOneStrikeTest()
      {
      g.Roll(10); // strike
      g.Roll(3);
      g.Roll(4);
      RollMany(16, 0);
      Assert.That(g.Score(), Is.EqualTo(24));
      }
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsSpare(roll))
      {
      score += 10 + rolls[roll + 2];
      }
      else
      {
      score += rolls[roll] + rolls[roll + 1];
      }
      roll += 2;
      }
      returnscore;
      }
      privatebool IsSpare(int roll)
      {
      returnrolls[roll] +
      rolls[roll + 1] == 10;
      }
      Failed Assert.AreEqual Expected:<24>. Actual:<17>
    • 44. The Fourth test.
      • ugly comment in OneStrikeTest.
      • 45. ugly comment in conditional.
      • 46. ugly expressions.
      publicint Score()
      {
      intscore = 0;
      introll = 0;
      for(int frame = 0; frame < 10; frame++)
      {
      if(rolls[roll] == 10) // strike
      {
      score += 10 + rolls[roll + 1]
      + rolls[roll + 2];
      roll++;
      }
      elseif (IsSpare(roll))
      {
      score += 10 + rolls[roll + 2];
      roll += 2;
      }
      else
      {
      score += rolls[roll] + rolls[roll + 1];
      roll += 2;
      }
      }
      returnscore;
      }
      [Test]
      publicvoidOneStrikeTest()
      {
      g.Roll(10); // strike
      g.Roll(3);
      g.Roll(4);
      RollMany(16, 0);
      Assert.That(g.Score(), Is.EqualTo(24));
      }
    • 47. The Fourth test.
      • ugly comment in OneStrikeTest.
      • 48. ugly comment in conditional.
      publicint Score()
      {
      intscore = 0;
      introll = 0;
      for(int frame = 0; frame < 10; frame++)
      {
      if(rolls[roll] == 10) // strike
      {
      score += 10 + StrikeBonus(roll);
      roll++;
      }
      elseif (IsSpare(roll))
      {
      score += 10 + SpareBonus(roll);
      roll += 2;
      }
      else
      {
      score += SumOfBallsInFrame(roll);
      roll += 2;
      }
      }
      returnscore;
      }
      privateint SumOfBallsInFrame(int roll)
      {
      returnrolls[roll] + rolls[roll + 1];
      }
      privateint SpareBonus(int roll)
      {
      returnrolls[roll + 2];
      }
      privateint StrikeBonus(int roll)
      {
      returnrolls[roll + 1] + rolls[roll + 2];
      }
      [Test]
      publicvoidOneStrikeTest()
      {
      g.Roll(10); // strike
      g.Roll(3);
      g.Roll(4);
      RollMany(16, 0);
      Assert.That(g.Score(), Is.EqualTo(24));
      }
    • 49. The Fourth test.
      - ugly comment in OneStrikeTest.
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsStrike(roll))
      {
      score += 10
      + StrikeBonus(roll);
      roll++;
      }
      elseif (IsSpare(roll))
      {
      score += 10
      + SpareBonus(roll);
      roll += 2;
      }
      else
      {
      score += SumOfBallsInFrame(roll);
      roll += 2;
      }
      }
      returnscore;
      }
      privatebool IsStrike(int roll)
      {
      returnrolls[roll] == 10;
      }
      [Test]
      publicvoidOneStrikeTest()
      {
      g.Roll(10); // strike
      g.Roll(3);
      g.Roll(4);
      RollMany(16, 0);
      Assert.That(g.Score(), Is.EqualTo(24));
      }
    • 50. The Fourth test.
      [Test]
      publicvoidOneStrikeTest()
      {
      RollStrike();
      g.Roll(3);
      g.Roll(4);
      RollMany(16, 0);
      Assert.That(g.Score(), Is.EqualTo(24));
      }
      privatevoidRollStrike()
      {
      g.Roll(10);
      }
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsStrike(roll))
      {
      score += 10
      + StrikeBonus(roll);
      roll++;
      }
      elseif (IsSpare(roll))
      {
      score += 10
      + SpareBonus(roll);
      roll += 2;
      }
      else
      {
      score += SumOfBallsInFrame(roll);
      roll += 2;
      }
      }
      returnscore;
      }
    • 51. The Fifth test.
      [Test]
      publicvoidPerfectGameTest()
      {
      RollMany(12, 10);
      Assert.That(g.Score(), Is.EqualTo(300));
      }
      publicint Score()
      {
      int score = 0;
      int roll = 0;
      for (int frame = 0; frame < 10; frame++)
      {
      if(IsStrike(roll))
      {
      score += 10
      + StrikeBonus(roll);
      roll++;
      }
      elseif (IsSpare(roll))
      {
      score += 10
      + SpareBonus(roll);
      roll += 2;
      }
      else
      {
      score += SumOfBallsInFrame(roll);
      roll += 2;
      }
      }
      returnscore;
      }
    • 52. Discussion/Review
    • 53. Adapted from: