pharo-users@lists.pharo.org

Any question about pharo is welcome

View all threads

[exercism bowling challenge] Is there a better way to do this

RW
r.wobben@home.nl
Mon, Dec 21, 2020 10:05 AM

Hello,

To come back at this one.

I made a class Bowling and a class Frame which contains two instance variables named firstThrow and secondThrow.

but I still wonder how do I know which one to fill in.

I can do ` (firstThrow nil?) `  but that feels not right.


Is there a more idiomatic solution for this ?

Roelof

Hello, To come back at this one. I made a class Bowling and a class Frame which contains two instance variables named firstThrow and secondThrow. but I still wonder how do I know which one to fill in. I can do \` (firstThrow nil?) \` but that feels not right. \ Is there a more idiomatic solution for this ? Roelof
RW
Russ Whaley
Mon, Dec 21, 2020 12:56 PM

I had a lot of fun putting my bowling game together back when you first
brought this up.

I have a Bowling (Game) class, a Frame class, aThrow class and a Score(ing)
class.  I made the Throws a class that I put in an OrderedCollection - but
instVars would work as well... the rest of your game should be oblivious to
your 'underpinnings' anyway.

The Bowling class controls the game and cycles through 10 frames, which I
create then house in an OrderedCollection at the start of the game.  Frame
has a subclass called Frame10 that is instantiated for the 10th frame of
the game.

Bowling processes each Frame, starting at 1, from the collection, and does
a runFrame (or whatever action name you want).  A Frame consists of throws,
1, 2, or 3 - depending on the frame and the result of the first throw.

A throw returns a random number of 'knocked down pins' between 0 (gutter
ball) and the number of pins remaining in the frame (results of a previous
throw).  So my Frame always knows -pinsCleared and -pinsRemaining - but it
asks the Throws what they cleared to calculate that info.

After each throw, the Score class runs through the frames to see if
anything additional can be scored...

  • i.e. the first throw on frame3 would be added to the 'spare' of frame2
    plus the score of frame 1 - to get the frame 2 score.  But frame3 cannot
    yet be scored.

Then when the frame does the 2nd throw, (as long as it's not a spare), the
3rd frame can also be scored.  Scoring was certainly the most
challenging/fun part for me as there are a number of variables to
consider.  Scoring would start at frame1 each time it is called and find
the first frame without a score (the running score is stored on the frame),
a frame can be asked what its score is, but it's kindof irrelevant as this
is not shown on a scorecard, and it can't be done it a vacuum... i.e. if I
have a strike or a spare in a frame, what is the frame score?  You have to
get the next two throws to figure that out... so I let the Score class do
that and simply update the Frame score instVar once the calculation has
been made.

As soon as Scoreing hits a frame it cannot score, it stops and the Frame
goes on to the next throw, or if it has hit maxThrows for the frame
(frame1-9, maxThrows=2, frame10 maxThrows=3)... some folks call the third
ball a bonus ball, I decided not to.

So, when you get to frame10, we are allowed to have 3 throws if certain
conditions are met -

  • your first throw is a strike, or
  • your second throw is a spare

... and of course scoring is a bit different for frame10, as well.

I created a simulator with a traceCr output to show my frames and throws.

Frame 1, 8 pins
Throw 1, 6 pins
Throw 2, 2 pins
Score: 8
Frame 2, 9 pins
Throw 1, 0 pins
Throw 2, 9 pins
Score: 17
Frame 3, 7 pins
Throw 1, 5 pins
Throw 2, 2 pins
Score: 24
Frame 4, 9 pins
Throw 1, 8 pins
Throw 2, 1 pins
Score: 33
Frame 5, 5 pins
Throw 1, 3 pins
Throw 2, 2 pins
Score: 38
Frame 6, 6 pins
Throw 1, 5 pins
Throw 2, 1 pins
Score: 44
Frame 7, 9 pins
Throw 1, 4 pins
Throw 2, 5 pins
Score: 53
Frame 8, 8 pins
Throw 1, 1 pins
Throw 2, 7 pins
Score: 61
Frame 9, 3 pins
Throw 1, 3 pins
Throw 2, 0 pins
Score: 64
Frame 10, 15 pins
Throw 1, 4 pins
Throw 2, 6 pins
Throw 3, 5 pins
Score: 79
Final score: 79
Strikes: 0
Spares:  1
Gutters:  2

Timer >> 24.933ms Bowling-runTestGame

I then tried my hand at a GUI - creating a BowlingPresenter, a
ScorecardPresenter, a FramePresenter and a Frame10Presenter, resulting in...

[image: image.png]

I plan to add multi-player (or at least playing against the computer), but
until I figure out how to make the throws be specific about which physical
pins are remaining and the percentages of the next throw clearing them
(much more complex that pure random) - there's not much else I can really
do.  So if anyone has direction for me on this, I'd love to try and tackle
it sometime.

Let me know if you have any questions.

Thanks,
Russ

On Mon, Dec 21, 2020 at 5:05 AM r.wobben--- via Pharo-users <
pharo-users@lists.pharo.org> wrote:

Hello,

To come back at this one.

I made a class Bowling and a class Frame which contains two instance
variables named firstThrow and secondThrow.

but I still wonder how do I know which one to fill in.

I can do (firstThrow nil?) but that feels not right.

Is there a more idiomatic solution for this ?

Roelof

--
Russ Whaley
whaley.russ@gmail.com

I had a lot of fun putting my bowling game together back when you first brought this up. I have a Bowling (Game) class, a Frame class, aThrow class and a Score(ing) class. I made the Throws a class that I put in an OrderedCollection - but instVars would work as well... the rest of your game should be oblivious to your 'underpinnings' anyway. The Bowling class controls the game and cycles through 10 frames, which I create then house in an OrderedCollection at the start of the game. Frame has a subclass called Frame10 that is instantiated for the 10th frame of the game. Bowling processes each Frame, starting at 1, from the collection, and does a runFrame (or whatever action name you want). A Frame consists of throws, 1, 2, or 3 - depending on the frame and the result of the first throw. A throw returns a random number of 'knocked down pins' between 0 (gutter ball) and the number of pins remaining in the frame (results of a previous throw). So my Frame always knows -pinsCleared and -pinsRemaining - but it asks the Throws what they cleared to calculate that info. After each throw, the Score class runs through the frames to see if anything additional can be scored... - i.e. the first throw on frame3 would be added to the 'spare' of frame2 plus the score of frame 1 - to get the frame 2 score. But frame3 cannot yet be scored. Then when the frame does the 2nd throw, (as long as it's not a spare), the 3rd frame can also be scored. Scoring was certainly the most challenging/fun part for me as there are a number of variables to consider. Scoring would start at frame1 each time it is called and find the first frame without a score (the running score is stored on the frame), a frame can be asked what its score is, but it's kindof irrelevant as this is not shown on a scorecard, and it can't be done it a vacuum... i.e. if I have a strike or a spare in a frame, what is the frame score? You have to get the next two throws to figure that out... so I let the Score class do that and simply update the Frame score instVar once the calculation has been made. As soon as Scoreing hits a frame it cannot score, it stops and the Frame goes on to the next throw, or if it has hit maxThrows for the frame (frame1-9, maxThrows=2, frame10 maxThrows=3)... some folks call the third ball a bonus ball, I decided not to. So, when you get to frame10, we are allowed to have 3 throws if certain conditions are met - - your first throw is a strike, or - your second throw is a spare ... and of course scoring is a bit different for frame10, as well. I created a simulator with a traceCr output to show my frames and throws. Frame 1, 8 pins Throw 1, 6 pins Throw 2, 2 pins Score: 8 Frame 2, 9 pins Throw 1, 0 pins Throw 2, 9 pins Score: 17 Frame 3, 7 pins Throw 1, 5 pins Throw 2, 2 pins Score: 24 Frame 4, 9 pins Throw 1, 8 pins Throw 2, 1 pins Score: 33 Frame 5, 5 pins Throw 1, 3 pins Throw 2, 2 pins Score: 38 Frame 6, 6 pins Throw 1, 5 pins Throw 2, 1 pins Score: 44 Frame 7, 9 pins Throw 1, 4 pins Throw 2, 5 pins Score: 53 Frame 8, 8 pins Throw 1, 1 pins Throw 2, 7 pins Score: 61 Frame 9, 3 pins Throw 1, 3 pins Throw 2, 0 pins Score: 64 Frame 10, 15 pins Throw 1, 4 pins Throw 2, 6 pins Throw 3, 5 pins Score: 79 Final score: 79 Strikes: 0 Spares: 1 Gutters: 2 Timer >> 24.933ms Bowling-runTestGame I then tried my hand at a GUI - creating a BowlingPresenter, a ScorecardPresenter, a FramePresenter and a Frame10Presenter, resulting in... [image: image.png] I plan to add multi-player (or at least playing against the computer), but until I figure out how to make the throws be specific about which physical pins are remaining and the percentages of the next throw clearing them (much more complex that pure random) - there's not much else I can really do. So if anyone has direction for me on this, I'd love to try and tackle it sometime. Let me know if you have any questions. Thanks, Russ On Mon, Dec 21, 2020 at 5:05 AM r.wobben--- via Pharo-users < pharo-users@lists.pharo.org> wrote: > Hello, > > > To come back at this one. > > I made a class Bowling and a class Frame which contains two instance > variables named firstThrow and secondThrow. > > > but I still wonder how do I know which one to fill in. > > I can do ` (firstThrow nil?) ` but that feels not right. > > > Is there a more idiomatic solution for this ? > > > Roelof > > > -- Russ Whaley whaley.russ@gmail.com
RW
Roelof Wobben
Mon, Dec 21, 2020 3:42 PM

Op 21-12-2020 om 13:56 schreef Russ Whaley:

I had a lot of fun putting my bowling game together back when you
first brought this up.

I have a Bowling (Game) class, a Frame class, aThrow class and a
Score(ing) class.  I made the Throws a class that I put in an
OrderedCollection - but instVars would work as well... the rest of
your game should be oblivious to your 'underpinnings' anyway.

The Bowling class controls the game and cycles through 10 frames,
which I create then house in an OrderedCollection at the start of the
game.  Frame has a subclass called Frame10 that is instantiated for
the 10th frame of the game.

Bowling processes each Frame, starting at 1, from the collection, and
does a runFrame (or whatever action name you want).  A Frame consists
of throws, 1, 2, or 3 - depending on the frame and the result of the
first throw.

That is also the way I can work.
But I do not see how I can convert the given collection to frames

Roelof

Op 21-12-2020 om 13:56 schreef Russ Whaley: > I had a lot of fun putting my bowling game together back when you > first brought this up. > > I have a Bowling (Game) class, a Frame class, aThrow class and a > Score(ing) class.  I made the Throws a class that I put in an > OrderedCollection - but instVars would work as well... the rest of > your game should be oblivious to your 'underpinnings' anyway. > > The Bowling class controls the game and cycles through 10 frames, > which I create then house in an OrderedCollection at the start of the > game.  Frame has a subclass called Frame10 that is instantiated for > the 10th frame of the game. > > Bowling processes each Frame, starting at 1, from the collection, and > does a runFrame (or whatever action name you want).  A Frame consists > of throws, 1, 2, or 3 - depending on the frame and the result of the > first throw. > That is also the way I can work. But I do not see how I can convert the given collection to frames Roelof
RO
Richard O'Keefe
Wed, Dec 23, 2020 12:29 AM

Yes, there is a better way to do it.
I found this particular problem at the Programming Praxis
web site, which took it from
http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata

This is a superb example of how an informal specification
and a set of test cases can diverge.

The specification says to score a bowling game according
to a particular set of rules.  Here is exactly the algorithm
called for, written in the functional programming language
Haskell:

score (10:b:c:xs) =        b+10+c+(if null xs then 0 else score (b:c:xs))
score (a:b:c:xs) | a+b = 10 = 10+c+(if null xs then 0 else score (c:xs))
score (a:b:xs) =              a+b+(if null xs then 0 else score xs)

That's it.  Explanation, (10:b:c:xs) means "a sequence starting
with 10, followed by something (call it b), followed by something
(call it c), followed by the rest of the sequence (call that xs).
This is actually strictly statically typed as well.
And it shows the while the data are provided from first to
last, the score is most easily computed from last to first.

Let's remind ourselves of Kent Beck's four rules of good code:

  1. Runs all the tests
  2. Contains no duplication
  3. Expresses all the important design intentions
  4. Minimizes entities (e.g. classes and methods).

Hence the Extreme Programming slogan
"You Ain't Gonna Need It."

Now we come to the divergence between the specification
(which calls for #roll: and #score methods)
and the tests (which call for a #scoreAfterRolling: method
and then in test 15 calls for #scoreRolling:after:).

There are two kind of test case for this exercise.

  • check that the result is correct in a good case
  • check that the right error is raised given bad data.
    Now the second kind go far beyond the informal
    specification and require not only that certain errors
    be detected but when they are detected.

And this is something you have to bear in mind when
writing down your test cases:  you have to prevent ideas
you may have about how the code might work creeping into
your test cases and forcing the code to work that way.
Most of my code, and most of the trouble I had writing the
code, was about these "does it report the right error at
the right time and oh yeah I made up these 'right' definitions
with no warrant in the customer's requirements" test cases.

I mean, seriously, NO methods in common between the specifications
and tests?

Returning to Kent Beck's maxims.  How many classes do we need?
The test code calls for ONE class, "Bowling".
There are no tests for any Frame pr Roll classes,
and no need for any.
How many methods does it need?
The test code checks #scoreAfterRolling and #scoreRolling:after:
There are not tests for any others.
What state should the instances of the class hold?
There is hint
You will need to store some game state.
but that applies to the #roll:/#score interface,
NOT to the interface that's actually tested.

Use as many classes and methods as you NEED,
but remember that you need a lot fewer than you think,

On Mon, 21 Dec 2020 at 23:05, r.wobben--- via Pharo-users <
pharo-users@lists.pharo.org> wrote:

Hello,

To come back at this one.

I made a class Bowling and a class Frame which contains two instance
variables named firstThrow and secondThrow.

but I still wonder how do I know which one to fill in.

I can do (firstThrow nil?) but that feels not right.

Is there a more idiomatic solution for this ?

Roelof

Yes, there is a better way to do it. I found this particular problem at the Programming Praxis web site, which took it from http://butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata This is a superb example of how an informal specification and a set of test cases can diverge. The specification says to score a bowling game according to a particular set of rules. Here is exactly the algorithm called for, written in the functional programming language Haskell: score (10:b:c:xs) = b+10+c+(if null xs then 0 else score (b:c:xs)) score (a:b:c:xs) | a+b = 10 = 10+c+(if null xs then 0 else score (c:xs)) score (a:b:xs) = a+b+(if null xs then 0 else score xs) That's *it*. Explanation, (10:b:c:xs) means "a sequence starting with 10, followed by something (call it b), followed by something (call it c), followed by the rest of the sequence (call that xs). This is actually strictly statically typed as well. And it shows the while the data are *provided* from first to last, the score is most easily *computed* from last to first. Let's remind ourselves of Kent Beck's four rules of good code: 1. Runs all the tests 2. Contains no duplication 3. Expresses all the important design intentions 4. Minimizes entities (e.g. classes and methods). Hence the Extreme Programming slogan "You Ain't Gonna Need It." Now we come to the divergence between the specification (which calls for #roll: and #score methods) and the tests (which call for a #scoreAfterRolling: method and then in test 15 calls for #scoreRolling:after:). There are two kind of test case for this exercise. + check that the result is correct in a good case - check that the right error is raised given bad data. Now the second kind go far beyond the informal specification and require not only that certain errors be detected but *when* they are detected. And this is something you have to bear in mind when writing down your test cases: you have to prevent ideas you may have about *how* the code might work creeping into your test cases and forcing the code to work that way. Most of my code, and most of the trouble I had writing the code, was about these "does it report the right error at the right time and oh yeah I made up these 'right' definitions with no warrant in the customer's requirements" test cases. I mean, seriously, NO methods in common between the specifications and tests? Returning to Kent Beck's maxims. How many classes do we need? The test code calls for ONE class, "Bowling". There are no tests for any Frame pr Roll classes, and no need for any. How many methods does it need? The test code checks #scoreAfterRolling and #scoreRolling:after: There are not tests for any others. What state should the instances of the class hold? There is hint You will need to store some game state. but that applies to the #roll:/#score interface, NOT to the interface that's actually tested. Use as many classes and methods as you NEED, but remember that you need a lot fewer than you think, On Mon, 21 Dec 2020 at 23:05, r.wobben--- via Pharo-users < pharo-users@lists.pharo.org> wrote: > Hello, > > > To come back at this one. > > I made a class Bowling and a class Frame which contains two instance > variables named firstThrow and secondThrow. > > > but I still wonder how do I know which one to fill in. > > I can do ` (firstThrow nil?) ` but that feels not right. > > > Is there a more idiomatic solution for this ? > > > Roelof > > >
RW
Roelof Wobben
Sat, Jan 2, 2021 10:43 AM