• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Jeanne Boyarsky
  • Ron McLeod
Sheriffs:
  • Paul Clapham
  • Liutauras Vilda
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
Bartenders:

Bowling League: first story

 
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In a previous post, I mentioned how I tried my hand at the problem that Bob Martin and Bob Koss solved in their article
"An Extreme Programming Episode."

If you haven't already, please read the article first so that you will understand what I'm talking about in subsequent replies to this post. I will describe the process that I went through to arrive at a solution to the same problem and hopefully I will get some feedback and discussion going.
J.Lacar
Added link to the code thread
Added the following DISCLAIMER: I am NOT an experienced XPer although I am very enthusiastic about it. My posts are entirely my own opinions and may or may not be consistent with actual XP practices. If you really want to know more about XP, I suggest you visit one of the XP sites: xprogramming.com, extremeprogramming.org, or objectmentor.com

[This message has been edited by JUNILU LACAR (edited March 26, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
First cut
The task that the Bobs decided to tackle first was "Score a Single Bowling Game." I tried to imagine how I would go about doing it had I been given the same task.
Even though I know how scoring is done in bowling, I didn't want to come up with the requirements myself. That would take away from the experience of using an XP approach. So, I asked my wife to pretend that she was trying to teach somebody who had no prior knowledge of bowling: me, the developer.
I tried to get her to write down the requirements but she wouldn't go that far in humoring me. So I caved and started interviewing her and writing notes based on her answers. After about 10 minutes, I had something like this:

  • The object of the game is to knock down as many pins as you can.
  • A game is scored on a single line on the scoresheet. The line is divided into 10 boxes or "frames".
  • Scoring of the game proceeds from left to right, with the running total of pins knocked down noted in each frame.
  • Counts of pins knocked down in each frame are also noted in the upper portion of the frame.
  • In each frame, the player can knock down at most 10 pins.
  • The player starts bowling each frame with all 10 pins standing
  • The player has two "throws" with which to knock down all 10 pins for the current frame.
  • Each frame on the scorecard has a small box in the upper right corner where marks are made to indicate a "strike" or "spare" or the number of pins knocked down by the second throw.
  • If the player knocks down all the pins on the first throw, it's called a "strike" and is usually marked as an 'X' in the small box. The number of pins knocked down with the next two throws are added as "bonus" points for this frame.
  • If the player knocks down all the pins on the second throw, it's called a "spare". The number of pins knocked down with the next throw will be added as "bonus" points for this frame.
  • If the player does not knock down all 10 pins in a frame with two throws, the number of pins knocked down are added to the running total, which is marked in the frame.

  • Those of you who know how to score bowling will probably notice that the rules listed above are far from being complete. I noticed this too and stopped. Looking at the list, I realized a few things:

    1. This was going to be harder than I thought.
    2. If I really didn't know how to score a bowling game, I probably still wouldn't know how to do it if all I had was this list.
    3. My user didn't know how to convey the rules clearly and coherently. If I didn't have prior knowledge or hadn't asked the right questions, I probably would have an even more incomplete list. What's more, since I (supposedly) didn't have prior knowledge of the rules, I wouldn't have even known that the list was still incomplete!
    4. The rules were all jumbled up and the actual procedure for scoring was still not clear.

    5. Time for a different approach...

      [This message has been edited by JUNILU LACAR (edited March 24, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Second cut
After stopping short of going into a mini "analysis-paralysis", I reflected on what had happened. It really wasn't unlike what had happened in many of the projects I have been involved with in the past. Developers would go in and interview the client, developers take notes, developers go back to their desks and try to make sense of those notes, transfer those notes and produce a lot of formal "documentation" yet still not quite fully understand the business. Old habits are hard to break, even though you know that no good can come out of them.
So, I told my wife, "OK, just run me through a sample game that has most if not all the things that we just talked about." And she proceeds to do so. In the end, she manages to describe what to do in the following scenarios:

  • Two balls thrown, no mark
  • Four balls thrown, no mark
  • A strike is thrown
  • Two strikes in a row
  • Three strikes in a row (turkey)
  • Four strikes in a row (four-bagger)
  • A spare is thrown
  • Two spares in a row
  • Three spares in a row
  • Strike thrown in last frame
  • Two strikes in last frame
  • Three strikes in last frame
  • Spare thrown in last frame, then a strike for the "bonus" ball

  • Now I felt I was getting somewhere...time to write some code.
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
First TestCase
After the second cut and having all those scenarios explained to me, I thought I had a pretty good idea of how to score a bowling game.
I already had JUnit installed and ready to use, so I fired up TextPad and started writing my first test case. In XP, you have to write a test case first before writing code.
The first test case was very similar to what the Bobs had and was related to the first scenario: two throws, no mark. I decided to set up the Game fixture from the start since I already knew that I would be needing it for more tests.

This obviously didn't compile since the Game class did not exist. So, I set about writing the skeleton for the Game class:

Now this was all preliminary stuff just to get the brain juices going. I wasn't skipping the design though. Writing the test first actually helped me start thinking of the Game class and what its public interface would be. For me, I felt that it involved more of design rather than coding.
I saved both TestGame.java and Game.java in the same directory and compiled TestGame successfully.
I run JUnit. Red bar: test failed. Now to make the test work:

Save, then run the test in JUnit again. This time, I get a green bar: 100% pass on the tests.
Now the fun begins...

[This message has been edited by JUNILU LACAR (edited March 25, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
CVSjr
Well, it's been a day and I'm still here talking to myself. This part of JavaRanch sure is quiet. Maybe everybody thinks I'm just a crazy, babbling old coot with too much time on his hands. Oh well, at least I'll save some space on my hard disk by putting this here.
Looks like we need to have some way to keep track of the progress of the code. I'm going to dub this online configuration management system "CVSjr".
Here's how I'm thinking it will work:

  • Two threads will be set up for each task to be worked on: a discussion thread and a code thread.
  • Discussion threads and code threads will have the same topic title but the code thread will be qualified by "(code)"
  • Discussion threads and code threads will have links to each other to facilitate going back and forth between them.
  • In the code thread, A JavaDoc @version comment will be placed at the top of each compilation unit to keep track of the increments
  • Each new unit revision will be made by replying to the most recent revision.
  • How do we prevent more than one person working on the code at the same time? We need some sort of locking protocol. Man, this could get ugly. I guess we'll deal with it when we have to. (XP creed: YAGNI - You ain't gonna need it)
  • Version numbers will have the form: MM.mm.bbbb where MM is the application Increment#, mm is the "edition" number, and bbbb is the unit increment#.
  • The unit increment# should be advanced after each revision that results from either refactoring or code additions.
  • Any posts to the discussion thread should give the reference the version number in the code thread.
  • When a code thread is successfully integrated, a request will be sent to the bartender to lock the thread to prevent further posts. In effect, we'll be "editioning" that increment. The next "edition" will then be started in a new thread.

  • I'll start a new thread for the code here .

    [This message has been edited by JUNILU LACAR (edited March 25, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Second and Third test
Now to add the test for the second scenario: Four throws, no mark. (See Version 0.0.0002)
This test passes without any change to the Game class. I wonder if that test was even necessary. I'll just leave it there for completeness since it was a scenario given by the user.
It will definitely not work with two strikes in a row... so let's write that that test next. (see Version 0.0.0003)
OK, so that test fails. Now to make it work...

[This message has been edited by JUNILU LACAR (edited March 28, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Making testTwoStrikes() work
Refer to code Version 0.0.0003
Before proceeding, I guess I need to go back to that list of rules that I got in "First cut". When a strike is thrown, the next two throws are added to the score for this frame, kind of like "bonus" points. That means that if a strike is thrown on the second frame, those 10 pinfalls are added to the first frame and also counted in the second frame, making the total score 30 at that point.
The Game class doesn't enforce this logic yet. But what is the simplest way I can implement it? (XP creed: DTSTTCPW-Do the simplest thing that could possibly work)
Time to do some design again...
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Making testTwoStrikes() work (continued)
Refer to code Version 0.0.0003
Well, seems like somebody finally decided to check out who was kicking the dust up around here... Thanks for swingin' by, Sheriff Frank.
I'd welcome any input you or anybody else have to offer on the design of this thing. Like I said, I already came up with my own solution to the problem and I'm just trying to retrace my steps to show how it played out for me. It would be interesting though to see if it goes a different direction with input from others, so feel free to add to the design at any point.

The problem now was to find a way to handle "bonus" balls after throwing a strike. If I could do this, I could make testTwoStrikes() work.
In the back of my mind, I had these thoughts:

  • While XP advocates doing the simplest thing that could possibly work, you don't always start out with the simplest thing. In fact, you almost always start out by writing code that's more complicated that it should be (at least I do).
  • Programming is often like sculpture: you take a big block of the medium (rock or code) and and slowly chip away the pieces that are not part of the final work.
  • Most people (me included) learn through experience.

  • I decided to go with my gut and started deliberating on a Frame object to help keep track of the Game. Now it may not be the simplest thing to do, but it was the first thing that came to mind.
    Let's see if CRC cards will help here.
    ----------
    Class: Game
    Responsibilities:
    - Accept number of pinfalls
    - Know the current score
    Collaborates with:
    Frame
    ----------
    Class: Frame
    Responsibilities:
    Know how many balls have been thrown
    Know the number of pinfalls
    Know if a strike or spare was thrown
    Collaborates with:
    <none>
    ----------
    All right, let's throw the "ball" around here:

    1. Throw the ball to Game and ask it to add() a pinfall count. Game delegates the request to the current Frame. I wonder if we need to add "Keep track of current Frame" to Game's list of responsibilities.
    2. Frame now has the ball. Frame should know if it can accept throws (maximum of two throws per frame). But then again, if a strike is thrown, then Frame should only accept 1 throw. OK, let's just say that Frame accepts the throw and records the pinfall. Frame now throws the ball back to Game
    3. Game throws ball back to me (the user) since it doesn't have anything else to do.

    4. Now if I want to get the score:

      1. Throw the ball to Game and ask it for the score().
      2. Game has the ball. Game asks the first Frame for number of pinfalls it recorded. (pass ball to Frame)
      3. Frame reports number of pinfalls. Now we're treading in non-OO territory. Allen Holub advises: "Don't ask for the information that you need to do something; rather, ask the object that has that information to do the job for you." Ah well, we'll refactor it later I guess.
      4. Game applies rules for "bonus" points, if necessary by asking the next few Frames for their pinfall count.

      5. I don't know if I'm guilty of doing BUFD (Big UpFront Design) yet or not but it seems like I need to get this planned out before I implement anything.
        It's already 1:30am and I have to get up early in the morning. I'll pick up again tomorrow night. Hopefully, somebody joins in between now and then.

        [This message has been edited by JUNILU LACAR (edited March 25, 2001).]
 
Ranch Hand
Posts: 1874
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Junilu , Just some curiosity. You have mentioned time as 1.30 am . But , UBB shows the time as 11.30 pm. From Where are you logging ? I mean which part of u.s.a?

Shailesh.
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Shailesh,
I'm in Columbus, OH (US Eastern Time). UBB times are Mountain Time, 2 hrs behind EST. I'm in the COSI Science Museum right now with my kids. If you ever get a chance to visit Columbus, OH you might want to check out the COSI. It's coool
J.Lacar
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Enter the Frame
Continuing to try to make testTwoStrikes() work, it seems I need a Frame object to help keep track of the Game.
Now, before I can start writing code for Frame, I need to write the unit tests. So, I create TestFrame.java (Version 0.0.0001) with a Frame fixture and a first test called testFirstBallNoMark(). As you can see, the test invokes two responsibilities of Frame: to accept a pinfall count with the add() method (oops, looks like that needs to be added to the CRC card), and to report the pinfall count so far with the getPinFalls() method.
This fails compilation of course since there is no Frame class. So we create the skeleton of the Frame class in Frame.java (Version 0.0.0000)
I load the TestFrame unit test in JUnit and run it. As expected, the test fails. Now to make it work.
Now I could do the same thing I did in Game, which is to set up a private field to keep a running total of pinfalls. However, I think the name of the test is leading me somewhere here. testFirstBallNoMark() will lead to testSecondBallNoMark(). Does this mean that I need to keep track of how many pins fell with each ball thrown? I think so.
Consider the scenario where a spare is throw in a frame and no mark is thrown in the next frame. When Game tries to figure out the score in the spare frame, it will have to ask the next frame for the number of pinfalls for the first ball thrown to get the number of "bonus" points.
So, I have Frame.java (version 0.0.0001) I know some people are going to look at this code and have some way to improve it. That's not the purpose right now. We just want the test to run. We'll refactor this later but only to make it easier to add new functionality.
Gotta sign off for now. I have to interview with a new client early tomorrow morning. Still hoping for participation from other JavaRanchers.
J.Lacar

[This message has been edited by JUNILU LACAR (edited March 28, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Back to the Game
Looking back at the CRC cards, I see that Game delegates add() to Frame. Why do we have to go through Game to add pinfalls? Since there's a composition relationship between Game and Frame (a Game has exactly 10 Frames), my initial thought was to make Frame an inner class of Game. So, as far as the outside world was concerned, the points were still being added to Game. IOW, the Frame was really just a part of the implementation.
However, I hadn't dug deep enough into JUnit to know how to set up test cases for inner classes. Also, in their article, the Bobs declared all their classes and test cases public so I decided to follow their lead for now. At this point, I didn't see that it would make a difference if I made Frame a public or inner class.
As I said, Game delegates add() to Frame. First, I add a private member array of 10 Frames. Then, I modify Game.add() to delegate to Frame. Refer to Game.java (version 0.0.0004). I also add a private reference to the current Frame. Back to JUnit, load and run TestGame. Red bar: 100% fail. This is expected because getScore() is now wrong.
Oops, on more careful inspection of JUnit results, I see I have 3 Errors, not Failures. I realize that the _frames array has not been initialized. Cool. JUnit also helps me debug. I add a loop to initialize the array of Frames and save/compile. I won't increment the version# until I get a green bar.
I run JUnit again. Still red bar but this time I get 3 Failures instead of Errors.
Now to make getScore() working again. We now have Game collaborating with Frame to keep track of throws. I suppose Game will go to each Frame in turn and ask the number of pinfalls. So I write some code. Save/compile, then run TestGame in JUnit. Red bar, 2 failures. Only testTwoThrowsNoMark() is working again.

[This message has been edited by JUNILU LACAR (edited March 28, 2001).]
 
Junilu Lacar
Sheriff
Posts: 17734
302
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Deja vu all over again
Refer to Game.java (version 0.0.0004).
Just a quick pause to get my bearings. This whole increment started out trying to get TestGame.testTwoStrikes() to work. Then I went and started a new TestCase: TestFrame. Then I did Frame and got TestFrame to work. Then back to Game and made it collaborate with Frame. At that point, my TestGame results regressed to 100% fail and now I have to make the first two tests working again. When I succeed with that, I'll be back where I started: trying to get TestGame.testTwoStrikes() to work!
Did I just go in a big circle? A small nugget of doubt begins to form in my mind. Am I going about this the wrong way? But then again, I do have a new unit test and class. Oh well, we'll just have to see how it goes. Let's take more baby steps.
testFourThrowsNoMark() is failing because the _currentFrame never advances after it is done, i.e. the two throws have been accepted. So I add some code to advance _currentFrame in which I invoke the isDone() method of Frame. This of course does not exist so I have to go and extend Frame.
I'm going to start a to-do list here so I don't lose track of where I sidetrack from.
To-Do List:
- get testTwoStrikes() to work
- get testFourThrowsNoMark() to work
- add Frame.isDone()
- write test for Frame.isDone()
The last task will be the current one. When I complete each task, I'll cross it out and work on the next one above it. If I need to sidetrack again, I add a new task and that becomes the current one. Just a stack really.
"write test for Frame.isDone()" is completed in TestFrame.java (version 0.0.0002)
"add Frame.isDone()" is completed in Frame.java (version 0.0.0002)
With these two quick changes, the next task "get testFourThrowsNoMark() to work" is verified to be completed by running JUnit. Now I get only 1 Failure: testTwoStrikes().

[This message has been edited by JUNILU LACAR (edited March 28, 2001).]
 
Don't touch me. And dont' touch this tiny ad:
Smokeless wood heat with a rocket mass heater
https://woodheat.net
reply
    Bookmark Topic Watch Topic
  • New Topic