f



Test Driven Development Sample

Adventures in C#: The Bowling Game
Ron Jeffries 
11/17/2003 

  When I demonstrate Test-Driven Development using the Bowling Game
  example, I begin by describing the problem and inviting the attendees to
  do a little up front design about what objcts we may need. Then I take a
  very simple approach that produces a rather simple single-class solution,
  with none of the complexity we anticipated. I've been wondering how to
  drive the development to cause the creation of some of the classes that
  are anticipated, supposing that we might have some actual need for them.
  Here's an example of doing TDD with a bit bigger "design" in mind.

  http://www.xprogramming.com/xpmag/acsBowling.htm

Ron Jeffries
www.XProgramming.com
Just because we learned something new today doesn't mean we were
frickin' idiots yesterday.  -- Chris Morris, possibly paraphrasing someone.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/17/2003 5:24:34 PM
comp.object 3218 articles. 1 followers. Post Follow

242 Replies
1228 Views

Similar Articles

[PageSpeed] 6

Ron Jeffries <ronjeffries@REMOVEacm.org> might (or might not) have
written this on (or about)  Mon, 17 Nov 2003 12:24:34 -0500, :

>Adventures in C#: The Bowling Game
>Ron Jeffries 
>11/17/2003 
>
>  When I demonstrate Test-Driven Development using the Bowling Game
>  example, I begin by describing the problem and inviting the attendees to
>  do a little up front design about what objcts we may need. Then I take a
>  very simple approach that produces a rather simple single-class solution,
>  with none of the complexity we anticipated. I've been wondering how to
>  drive the development to cause the creation of some of the classes that
>  are anticipated, supposing that we might have some actual need for them.
>  Here's an example of doing TDD with a bit bigger "design" in mind.
>
>  http://www.xprogramming.com/xpmag/acsBowling.htm

I can't say that I like it as much as the traditional solution, but it
is pretty sweet.  It's the first reasonable version with frames that
I've seen.  

One complaint I have is that the tests are doing the parsing of
whether a frame is Open, Spare, Strike, or Bonus.  It seems to me that
this should be in the BowlingGame class itself.  The tests shouldn't
know about whether a throw is a strike, spare, open, or bonus.  The
tests should just know which balls where thrown IMHO.




Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
11/17/2003 10:31:05 PM
Ron Jeffries wrote:

> Adventures in C#: The Bowling Game
> Ron Jeffries 
> 11/17/2003 
> 
>   When I demonstrate Test-Driven Development using the Bowling Game
>   example, I begin by describing the problem and inviting the attendees to
>   do a little up front design about what objcts we may need. Then I take a
>   very simple approach that produces a rather simple single-class solution,
>   with none of the complexity we anticipated. I've been wondering how to
>   drive the development to cause the creation of some of the classes that
>   are anticipated, supposing that we might have some actual need for them.
>   Here's an example of doing TDD with a bit bigger "design" in mind.
> 
>   http://www.xprogramming.com/xpmag/acsBowling.htm
> 
> Ron Jeffries
> www.XProgramming.com
> Just because we learned something new today doesn't mean we were
> frickin' idiots yesterday.  -- Chris Morris, possibly paraphrasing someone.
> 

Yet Another Pet Example. Actually, it is a trivial folding in functional 
programming, one just has to read the requirements and transform that 
into a data structure, that is unless you want to write it as a single 
block of code.

The interesting thing missing from the story is if this is a scoring 
calculator module to be integrated into another picture what is the 
reason behind the interface for this module.

The other interested thing that is missing is how much time and how much 
lines of code it took you to come up with the TDD solution, in other 
words you can praise TDD all you want, you have to come with some 
objective engineering measures.

----

Now forget about bowling frames, take a minimally challenging example.

I added one just for your convenience, it's up to you if you take it or not.

*Longest increasing subsequence.*

Let there be a sequence of real, a subsequence is a sequence obtained
from the original by removing any number of positions while retaining
the ordering for the remaining positions. Find the longest increasing 
subsequence.

More formally, givent he input an array of reals (x[0] ..
x[x.length-1]), give the output an array of integer indices: (index[0]
.... index[index.length-1]

so that for any k 0<=k< index.length - 1 : 0<=index[k]<x.length
-- index[k] is a valid index in the input array)

and for any 0<=j<k<index.length, we have:

index[j]<index[k]
-- we don't change the ordering

x[index[j]] <= x[index[k]]
-- the corresponding elements are in ascending order
-----

Let's see how TDD fares on this one.


Costin



0
c_cozianu (217)
11/18/2003 1:54:22 AM
On Mon, 17 Nov 2003 16:31:05 -0600, "Uncle Bob (Robert C. Martin)"
<u.n.c.l.e.b.o.b@objectmentor.com> wrote:

>I can't say that I like it as much as the traditional solution, but it
>is pretty sweet.  It's the first reasonable version with frames that
>I've seen.  

Yes. I'm going to do my standard procedural one next and compare them. But the
objects are at least semi-sweet.
>
>One complaint I have is that the tests are doing the parsing of
>whether a frame is Open, Spare, Strike, or Bonus.  It seems to me that
>this should be in the BowlingGame class itself.  The tests shouldn't
>know about whether a throw is a strike, spare, open, or bonus.  The
>tests should just know which balls where thrown IMHO.

I think of it as the interface to BowlingGame is coming from the playing of the
game itself. The players know whether they bowled a strike, spare, open frame,
and so on, and they say so. 

It would certainly be possible to do the parsing readily enough, but my
intuition said (and I bet you'll agree) that by the time we parse, we might as
well just add up the score rather than create all those cute objects.

It's definitely a different breakout of responsibility, and makes the classes
that use the Game know more. But that was the point, really, try it another way
and see what one gets.

Thanks for the feedback!

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 2:31:28 AM
On Mon, 17 Nov 2003 17:54:22 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>*Longest increasing subsequence.*
>
> <description snipped>
>-----
>
>Let's see how TDD fares on this one.

Might be interesting, I'll think about working it. If I understand your
notation, this problem is usually named "Longest increasing SCATTERED
subsequence", is that what you have in mind?

An elementary algorithm is probably fairly simply generated using TDD,
generating all subsequences and comparing them. That will of course have huge
run time and will be seriously sub-optimal.

The problem, if I recall from my college days long ago, is best solved with a
recursive algorithm with memory as to the longest sequence as yet found, over an
increasing sequence length, or something weird like that.

It's the sort of math problem where it takes a kind of creative math insight to
do a really good job, and as such I'd expect it to be unlikely that a TDD
approach would find its way onto the right approach. 

So, are there problems that are not amenable to TDD? I think not, because I'd
still use TDD to confine the code to correctness even if I was working on a
specific algorithm. Will TDD, however, just converge on an optimal solution? I
think not. It will converge on a working solutionn but there's no guarantee of
optimality. I know of no approach that guarantees that, but would love to hear
about one.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 2:46:38 AM
On Mon, 17 Nov 2003 17:54:22 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>Let's see how TDD fares on this one.

While we're at it, please show us how your approach, whatever it is, fares on
this one.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 2:47:24 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...
> Ron Jeffries wrote:
>

SNIP

> Now forget about bowling frames, take a minimally challenging example.
>
> I added one just for your convenience, it's up to you if you take it or
not.
>
> *Longest increasing subsequence.*
>
> Let there be a sequence of real, a subsequence is a sequence obtained
> from the original by removing any number of positions while retaining
> the ordering for the remaining positions. Find the longest increasing
> subsequence.
>
> More formally, givent he input an array of reals (x[0] ..
> x[x.length-1]), give the output an array of integer indices: (index[0]
> ... index[index.length-1]
>
> so that for any k 0<=k< index.length - 1 : 0<=index[k]<x.length
> -- index[k] is a valid index in the input array)
>
> and for any 0<=j<k<index.length, we have:
>
> index[j]<index[k]
> -- we don't change the ordering
>
> x[index[j]] <= x[index[k]]
> -- the corresponding elements are in ascending order
> -----
>
> Let's see how TDD fares on this one.

I'll bite.  Paraphrasing your description, I assume you mean:

  f([8,9,1,2,3]) -> [2,3,4]

Assuming a 0 indexed integer array.

Ok...what is the result for this:

  f([8,8,8,8]) -> ???

or this:

  f([1,2,3,1,2,3,1,2,3]) -> ???

Need all the stories to build the tests...

The tests I would assume would build the function are:

  assertEquals([], f([]));

  assertEquals([0], f([1]));

  assertEquals([1,2], f([1,1,2]));

  assertEquals([2,3,4], f([1,2,1,2,3]));

BUT, we need the solutions for the above two ambiguities to finish the
construction.



John


0
jelrick (16)
11/18/2003 2:52:34 AM
"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
news:8h1jrvo142kf4d9sseg5hjbvvhm15khs7t@4ax.com...
> On Mon, 17 Nov 2003 17:54:22 -0800, Costin Cozianu <c_cozianu@hotmail.com>
> wrote:
>
> >*Longest increasing subsequence.*
> >
> > <description snipped>
> >-----
> >
> >Let's see how TDD fares on this one.
>
> Might be interesting, I'll think about working it. If I understand your
> notation, this problem is usually named "Longest increasing SCATTERED
> subsequence", is that what you have in mind?
>
> An elementary algorithm is probably fairly simply generated using TDD,
> generating all subsequences and comparing them. That will of course have
huge
> run time and will be seriously sub-optimal.

IF I'm reading his description correctly (which I may not be), the problem
can be solved with a for loop and five local variables.

And...yes...I'd certainly want to test this particular algorithm<g>


John


0
jelrick (16)
11/18/2003 2:57:59 AM
Ron Jeffries wrote:

> On Mon, 17 Nov 2003 17:54:22 -0800, Costin Cozianu <c_cozianu@hotmail.com>
> wrote:
>
> >*Longest increasing subsequence.*
> >
> > <description snipped>
> >-----
> >
> >Let's see how TDD fares on this one.
>
> Might be interesting, I'll think about working it. If I understand your
> notation, this problem is usually named "Longest increasing SCATTERED
> subsequence", is that what you have in mind?
>
> An elementary algorithm is probably fairly simply generated using TDD,
> generating all subsequences and comparing them. That will of course have
huge
> run time and will be seriously sub-optimal.

Jeeze, Ron - don't you think, if TDD's all it's cracked up to be - can't you
knock off the Traveling Salesman Problem while you are at it?

--
  G. Spencer Brown


0
phlip_cpp (3852)
11/18/2003 4:07:51 AM
John Elrick wrote:

> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...
> 
>>Ron Jeffries wrote:
>>
> 
> 
> SNIP
> 
> 
>>Now forget about bowling frames, take a minimally challenging example.
>>
>>I added one just for your convenience, it's up to you if you take it or
> 
> not.
> 
>>*Longest increasing subsequence.*
>>
>>Let there be a sequence of real, a subsequence is a sequence obtained
>>from the original by removing any number of positions while retaining
>>the ordering for the remaining positions. Find the longest increasing
>>subsequence.
>>
>>More formally, givent he input an array of reals (x[0] ..
>>x[x.length-1]), give the output an array of integer indices: (index[0]
>>... index[index.length-1]
>>
>>so that for any k 0<=k< index.length - 1 : 0<=index[k]<x.length
>>-- index[k] is a valid index in the input array)
>>
>>and for any 0<=j<k<index.length, we have:
>>
>>index[j]<index[k]
>>-- we don't change the ordering
>>
>>x[index[j]] <= x[index[k]]
>>-- the corresponding elements are in ascending order
>>-----
>>
>>Let's see how TDD fares on this one.
> 
> 
> I'll bite.  Paraphrasing your description, I assume you mean:
> 
>   f([8,9,1,2,3]) -> [2,3,4]
> 
> Assuming a 0 indexed integer array.
> 
> Ok...what is the result for this:
> 
>   f([8,8,8,8]) -> ???
> 

f([8,8,8,8]) -> [0,1,2,3]

> or this:
> 
>   f([1,2,3,1,2,3,1,2,3]) -> ?

f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])

> 
> Need all the stories to build the tests...
> 

You see, that's the problem with your enthusiastic XP/TDD approach. I 
gave you enough specification, and now you've mudied the waters with 
your "stories" :)

The specification stated quite clearly that x[index[i]]<=x[index[k]]

The specification also did not state that the longest increasing 
subsequence is unique, so you shouldn't assume that. Any subsequence 
that has maximal length will do.

Therefore your assertEquals() below are restricting the space of 
solutions. They are not a faithful encoding of specification.

> The tests I would assume would build the function are:
> 
>   assertEquals([], f([]));
> 
>   assertEquals([0], f([1]));
> 
>   assertEquals([1,2], f([1,1,2]));
> 
f([1,1,2]) -> [0,1,2]

>   assertEquals([2,3,4], f([1,2,1,2,3]));

f( [1,2,1,2,3]) -> [0,1,3,4] // also [0,2,3,4]
> 
> BUT, we need the solutions for the above two ambiguities to finish the
> construction.
> 
> 

Yes, if you read more carefully the spec you can disambiguate :)

Do they even bother to write Software Requirements Specification these 
days in an XP shop ?


Actually if I am to buy into what Uncle Bob claimed yesteryear, I just 
couldn't let this piece of software be subcontracted to a XP shop, 
because he'd refuse to sign on a specification, he'd only sign off 
specific executable tests. And this is, it seems to me just not good 
enough, to dillute a specification as a set of tests.

Tests can be a proof that you failed, but never a proof that you 
succeeded, so realistically speaking, should we sign a contract over a 
spec, or should we sign it over a bunch of  "assertEquals" ?

> 
> John
> 
> 

Costin

0
c_cozianu (217)
11/18/2003 5:27:31 AM
Ron Jeffries wrote:

> On Mon, 17 Nov 2003 17:54:22 -0800, Costin Cozianu <c_cozianu@hotmail.com>
> wrote:
> 
> 
>>Let's see how TDD fares on this one.
> 
> 
> While we're at it, please show us how your approach, whatever it is, fares on
> this one.
> 

Actually, I think I've said more than once on this forum that TDD is a 
surrogate for the real thing, which is "proof driven development".

Indeed, "test driven" in this example can offer little to no insight 
into the nature of the solution, while trying to establish a proof and 
having knowledge of established proof techniques both for loops and/or 
recursive functions, will offer instantaneous gratification :)

Just try to establish an invariant (for imperative loop) or a relation 
when going from a solution of the sequence x[0]..x[k] to a solution of 
x[0]..x[k+1]


Costin

0
c_cozianu (217)
11/18/2003 6:03:40 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...

> More formally, givent he input an array of reals (x[0] ..
> x[x.length-1]), give the output an array of integer indices: (index[0]
> ... index[index.length-1]

I'm one of those who uses TDD but I dont expect it to work for everything. However I
have the following observations to make ...

* This sort of algorithm-centric problem is IME very atypical of "day-to-day" software
development unless one is working in a very specialised field such as signal processing.

* Problems such as this almost always have well analysed boiler-plate solutions that I can
just nick and use without having to prove or test it myself.

So even accepting that TDD is all but useless for such a problem I would still conclude
"big deal".

Your other worries about applicability of TDD to concurrent systems (I think this was you)
are much more valid and relevent IMO.

Paul C.





0
11/18/2003 9:33:58 AM
On Tue, 18 Nov 2003 02:57:59 GMT, "John Elrick" <jelrick@adelphia.net> wrote:

>IF I'm reading his description correctly (which I may not be), the problem
>can be solved with a for loop and five local variables.

It could be, though it seems to me that we have to save sequences, or do some
reasoning about the structure that hasn't come to me yet. I'd love to see your
idea worked out.
>
>And...yes...I'd certainly want to test this particular algorithm<g>

Oh yes!

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 12:00:28 PM
On Mon, 17 Nov 2003 22:03:40 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>Actually, I think I've said more than once on this forum that TDD is a 
>surrogate for the real thing, which is "proof driven development".

It might be a surrogate for /another/ thing which is "proof driven development".
If all programs had to be proven, there would be a lot fewer programmers in the
world, because most of us aren't up to it.

The longest subsequence problem has a moderately well-known history, and there
are solutions out there -- even some on the Web. 
>
>Indeed, "test driven" in this example can offer little to no insight 
>into the nature of the solution, while trying to establish a proof and 
>having knowledge of established proof techniques both for loops and/or 
>recursive functions, will offer instantaneous gratification :)

This might be some new definition of the word "instantaneous" that I wasn't
previously familiar with. Please take us through this experience, preferably
without referencing existing solutions.
>
>Just try to establish an invariant (for imperative loop) or a relation 
>when going from a solution of the sequence x[0]..x[k] to a solution of 
>x[0]..x[k+1]

Yes, that's what the first thing Google finds recommends. So teach us. I can
help people learn how to do TDD -- let's suppose that's the limit of my
capability. Take us beyond, Costin, show us how to do this thing that you
recommend.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 12:04:48 PM
On Mon, 17 Nov 2003 21:27:31 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>Yes, if you read more carefully the spec you can disambiguate :)
>
>Do they even bother to write Software Requirements Specification these 
>days in an XP shop ?

Often they do not. As you have just demonstrated perfectly, talking with the
customer works ever so much better. I shall elaborate:

You presented, as far as I can tell, a perfectly unambiguous description of the
problem. It was without flaw (although the limitations of formatting might make
one wish for better technology).

Yet although the spec was unambiguous, at least one reader who was serious
enough to think about it did not successfully disambiguate. A few words from you
were able to set him straight.

I think there's a deep lesson in that, and it is not "Everyone should learn to
write and read unambiguously".

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 12:08:51 PM
On Tue, 18 Nov 2003 09:33:58 -0000, "Paul Campbell"
<p.au.l.ca.mp.b.ell@ob.jectvi.sion.c.o.u.k> wrote:

>Your other worries about applicability of TDD to concurrent systems (I think this was you)
>are much more valid and relevent IMO.

Indeed. I have similar concerns. TDD can help me write simple code, which makes
it easier to get correctly-working concurrent systems. But simplicity is often
not enough when it comes to concurrency. Stronger approaches are needed.

And, by the way, that's OK with me. XP and TDD aren't about knowing only one
technique. They offer techniques that are good starting ones, because of their
simplicity and their wide applicability. It's quite valuable -- and in some
cases quite necessary -- to have much more powerful tools in one's bag of
tricks.  

Even proof, which seems to be used quite infrequently in practice. Maybe it
should be used more. Some good practical articles, books, and Web postings might
help, if someone was a strong supporter of the idea.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 12:12:22 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> might (or might not) have
written this on (or about)  Tue, 18 Nov 2003 07:12:22 -0500, :

>On Tue, 18 Nov 2003 09:33:58 -0000, "Paul Campbell"
><p.au.l.ca.mp.b.ell@ob.jectvi.sion.c.o.u.k> wrote:
>
>>Your other worries about applicability of TDD to concurrent systems (I think this was you)
>>are much more valid and relevent IMO.
>
>Indeed. I have similar concerns. TDD can help me write simple code, which makes
>it easier to get correctly-working concurrent systems. But simplicity is often
>not enough when it comes to concurrency. Stronger approaches are needed.

We've been having some interesting concurrency issues with FitNesse
lately.  TDD certainly hasn't prevented them; but we've been able to
build tests cases that demonstrate them.  So far we aren't hurting.  

The concurrency issues in FitNesse are about to get more interesting.
There are some features we want to implement that will cause some
rather tricky thread juggling.  Should be fun.
>
>And, by the way, that's OK with me. XP and TDD aren't about knowing only one
>technique. They offer techniques that are good starting ones, because of their
>simplicity and their wide applicability. It's quite valuable -- and in some
>cases quite necessary -- to have much more powerful tools in one's bag of
>tricks.  

You said a mouthful there!  Yeah, XP isn't about throwing all your old
tools away, it's about adding some new ones to your kit, and changing
the weighting on your tool selection decision matrix.  

>Even proof, which seems to be used quite infrequently in practice. Maybe it
>should be used more. 

I can't say I use *formal* proofs; but when diagnosing a concurrency
problem, the steps of logical reasoning are very much like a proof.
Interestingly enough those logical steps are based upon a set of
assumptions that don't always turn out to be true.  So experimentation
is also an important part.


Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
11/18/2003 7:49:14 PM
Robert C. Martin wrote in message

> I can't say I use *formal* proofs; but when diagnosing a concurrency
> problem, the steps of logical reasoning are very much like a proof.
> Interestingly enough those logical steps are based upon a set of
> assumptions that don't always turn out to be true.

And whose fault is that?

>  So experimentation
> is also an important part.

Elliott
-- 
      Substituting Refactoring For Analysis Modelling and Review
            When Facing Major High Level Design Problems
                 Is Like Drawing a Shade When Totally Dark
-- 
                      *~* Theory Leads, Practice Verifies *~*
     *~*  Global Plans + IID (Iterative & Incremental Development) *~*




0
universe2 (613)
11/18/2003 9:28:47 PM
"Universe" <universe@covad.net> wrote in message
news:e99c3$3fba8f10$3f47e403$23197@msgid.meganewsservers.com...
>
> Robert C. Martin wrote in message
>
> > I can't say I use *formal* proofs; but when diagnosing a concurrency
> > problem, the steps of logical reasoning are very much like a proof.
> > Interestingly enough those logical steps are based upon a set of
> > assumptions that don't always turn out to be true.

> And whose fault is that?

~ For most development questions, issues, matters?
~ For previously tackled kinds of development?  - and most kinds have
been

> >  So experimentation is also an important part.

*When* the above are not true.  And thus for most projects hi-risk
prototyping should be the *lesser* part of development.

You're improperly bowing to spontaneity and thus wasting resources -
time, money, skill, etc - if that  is not the case.

Elliott
-- 
                      *~* Theory Leads, Practice Verifies *~*
     *~*  Global Plans + IID (Iterative & Incremental Development) *~*
-- 
      Substituting Refactoring For Analysis Modelling and Review
            When Facing Major High Level Design Problems
                 Is Like Drawing a Shade When Totally Dark




0
universe2 (613)
11/18/2003 9:45:14 PM
Ron Jeffries wrote:

> On Tue, 18 Nov 2003 02:57:59 GMT, "John Elrick" <jelrick@adelphia.net> wrote:
> 
> 
>>IF I'm reading his description correctly (which I may not be), the problem
>>can be solved with a for loop and five local variables.
> 
> 
> It could be, though it seems to me that we have to save sequences, or do some
> reasoning about the structure that hasn't come to me yet. I'd love to see your
> idea worked out.
> 
>>And...yes...I'd certainly want to test this particular algorithm<g>
> 
> 
> Oh yes!
> 

Before I post a solution, and the mechanism to come up with one, can you 
give me a hand and write the unit tests ??

Costin

0
c_cozianu (217)
11/18/2003 9:53:06 PM
On Tue, 18 Nov 2003 13:53:06 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>Before I post a solution, and the mechanism to come up with one, can you 
>give me a hand and write the unit tests ??

No, sorry, not at this time. I'm not unwilling, but since I don't have a program
that does the job, and I'm not working on one, creating the tests would be a
fairly long and involved hand process. And I suspect that I would get many of
them wrong through manual errors, which might be confusing.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/18/2003 10:48:43 PM
Ron Jeffries wrote:

> On Tue, 18 Nov 2003 13:53:06 -0800, Costin Cozianu <c_cozianu@hotmail.com>
> wrote:
> 
> 
>>Before I post a solution, and the mechanism to come up with one, can you 
>>give me a hand and write the unit tests ??
> 
> 
> No, sorry, not at this time. I'm not unwilling, but since I don't have a program
> that does the job, and I'm not working on one, creating the tests would be a
> fairly long and involved hand process. And I suspect that I would get many of
> them wrong through manual errors, which might be confusing.
> 

So there is a class of problem where, simply put, tests are much harded 
to write if they can be written at all before a solution is written.

0
c_cozianu (217)
11/18/2003 11:09:59 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpcam3$1mlvei$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:
>
> > "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> > news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...

SNIP

> >   f([8,9,1,2,3]) -> [2,3,4]
> >
> > Assuming a 0 indexed integer array.
> >
> > Ok...what is the result for this:
> >
> >   f([8,8,8,8]) -> ???
> >
>
> f([8,8,8,8]) -> [0,1,2,3]

This is not an increasing subsequence.  Based on your description it can be
one of the following:

-> []
-> [0]
-> [3]

But _not_ all of them:

>>Let there be a sequence of real, a subsequence is a sequence obtained
>>from the original by removing any number of positions while retaining
>>the ordering for the remaining positions. Find the longest increasing
>>subsequence.

For this product to be correct you must reword as follows:

"Find the longest increasing or equal subsequence."


>
> > or this:
> >
> >   f([1,2,3,1,2,3,1,2,3]) -> ?
>
> f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])

Nope...this isn't multiple choice and you know it.  Which one is correct?
You are specifying the requirements.  You must make the decision, not me.

>
> >
> > Need all the stories to build the tests...
> >
>
> You see, that's the problem with your enthusiastic XP/TDD approach. I
> gave you enough specification, and now you've mudied the waters with
> your "stories" :)

No you didn't.  You left several ambiguities in the description, as I
already pointed out.  I missed one:

f([9,8,7])

What is the longest incresing subsequence?

Note, that based on your description, it can be any of:

-> []
-> [0]
-> [2]

Your call.


John


0
jelrick (16)
11/18/2003 11:12:01 PM
John Elrick wrote:

> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bpcam3$1mlvei$1@ID-152540.news.uni-berlin.de...
> 
>>John Elrick wrote:
>>
>>
>>>"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
>>>news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...
> 
> 
> SNIP
> 
> 
>>>  f([8,9,1,2,3]) -> [2,3,4]
>>>
>>>Assuming a 0 indexed integer array.
>>>
>>>Ok...what is the result for this:
>>>
>>>  f([8,8,8,8]) -> ???
>>>
>>
>>f([8,8,8,8]) -> [0,1,2,3]
> 
> 
> This is not an increasing subsequence.  Based on your description it can be
> one of the following:
> 
> -> []
> -> [0]
> -> [3]
> 
> But _not_ all of them:
> 

Based on my description the condition was x[index[j]] <= x[index[k]] for 
any j<k in the given range.

> 
>>>Let there be a sequence of real, a subsequence is a sequence obtained
>>
>>>from the original by removing any number of positions while retaining
>>
>>>the ordering for the remaining positions. Find the longest increasing
>>>subsequence.
> 
> 
> For this product to be correct you must reword as follows:
> 
> "Find the longest increasing or equal subsequence."
> 
> 

Ok, you found a better title.

> 
>>>or this:
>>>
>>>  f([1,2,3,1,2,3,1,2,3]) -> ?
>>
>>f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])
> 
> 
> Nope...this isn't multiple choice and you know it.  

Why not ?

> Which one is correct?

Any. Both are correct.

> You are specifying the requirements.  You must make the decision, not me.
> 
> 

I can make the decision to take my business elsewhere :)

>>>Need all the stories to build the tests...
>>>
>>
>>You see, that's the problem with your enthusiastic XP/TDD approach. I
>>gave you enough specification, and now you've mudied the waters with
>>your "stories" :)
> 
> 
> No you didn't.  You left several ambiguities in the description, as I
> already pointed out.  I missed one:
> 
> f([9,8,7])
> 
> What is the longest incresing subsequence?
> 
> Note, that based on your description, it can be any of:
> 
> -> []
> -> [0]
> -> [2]
> 
> Your call.
> 

Any of [0], [1], [2] but never [] because its lenght is 0.
> 
> John
> 
> 

Any subsequence that meets the specified conditions and has maximum 
length will do. I said "longest" because writing in ASCII the formal 
clause for maximality is essentially boring, but I guess "longest" is 
clear enough what is supposed to mean. Maybe is not "increasing" but 
rather non-decreasing" Well, if you replace "<=" with "<" in the 
specification, you have essentially the same algorithm.

I don't know where you practice, but customers don't like to be pissed 
off responding to obvious questions :)

Cheers,
Costin

0
c_cozianu (217)
11/19/2003 12:35:24 AM
"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
news:1p2krvcbb39jhua8mts909rg64aojco1nb@4ax.com...
> On Mon, 17 Nov 2003 21:27:31 -0800, Costin Cozianu <c_cozianu@hotmail.com>
> wrote:
>
> >Yes, if you read more carefully the spec you can disambiguate :)
> >
> >Do they even bother to write Software Requirements Specification these
> >days in an XP shop ?
>
> Often they do not. As you have just demonstrated perfectly, talking with
the
> customer works ever so much better. I shall elaborate:
>
> You presented, as far as I can tell, a perfectly unambiguous description
of the
> problem. It was without flaw (although the limitations of formatting might
make
> one wish for better technology).

Actually, I found the spec ambiguous and contradictory:

"Find the longest increasing subsequence."

"the corresponding elements are in ascending order"

Then the expression:

"x[index[j]] <= x[index[k]]"

Which contradicts the first two statements.  The first requirement
specifically states "increasing", as to eliminate the idea of equality.  I
have found through repeated experience that any sign of conflict should be
treated very seriously.

Secondly, there is _nothing_ in the spec that covers the situation of
conflicting cases where there is more than one sequence of the exact same
length.  His response "it doesn't matter" indicates to me that he did not
consider this possibility when wording the spec.

I was actually shocked that a computer professional posing a requirement
would dismiss the need to specify the definition of which result should be
considered "correct".  If both are "correct", then the defined result is
_not_.  It should be an array of arrays, since all are equally correct.

I deal with ambiguous and confusing specifications all the time...we are not
an XP shop, I'm just giving it a fair hearing.



John


0
jelrick (16)
11/19/2003 12:49:37 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpedua$1mgkns$1@ID-152540.news.uni-berlin.de...

> I don't know where you practice, but customers don't like to be pissed
> off responding to obvious questions :)
>

I'd have thought they'd get more pissed off if the software doesn't do what
they want ;-)

This thread is very interesting to follow.

Cheers
Shane


0
shanemingins (337)
11/19/2003 12:49:49 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpedua$1mgkns$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:
>

SNIP

> > Nope...this isn't multiple choice and you know it.
>
> Why not ?
>
> > Which one is correct?
>
> Any. Both are correct.

Then the result should be an array of an array of integer indexes,
comprising the longest sequences.

Or we should state in the requirements which is expected.

>
> > You are specifying the requirements.  You must make the decision, not
me.
> >
> >
>
> I can make the decision to take my business elsewhere :)

Yes.  And any customer who can't make a simple decision like that would be
offered to do so.<vbg - I'm don't want to start a fight>

SNIP

> > -> []
> > -> [0]
> > -> [2]
> >
> > Your call.
> >
>
> Any of [0], [1], [2] but never [] because its lenght is 0.

I included [] as the possiblity that "there is no single longest sequence".

SNIP

> Any subsequence that meets the specified conditions and has maximum
> length will do. I said "longest" because writing in ASCII the formal
> clause for maximality is essentially boring, but I guess "longest" is
> clear enough what is supposed to mean. Maybe is not "increasing" but

Longest is quite clear...my questions dealt with the ambiguous situation of
where there is conflicting answers.

If it doesn't matter, then it would be easy to rewrite:

"The longest non-decreasing, non-sequential subsequence of reals.  In the
event there is more than one longest of the same length, return the first
subsequence which satisfies the criterea."  Or the last...or the middle
case...whatever.

> rather non-decreasing" Well, if you replace "<=" with "<" in the
> specification, you have essentially the same algorithm.

But not the same results.  If I am getting a spec, the customer expects it
to work a certain way.  That's what I'm used to having to decipher.

>
> I don't know where you practice, but customers don't like to be pissed
> off responding to obvious questions :)

I practice somewhere where I constantly have to clarify "obvious"
questions...with the Federal Government.  You would be surprised how much
trouble you will get into if you don't clarify those issues with them...9
times out of 10 you will deliver the wrong thing thinking you understood
just because you read the formal requirements<g>  The other 1 time, it
conflicts because two people have different ideas of what is to happen.

FWIW


John


0
jelrick (16)
11/19/2003 1:08:06 AM
John Elrick wrote:

> "Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
> news:1p2krvcbb39jhua8mts909rg64aojco1nb@4ax.com...
> 
>>On Mon, 17 Nov 2003 21:27:31 -0800, Costin Cozianu <c_cozianu@hotmail.com>
>>wrote:
>>
>>
>>>Yes, if you read more carefully the spec you can disambiguate :)
>>>
>>>Do they even bother to write Software Requirements Specification these
>>>days in an XP shop ?
>>
>>Often they do not. As you have just demonstrated perfectly, talking with
> 
> the
> 
>>customer works ever so much better. I shall elaborate:
>>
>>You presented, as far as I can tell, a perfectly unambiguous description
> 
> of the
> 
>>problem. It was without flaw (although the limitations of formatting might
> 
> make
> 
>>one wish for better technology).
> 
> 
> Actually, I found the spec ambiguous and contradictory:
> 
> "Find the longest increasing subsequence."
> 
> "the corresponding elements are in ascending order"
> 
> Then the expression:
> 
> "x[index[j]] <= x[index[k]]"
> 
> Which contradicts the first two statements.  The first requirement
> specifically states "increasing", as to eliminate the idea of equality.  I
> have found through repeated experience that any sign of conflict should be
> treated very seriously.
> 

When you have mathematics vs. words you should have trusted the math.
In any case, it was a friendly challenge, so you could hav

This was a challenge and increasing as well as monotone are by default 
taken by mathematician to mean "<=". The relation induced by "<" is 
usually called _strict_ : (strictly monotone, strictly increasing).

Here it is:

http://planetmath.org/encyclopedia/StrictlyMonotoneFunction.html

> Secondly, there is _nothing_ in the spec that covers the situation of
> conflicting cases where there is more than one sequence of the exact same
> length.  His response "it doesn't matter" indicates to me that he did not
> consider this possibility when wording the spec.
> 

Well, it really doesn't matter. The program should output any increasing 
sub-sequence that has maximum length.

The customer is happy as long as it is a subsequence, it is increasing 
and has maximu length.

> I was actually shocked that a computer professional posing a requirement
> would dismiss the need to specify the definition of which result should be
> considered "correct".  If both are "correct", then the defined result is
> _not_.  It should be an array of arrays, since all are equally correct.
> 

I am actually shocked to see a computer professional shocked that a 
requirement specification may define a non-functional relation between 
input and output.

What have you studied in school ? Have you ever heard of Dijkstra ?

> I deal with ambiguous and confusing specifications all the time...we are not
> an XP shop, I'm just giving it a fair hearing.
> 
> 

I don't doubt that you're dealing with confusing specifications -- all 
of us do. But pissing off the customer in the process of clarification 
is hardly any progress.

> 
> John
> 
> 

Costin

0
c_cozianu (217)
11/19/2003 1:39:15 AM
On Tue, 18 Nov 2003 15:09:59 -0800, Costin Cozianu <c_cozianu@hotmail.com>
wrote:

>> No, sorry, not at this time. I'm not unwilling, but since I don't have a program
>> that does the job, and I'm not working on one, creating the tests would be a
>> fairly long and involved hand process. And I suspect that I would get many of
>> them wrong through manual errors, which might be confusing.
>> 
>
>So there is a class of problem where, simply put, tests are much harded 
>to write if they can be written at all before a solution is written.

Certainly. Requirements we don't understand are very hard to write tests for.

The discipline of writing tests, therefore, is one way to discipline ourselves
to come to understand the requirements. Not the only way, surely, but one way.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/19/2003 4:20:24 AM
Costin Cozianu <c_cozianu@hotmail.com> wrote in message news:<bpe4dp$1nbjt3$1@ID-152540.news.uni-berlin.de>...

> Before I post a solution, and the mechanism to come up with one, can you 
> give me a hand and write the unit tests ??

Before you post your solution (***) , can you give me the following "big
Oh" numbers (as best you can) :

- Best case
- Average case
- Worst case


The quick skeletal algorithm I devised (about 1hr of thought in total,
near-zero memory requirements etc) has the following :

Best = O(n log2 n)
Average = O(n^2) - O(n^3) [ this is what it appears to be *** ]
Worst = O(2^n) [ditto]

The best/worst seem bad, though I do have the potential for pruning the
candidate space severely based on some basic evaluation of any
candidate subsequence pattern. But as I always operate on the most
pessimistic basis, I cannot be sure the pruning removes the worst case
without actually implementing the algorithm.

*** Note this is primarily to protect myself from the potential disgrace
    of a CS101 student coming here and showing something in 10 lines of
    of C code and worst case of O(n) . :-)


Regards,
Steven Perryman

BTW, nice formal spec for the problem. Which saved me the first step in
my usual development process.
0
ggroups5 (201)
11/19/2003 11:22:58 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpehm0$1npp66$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:
>
> > "Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
> > news:1p2krvcbb39jhua8mts909rg64aojco1nb@4ax.com...
> >
> >>On Mon, 17 Nov 2003 21:27:31 -0800, Costin Cozianu
<c_cozianu@hotmail.com>
> >>wrote:
SNIP

> When you have mathematics vs. words you should have trusted the math.

<g>Never...I've worked too long.  See below...

> In any case, it was a friendly challenge, so you could hav

True...however, I was only attempting to clarify.

>
> This was a challenge and increasing as well as monotone are by default
> taken by mathematician to mean "<=". The relation induced by "<" is
> usually called _strict_ : (strictly monotone, strictly increasing).
>
> Here it is:
>
> http://planetmath.org/encyclopedia/StrictlyMonotoneFunction.html

Not a mathematician...and even if I was, I'd still ask.  One thing I've
learned is to _never_ trust a formula if it contradicts any portion of the
spec.  Mind you, I deal with many people who _think_ they can write concise
descriptions of their problem set that do not survive the first set of test
data.  Perhaps that experience caused me to react differently than I would
had I started with the assumption that you were a mathematician.

>
> > Secondly, there is _nothing_ in the spec that covers the situation of
> > conflicting cases where there is more than one sequence of the exact
same
> > length.  His response "it doesn't matter" indicates to me that he did
not
> > consider this possibility when wording the spec.
> >
>
> Well, it really doesn't matter. The program should output any increasing
> sub-sequence that has maximum length.
>
> The customer is happy as long as it is a subsequence, it is increasing
> and has maximu length.

<vbg>Not mine.  Plus, we introduce non-repeatibility.  Let's say two people
take two different attacks on the problem, but in the case of duplication
one chooses the first result and the other chooses the last.  We feed
through a very large sequence of live data where the results are not known
and are expensive to check by hand.   The results from the two programs
differ.  Now, is there a problem with one of the algorithms that is causing
it to fail under this circumstance?

Additionally, this particular problem smacks of being a portion of a large
problem.   How am I to know that the larger problem will not become skewed
by the results unless I ask.

>
> > I was actually shocked that a computer professional posing a requirement
> > would dismiss the need to specify the definition of which result should
be
> > considered "correct".  If both are "correct", then the defined result is
> > _not_.  It should be an array of arrays, since all are equally correct.
> >
>
> I am actually shocked to see a computer professional shocked that a
> requirement specification may define a non-functional relation between
> input and output.

Oh...I have seen quite a few of these in my life.  And the first step is to
clarify the desired result.  the question was simple: "what is the result
you expect in this circumstance?"  My shock existed...not for the output but
the, I guess you could say, dismissive attitude toward a potential situation
that could inject large scale risk into a real world project.  Perhaps I
read too much into a friendly challenge, I apologize if I have overreacted.

>
> What have you studied in school ? Have you ever heard of Dijkstra ?

I graduated from school over twenty years ago...yes I have.  But my studies
have very little to do with the actual problems I've faced in the field.

>
> > I deal with ambiguous and confusing specifications all the time...we are
not
> > an XP shop, I'm just giving it a fair hearing.
> >
> >
>
> I don't doubt that you're dealing with confusing specifications -- all
> of us do. But pissing off the customer in the process of clarification
> is hardly any progress.

Well...we are dealing with the limitations of the written word in more
senses than one.  My intent was to clarify the requirements...not to piss
you off.


John


0
jelrick (16)
11/19/2003 1:07:01 PM
S Perryman wrote:
> Costin Cozianu <c_cozianu@hotmail.com> wrote in message news:<bpe4dp$1nbjt3$1@ID-152540.news.uni-berlin.de>...
> 
> 
>>Before I post a solution, and the mechanism to come up with one, can you 
>>give me a hand and write the unit tests ??
> 
> 
> Before you post your solution (***) , can you give me the following "big
> Oh" numbers (as best you can) :
> 
> - Best case
> - Average case
> - Worst case
> 

The problem can take O(n log2 n) in the worst case (with the constant 
factor 1). Additional storage required is ideally O(n), I'm willing to 
give it a pass with O(n^2) to make it for easier to read OO code, but no 
more than that.

> 
> The quick skeletal algorithm I devised (about 1hr of thought in total,
> near-zero memory requirements etc) has the following :
> 
> Best = O(n log2 n)
> Average = O(n^2) - O(n^3) [ this is what it appears to be *** ]
> Worst = O(2^n) [ditto]
> 
> The best/worst seem bad, though I do have the potential for pruning the
> candidate space severely based on some basic evaluation of any
> candidate subsequence pattern. But as I always operate on the most
> pessimistic basis, I cannot be sure the pruning removes the worst case
> without actually implementing the algorithm.
> 
> *** Note this is primarily to protect myself from the potential disgrace
>     of a CS101 student coming here and showing something in 10 lines of
>     of C code and worst case of O(n) . :-)
> 
> 
> Regards,
> Steven Perryman
> 
> BTW, nice formal spec for the problem. Which saved me the first step in
> my usual development process.


Best,
Costin

0
c_cozianu (217)
11/19/2003 2:28:14 PM
John Elrick wrote:

>>Well, it really doesn't matter. The program should output any increasing
>>sub-sequence that has maximum length.
>>
>>The customer is happy as long as it is a subsequence, it is increasing
>>and has maximu length.
> 
> 
> <vbg>Not mine.  Plus, we introduce non-repeatibility.  Let's say two people
> take two different attacks on the problem, but in the case of duplication
> one chooses the first result and the other chooses the last.  We feed
> through a very large sequence of live data where the results are not known
> and are expensive to check by hand.   The results from the two programs
> differ.  Now, is there a problem with one of the algorithms that is causing
> it to fail under this circumstance?
>

Two correct results are easy to compare: the must have the same length. 
What is not easy to test for is that the program(s) found the maximum 
length subsequence.

> Additionally, this particular problem smacks of being a portion of a large
> problem.   How am I to know that the larger problem will not become skewed
> by the results unless I ask.
> 
>

Ok, here's the deal in this hypothetical: the customer is not a real 
"don't know anything customer", it is another programmer who wants to 
subcontract.

>>
>>I am actually shocked to see a computer professional shocked that a
>>requirement specification may define a non-functional relation between
>>input and output.
> 
> 
> Oh...I have seen quite a few of these in my life.  And the first step is to
> clarify the desired result.  the question was simple: "what is the result
> you expect in this circumstance?"  My shock existed...not for the output but
> the, I guess you could say, dismissive attitude toward a potential situation
> that could inject large scale risk into a real world project.  Perhaps I
> read too much into a friendly challenge, I apologize if I have overreacted.
> 
>

To convince you even more, that non-functional relations between input 
and output are plausible in the real world, I could have asked you to 
generate a strong key for a cryptographic protocol. Or just the random 
number generator. We don't want that to be deterministic, do we ?

So yes, we deal with requirements that are non-deterministic. of course, 
this makes the doctrine of unit testing and TDD very hard to apply (if 
possible at all), but that's a secondary consideration.

Best,
Costin

0
c_cozianu (217)
11/19/2003 2:38:41 PM
I suppose this is as good a place as any to jump into the conversation.

"Costin Cozianu" <c_cozianu@hotmail.com> presented a problem in
news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...

To paraphrase we are given a (possibly empty) sequence of numbers.
The crux of the problem is to remove the fewest numbers from the sequence
which both preserves the original ordering and produces a new sequence that
is monotonicly increasing.  (I know this is not identical to the problem
presented, but it is the part I find interesting.)

How does TDD help with this problem?

* My first test cases were {}, {1}, and {1 2}.  These all pass simply by
returning the input sequence.

* Next I tried {2 1}.  I solved this case by returning the first element if
the sequence was out of order.

I should have tried {1 1}, but didn't.  This would ensure I made the right
choice of < vs <= in my test.

With this little experience out of the way, I started to think about more
general solutions.  I observed that if a given sequence is in order, the
task is finished.  I simply output the sequence.  If the sequence is not in
order, there must be at least one pair of numbers in the sequence for which
the first is greater than the second.  At least one of these numbers must be
removed in the final sequence.  I generate two new sequences.  One with the
first of the pair removed and the other with the second removed.  If I am
able to find the longest sequence of each new sequence, the longer of the
two must be the answer. This leads to a simple recursive solution.

The function Longest takes a sequence.
If the sequence is ordered, return it.
Otherwise, find a pair of unordered numbers in the sequence,
generate two new sequences as described above, and
call Longest with each sequence.
Return the longer of the two results.

While implementing this, there were a few implementation specific test cases
for a partition function which split the sequence at an unordered pair.

The remaining test cases were {8 8 8 8} {1 2 3 1 2 3 1 2 3}, and a few
others.  In fact, I took a short cut and let the program generate the answer
which I then checked and added to the test.

TDD helped by:
  1. Making sure a few of the boundary cases worked, and kept working as the
design evolved.
  2. Helped me to understand the problem.  In particular, the {2 1} test
case started me thinking about partitioning the sequence.

TDD did not:
  1. Provide me with a cookbook solution.
  2. Substitute for thought or logic.
  3. Prove that my logic was correct or that my algorithm works in any but
the test cases.

Pretty much what I expected.

Regards,

Bob Oliver







0
oliverb (5)
11/19/2003 2:47:51 PM
Could you run the algorithm for me please with the input of 
[1000,999,998,..., 1] ?

I'm waiting for the results, so please don't hurry, take your time ...

I noticed that you haven't posted your unit tests yet :)

Best,
Costin

Robert Oliver wrote:

> I suppose this is as good a place as any to jump into the conversation.
> 
> "Costin Cozianu" <c_cozianu@hotmail.com> presented a problem in
> news:bpbu6c$1mf3mg$1@ID-152540.news.uni-berlin.de...
> 
> To paraphrase we are given a (possibly empty) sequence of numbers.
> The crux of the problem is to remove the fewest numbers from the sequence
> which both preserves the original ordering and produces a new sequence that
> is monotonicly increasing.  (I know this is not identical to the problem
> presented, but it is the part I find interesting.)
> 
> How does TDD help with this problem?
> 
> * My first test cases were {}, {1}, and {1 2}.  These all pass simply by
> returning the input sequence.
> 
> * Next I tried {2 1}.  I solved this case by returning the first element if
> the sequence was out of order.
> 
> I should have tried {1 1}, but didn't.  This would ensure I made the right
> choice of < vs <= in my test.
> 
> With this little experience out of the way, I started to think about more
> general solutions.  I observed that if a given sequence is in order, the
> task is finished.  I simply output the sequence.  If the sequence is not in
> order, there must be at least one pair of numbers in the sequence for which
> the first is greater than the second.  At least one of these numbers must be
> removed in the final sequence.  I generate two new sequences.  One with the
> first of the pair removed and the other with the second removed.  If I am
> able to find the longest sequence of each new sequence, the longer of the
> two must be the answer. This leads to a simple recursive solution.
> 
> The function Longest takes a sequence.
> If the sequence is ordered, return it.
> Otherwise, find a pair of unordered numbers in the sequence,
> generate two new sequences as described above, and
> call Longest with each sequence.
> Return the longer of the two results.
> 
> While implementing this, there were a few implementation specific test cases
> for a partition function which split the sequence at an unordered pair.
> 
> The remaining test cases were {8 8 8 8} {1 2 3 1 2 3 1 2 3}, and a few
> others.  In fact, I took a short cut and let the program generate the answer
> which I then checked and added to the test.
> 
> TDD helped by:
>   1. Making sure a few of the boundary cases worked, and kept working as the
> design evolved.
>   2. Helped me to understand the problem.  In particular, the {2 1} test
> case started me thinking about partitioning the sequence.
> 
> TDD did not:
>   1. Provide me with a cookbook solution.
>   2. Substitute for thought or logic.
>   3. Prove that my logic was correct or that my algorithm works in any but
> the test cases.
> 
> Pretty much what I expected.
> 
> Regards,
> 
> Bob Oliver
> 
> 
> 
> 
> 
> 
> 

0
c_cozianu (217)
11/19/2003 2:59:44 PM
Costin,

Thanks for the reply.

"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpg0j7$1o6vka$1@ID-152540.news.uni-berlin.de...
> Could you run the algorithm for me please with the input of
> [1000,999,998,..., 1] ?

Ultimately the algorithm will retain the rightmost 1, however, it might not
happen before the sun burns out and you may not be willing to wait that long
:)

I'll give the problem a little more thought.  In the meantime, a few more
observations.

I find it interesting that you chose to provide me with a test case (albiet,
without the answer) to illustrate the drawback of using my algorithm.  This
is something I would hope for in a good XP coach.

I still think the algorithm is correct under the original spec.  With only
short data sets, it could be just the ticket.  I probably would not try to
optimize a working and understandable piece of code until I had the need.

I don't think this kind of situation is terribly uncommon.  Designers and
customers both sometimes don't think things through, make wrong guesses, and
generally provide incomplete requirements.  Even when we sit down together,
we overlook stuff.  Software needs to be changed.  I find that TDD gives me
a better understanding of the effect these changes are having on the system.

>
> I'm waiting for the results, so please don't hurry, take your time ...
>
> I noticed that you haven't posted your unit tests yet :)

All of my unit tests are of the form:

assert LongestSequence (1,2,3,1,2,3,1,2,3) == (1,1,1,2,3)

I know that there are other possible solutions and that changing the
algorithm may cause some tests to fail that should really pass.  This is
clearly a drawback.

If I wanted to make my tests insensitive to algorithm changes, I would write
a function that takes the expected length of a sequence and a test sequence.
It would check the length and ensure the sequence was sorted.  The results
of this test depend only on the input data, not the algorithm.  When it
becomes a problem, this is what I will do.

Regards,

Bob



0
oliverb (5)
11/19/2003 5:52:40 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpfvbg$1o6uin$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:

Here's a solution in Ruby (had to pick _some_ language).  Built test first,
and follows the plan - return the first longest subsegment

=================
# TestRealToIndex.rb

require 'runit/cui/testrunner'
require 'runit/testsuite'
require 'runit/testcase'
require 'realToIndex'

class Testing_RealToIndexArray < RUNIT::TestCase
  def test_new
    aRealToIndexArray = RealToIndexArray.new
    assert_equal([], aRealToIndexArray.maxSubSegment)
  end

  def test_single
    aRealToIndexArray = RealToIndexArray.new([1])
    assert_equal([0], aRealToIndexArray.maxSubSegment)
  end

  def test_stream
    aRealToIndexArray = RealToIndexArray.new([1,2,3])
    assert_equal([0,1,2], aRealToIndexArray.maxSubSegment)
  end

  def test_inside
    aRealToIndexArray = RealToIndexArray.new([1,2,3,1,4])
    assert_equal([0,1,2,4], aRealToIndexArray.maxSubSegment)
  end

  def test_duplicates
    aRealToIndexArray = RealToIndexArray.new([8,8,8])
    assert_equal([0,1,2], aRealToIndexArray.maxSubSegment)
  end
end

RUNIT::CUI::TestRunner.run(Testing_RealToIndexArray.suite)


=================
# realToIndex.rb

class RealToIndexArray

  class NullIndexItem

    def update(aReal, aIndex)
    end

    def count
      0
    end

    def maxSubSegment
      []
    end
  end

  class IndexItem

    def update(aReal, aIndex)
      if aReal >= @LastMax
        @LastMax = aReal
        @ArrayOfIndexes[@ArrayOfIndexes.length] = aIndex
      end
    end

    def initialize(aReal, aIndex)
      @ArrayOfIndexes = Array.new
      @LastMax = 0
      update(aReal, aIndex)
    end

    def count
      @ArrayOfIndexes.length
    end

    def maxSubSegment
      @ArrayOfIndexes
    end

  end

  def update(aReal, aIndex)
    @ArrayOfItems.each {|item|
      item.update(aReal, aIndex)
    }
    @ArrayOfItems[@ArrayOfItems.length] = IndexItem.new(aReal, aIndex)
  end

  def initialize(aArrayOfReals = [])
    @ArrayOfItems = Array.new
    aArrayOfReals.each_index {|i|
      update(aArrayOfReals[i], i)
    }
  end


  def maxSubSegment
    max_item = NullIndexItem.new
    @ArrayOfItems.each {|item|
     if item.count > max_item.count then
       max_item = item
     end
     }

    max_item.maxSubSegment
  end

end



0
jelrick (16)
11/19/2003 6:54:47 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpfvbg$1o6uin$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:
>
> >>Well, it really doesn't matter. The program should output any increasing
> >>sub-sequence that has maximum length.

SNIP

> Two correct results are easy to compare: the must have the same length.
> What is not easy to test for is that the program(s) found the maximum
> length subsequence.

What is also not easy to check is that one of the two is the correct length
by accident.  If the algorithm is flawed, it is still possible that it is
returning a "correct length", but not the correct results.

SNIPPING AGREEMENT

> To convince you even more, that non-functional relations between input
> and output are plausible in the real world, I could have asked you to
> generate a strong key for a cryptographic protocol. Or just the random
> number generator. We don't want that to be deterministic, do we ?

Deterministic?  Of course...but in a different sense.  For a random number
generator we would use statistical analysis on a large run to determine that
the results do exhibit randomness.  The strong key would have to survive a
similar test.


John


0
jelrick (16)
11/19/2003 6:59:10 PM
Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
written this on (or about)  Mon, 17 Nov 2003 21:27:31 -0800, :

>>   f([1,2,3,1,2,3,1,2,3]) -> ?
>
>f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])

Actually there are six solutions:
0,1,2,5,8,
0,1,4,5,8,
0,1,4,7,8,
0,3,4,5,8,
0,3,4,7,8,
0,3,6,7,8,

Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
11/19/2003 7:08:32 PM
>>Two correct results are easy to compare: the must have the same length.
>>What is not easy to test for is that the program(s) found the maximum
>>length subsequence.
> 
> 
> What is also not easy to check is that one of the two is the correct length
> by accident.  If the algorithm is flawed, it is still possible that it is
> returning a "correct length", but not the correct results.
> 

But if it is the maximum length and is a correct subsequence, than it is 
giving a correct result. A correct result is any of the maximum legnth 
sequences. There will be at least one, but there can be more than one 
sequence.

If you are to design unit tests (which you didn't) to allow for 
algorithm improvements or just refactoring, you cannot assume that the 
results will always be the same.

Even if you get a pass in assuming that, going at it the way you did by 
enumerating a few samples is not really convincing since you don't 
justify that those are the only important test cases (indeed they are not).

>>To convince you even more, that non-functional relations between input
>>and output are plausible in the real world, I could have asked you to
>>generate a strong key for a cryptographic protocol. Or just the random
>>number generator. We don't want that to be deterministic, do we ?
> 
> 
> Deterministic?  Of course...but in a different sense.  For a random number
> generator we would use statistical analysis on a large run to determine that
> the results do exhibit randomness.  The strong key would have to survive a
> similar test.
> 

And you'd have to survive waiting for the results of any conclusive 
tests :)  But what was the subject, did you want to argue that I have to 
give you deterministic or (functional) specification, or else you would 
not subcontract with me ?

The spec is what I posted initially, and I stand by it, ok, I admit to 
one inaccuracy, I should replace "the longest increasing subsequence" 
with "any longest increasing subsequence". Bad English.

> 
> John
> 
> 

Cheers,
Costin

0
c_cozianu (217)
11/19/2003 7:22:10 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpgfv0$1o9o2n$1@ID-152540.news.uni-berlin.de...
> >>Two correct results are easy to compare: the must have the same length.
> >>What is not easy to test for is that the program(s) found the maximum
> >>length subsequence.
> >
> >
> > What is also not easy to check is that one of the two is the correct
length
> > by accident.  If the algorithm is flawed, it is still possible that it
is
> > returning a "correct length", but not the correct results.
> >
>
> But if it is the maximum length and is a correct subsequence, than it is
> giving a correct result. A correct result is any of the maximum legnth
> sequences. There will be at least one, but there can be more than one
> sequence.

The key words here are "correct subsequence"...as RDM pointed out, there
were six additional correct results for that one example.  What if the
algorithm produces a result that looks correct, but is not?

>
> If you are to design unit tests (which you didn't) to allow for
> algorithm improvements or just refactoring, you cannot assume that the
> results will always be the same.

Nope...wasn't part of the spec or needed at this time.

>
> Even if you get a pass in assuming that, going at it the way you did by
> enumerating a few samples is not really convincing since you don't
> justify that those are the only important test cases (indeed they are
not).

Care to elaborate?

>
> >>To convince you even more, that non-functional relations between input
> >>and output are plausible in the real world, I could have asked you to
> >>generate a strong key for a cryptographic protocol. Or just the random
> >>number generator. We don't want that to be deterministic, do we ?
> >
> >
> > Deterministic?  Of course...but in a different sense.  For a random
number
> > generator we would use statistical analysis on a large run to determine
that
> > the results do exhibit randomness.  The strong key would have to survive
a
> > similar test.
> >
>
> And you'd have to survive waiting for the results of any conclusive
> tests :)  But what was the subject, did you want to argue that I have to
> give you deterministic or (functional) specification, or else you would
> not subcontract with me ?

Not subcontract...I've done the other way too many times...got too much grey
hair from it.  It always turns into "but you said...no you said, but I
meant" type arguments that mean I don't get paid what I'm supposed to.

>
> The spec is what I posted initially, and I stand by it, ok, I admit to
> one inaccuracy, I should replace "the longest increasing subsequence"
> with "any longest increasing subsequence". Bad English.

That's cool.  I was never criticising the spec, I was attempting to get
clarification on points that, in my opinion, produced risk that the desired
results would not be delivered.


John


0
jelrick (16)
11/19/2003 7:33:27 PM
"John Elrick" <jelrick@adelphia.net> wrote in message
news:X%Oub.2334$_i1.1800824@news2.news.adelphia.net...
>
> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bpfvbg$1o6uin$1@ID-152540.news.uni-berlin.de...
> > John Elrick wrote:
>
> Here's a solution in Ruby (had to pick _some_ language).  Built test
first,
> and follows the plan - return the first longest subsegment
>
> =================
> # TestRealToIndex.rb
>

For laughs, I added this test:

  def test_heavyduplicates
    aRealToIndexArray = RealToIndexArray.new([1,2,3,1,2,3,1,2,3])
    assert_equal([0,1,2,5,8], aRealToIndexArray.maxSubSegment)
  end

Passed with flying colors...


John


0
jelrick (16)
11/19/2003 7:36:15 PM
Uncle Bob (Robert C. Martin) wrote:

> Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
> written this on (or about)  Mon, 17 Nov 2003 21:27:31 -0800, :
> 
> 
>>>  f([1,2,3,1,2,3,1,2,3]) -> ?
>>
>>f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])
> 
> 
> Actually there are six solutions:
> 0,1,2,5,8,
> 0,1,4,5,8,
> 0,1,4,7,8,
> 0,3,4,5,8,
> 0,3,4,7,8,
> 0,3,6,7,8,
> 

Of course, I wouldn't even bother to enumerate all of them. The problem 
is not about enumerating all solutions.

Added bonus if one can prove the the number of possible solutions is in 
the worst case exponential. As such, I'd have to be nuts to bother to 
enumerate all solutions for any particular case, those were just 
examples I gave to Eric to show that there are more than one possible 
solutions.

But I'm curious where the famous TDDers, Unit Tester have all gone ? I'd 
have expected for such a trivial problem the tests were ready by now.


Costin



0
c_cozianu (217)
11/19/2003 7:40:03 PM
John Elrick wrote:

> "John Elrick" <jelrick@adelphia.net> wrote in message
> news:X%Oub.2334$_i1.1800824@news2.news.adelphia.net...
> 
>>"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
>>news:bpfvbg$1o6uin$1@ID-152540.news.uni-berlin.de...
>>
>>>John Elrick wrote:
>>
>>Here's a solution in Ruby (had to pick _some_ language).  Built test
> 
> first,
> 
>>and follows the plan - return the first longest subsegment
>>
>>=================
>># TestRealToIndex.rb
>>
> 
> 
> For laughs, I added this test:
> 
>   def test_heavyduplicates
>     aRealToIndexArray = RealToIndexArray.new([1,2,3,1,2,3,1,2,3])
>     assert_equal([0,1,2,5,8], aRealToIndexArray.maxSubSegment)
>   end
> 
> Passed with flying colors...
> 
> 
> John
> 
> 
For laughs, do you think you can convince me with yet another test ?


Ok, I think Ruby is a lousy language design (or the way you write code 
is lousy), meaning even if I read Ruby code before, it is still not 
trivial for me to understand the code.

But let me try it, nevertheless:

You maintain an array of possible solutions, and for each real number 
element in the input array, you "update in place" all the existing 
possible solutions (by extending them with one element if the element 
coming in is >= than the last one ), and you add at the end a new 
possible solution of length 1: exactly the new element coming in.

As such your array of possible solutions always grows by 1. At the end, 
you scan the array of possible solutions and pick the first one with 
maximum length.

If I understood your code correctly, then your algorithm is wrong. I'll 
give you flying colors to find a failing test.

Costin


0
c_cozianu (217)
11/19/2003 7:54:22 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpghrb$1o8cu5$1@ID-152540.news.uni-berlin.de...
> John Elrick wrote:

SNIP

> Ok, I think Ruby is a lousy language design (or the way you write code
> is lousy), meaning even if I read Ruby code before, it is still not
> trivial for me to understand the code.

Hmmm....YOU don't understand it so either the language sucks or I'm
incompetent...

>
> But let me try it, nevertheless:
>
> You maintain an array of possible solutions, and for each real number
> element in the input array, you "update in place" all the existing
> possible solutions (by extending them with one element if the element
> coming in is >= than the last one ), and you add at the end a new
> possible solution of length 1: exactly the new element coming in.
>
> As such your array of possible solutions always grows by 1. At the end,
> you scan the array of possible solutions and pick the first one with
> maximum length.
>
> If I understood your code correctly, then your algorithm is wrong. I'll
> give you flying colors to find a failing test.

Hmmm...nope, I guess I don't get flying colors.

Let's see your failing test.



John


0
jelrick (16)
11/19/2003 8:27:44 PM
"John Elrick" <jelrick@adelphia.net> wrote in message
news:4nQub.2347$_i1.1816111@news2.news.adelphia.net...
>
> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bpghrb$1o8cu5$1@ID-152540.news.uni-berlin.de...
> > John Elrick wrote:
>
> SNIP

> > If I understood your code correctly, then your algorithm is wrong. I'll
> > give you flying colors to find a failing test.

Nope I do get flying colors...as implemented it fails on a negative.

Fixes below:

  def test_negatives
    aRealToIndexArray = RealToIndexArray.new([-1,0,2])
    assert_equal([0,1,2], aRealToIndexArray.maxSubSegment)
  end


    def initialize(aReal, aIndex)
      @ArrayOfIndexes = Array.new
      @LastMax = aReal
      @ArrayOfIndexes[0] = aIndex
    end


John


0
jelrick (16)
11/19/2003 8:49:09 PM
Hi

I had a go at doing the Bowling Game example last night with the same design
idea, i.e. Frames and no conditionals.

What I tried to do differently was pass to the Frame being scored the
NextFrame so that the score could be calculated for spares and strike.  This
caused me to create an EndFrame (would I be correct in terming this a
NullObject?).

I added a couple of extra tests along the way (a novice's caution).

It is in Java as I do not have access to a C# development environment.

I would appreciate any comments though.

Thanks Shane



package bowlingGame.tests;

import junit.framework.TestCase;
import bowlingGame.BowlingGame;

public class TestBowlingGame extends TestCase
{
    BowlingGame game;

    protected void setUp() throws Exception
    {
        game = new BowlingGame();
    }

    public void testGutterBalls()
    {
        manyOpenFrames(10, 0, 0);
        assertEquals(0, game.getScore());
    }

    public void testThrees()
    {
        manyOpenFrames(10, 3, 3);
        assertEquals(60, game.getScore());
    }

    public void testSpareFrame()
    {
        game.addSpareFrame(4, 6);
        game.addOpenFrame(3, 5);
        manyOpenFrames(8, 0, 0);
        assertEquals(21, game.getScore());
    }

    public void testSpareFrame2()
    {
        game.addSpareFrame(4, 6);
        game.addOpenFrame(5, 3);
        manyOpenFrames(8, 0, 0);
        assertEquals(23, game.getScore());
    }

    public void testSpareFrame3()
    {
        game.addSpareFrame(4, 6);
        game.addSpareFrame(4, 6);
        game.addOpenFrame(5, 3);
        manyOpenFrames(7, 0, 0);
        assertEquals(37, game.getScore());
    }

    public void testStrike()
    {
        game.addStrikeFrame();
        game.addOpenFrame(5, 3);
        manyOpenFrames(8, 0, 0);
        assertEquals(26, game.getScore());
    }

    public void testSpareAndStrike()
    {
        game.addSpareFrame(4, 6);
        game.addSpareFrame(4, 6);
        game.addStrikeFrame();
        game.addOpenFrame(5, 3);
        manyOpenFrames(6, 0, 0);
        assertEquals(60, game.getScore());
    }

    public void testStrikeFinalFrame()
    {
        manyOpenFrames(9, 0, 0);
        game.addStrikeFrame();
        game.addBonusRoll(5);
        game.addBonusRoll(3);
        assertEquals(18, game.getScore());
    }

    public void testSpareFinalFrame()
    {
        manyOpenFrames(9, 0, 0);
        game.addSpareFrame(4, 6);
        game.addBonusRoll(5);
        assertEquals(15, game.getScore());
    }

    public void testPerfectScore()
    {
        for (int i = 0; i < 10; i++)
            game.addStrikeFrame();
        game.addBonusRoll(10);
        game.addBonusRoll(10);
        assertEquals(300, game.getScore());
    }

    public void testAlternating()
    {
        for (int i = 0; i < 5; i++)
        {
            game.addStrikeFrame();
            game.addSpareFrame(4, 6);
        }
        game.addBonusRoll(10);
        assertEquals(200, game.getScore());
    }

    private void manyOpenFrames(int count, int firstBall, int secondBall)
    {
        for (int frameNumber = 0; frameNumber < count; frameNumber++)
            game.addOpenFrame(firstBall, secondBall);
    }
}


package bowlingGame;

import java.util.ArrayList;
import java.util.List;

public class BowlingGame
{
    private List frames;

    public BowlingGame()
    {
        this.frames = new ArrayList();
    }

    public void addOpenFrame(int firstBall, int secondBall)
    {
        this.frames.add(new OpenFrame(firstBall, secondBall));
    }

    public void addSpareFrame(int firstBall, int secondBall)
    {
        this.frames.add(new SpareFrame(firstBall, secondBall));
    }

    public void addStrikeFrame()
    {
        this.frames.add(new StrikeFrame());
    }

    public void addBonusRoll(int roll)
    {
        this.frames.add(new BonusRoll(roll));
    }

    public int getScore()
    {
        int score = 0;

        for (int i = 0; i < frames.size(); i++)
        {
            Frame frame = (Frame) frames.get(i);
            score += frame.getScore(getNextFrame(i));
        }
        return score;
    }

    private Frame getNextFrame(int index)
    {
        int nextIndex = index + 1;
        if (nextIndex == this.frames.size()) return new EndFrame();
        return (Frame) this.frames.get(nextIndex);
    }
}

package bowlingGame;

abstract public class Frame
{
    protected int firstBall;
    protected int secondBall;

    protected Frame(int firstBall, int secondBall)
    {
        this.firstBall = firstBall;
        this.secondBall = secondBall;
    }

    abstract int getScore(Frame nextFrame);

    public int getFirstBall()
    {
        return this.firstBall;
    }

    public int getSimpleTotal()
    {
        return this.firstBall + this.secondBall;
    }
}

package bowlingGame;

public class OpenFrame extends Frame
{
    public OpenFrame(int firstBall, int secondBall)
    {
        super(firstBall, secondBall);
    }

    public int getScore(Frame nextFrame)
    {
        return getSimpleTotal();
    }
}

package bowlingGame;

public class StrikeFrame extends Frame
{
    public StrikeFrame()
    {
        super(10, 10);
    }

    public int getScore(Frame nextFrame)
    {
        return this.firstBall + nextFrame.getSimpleTotal();
    }
}

package bowlingGame;

public class SpareFrame extends Frame
{
    public SpareFrame(int firstBall, int secondBall)
    {
        super(firstBall, secondBall);
    }

    public int getScore(Frame nextFrame)
    {
        return 10 + nextFrame.getFirstBall();
    }
}

package bowlingGame;

public class BonusRoll extends Frame
{
    public BonusRoll(int roll)
    {
        super(roll, 0);
    }

    public int getScore(Frame nextFrame)
    {
        return nextFrame.getFirstBall();
    }
}

package bowlingGame;

public class EndFrame extends Frame
{
    public EndFrame()
    {
        super(0, 0);
    }

    public int getScore(Frame nextFrame)
    {
        return 0;
    }
}

-- 
shanemingins@yahoo.com.clothes

remove clothes before replying

"It is not the strongest of the species that survive, nor the most
intelligent, but the one most responsive to change." --- Charles Darwin


0
shanemingins (337)
11/19/2003 9:03:05 PM
Have you tried with [1,2,1,1]  ?

John Elrick wrote:

> "John Elrick" <jelrick@adelphia.net> wrote in message
> news:4nQub.2347$_i1.1816111@news2.news.adelphia.net...
> 
>>"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
>>news:bpghrb$1o8cu5$1@ID-152540.news.uni-berlin.de...
>>
>>>John Elrick wrote:
>>
>>SNIP
> 
> 
>>>If I understood your code correctly, then your algorithm is wrong. I'll
>>>give you flying colors to find a failing test.
> 
> 
> Nope I do get flying colors...as implemented it fails on a negative.
> 
> Fixes below:
> 
>   def test_negatives
>     aRealToIndexArray = RealToIndexArray.new([-1,0,2])
>     assert_equal([0,1,2], aRealToIndexArray.maxSubSegment)
>   end
> 
> 
>     def initialize(aReal, aIndex)
>       @ArrayOfIndexes = Array.new
>       @LastMax = aReal
>       @ArrayOfIndexes[0] = aIndex
>     end
> 
> 
> John
> 
> 

0
c_cozianu (217)
11/19/2003 9:06:58 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpgm3f$1ockuv$1@ID-152540.news.uni-berlin.de...
> Have you tried with [1,2,1,1]  ?

I see your point.


John


0
jelrick (16)
11/19/2003 10:08:38 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpgh0g$1ok02n$1@ID-152540.news.uni-berlin.de...

> But I'm curious where the famous TDDers, Unit Tester have all gone ? I'd
> have expected for such a trivial problem the tests were ready by now.

Are you waiting for a set of unit tests that can be used to guarantee a
correct solution to your posted problem?  If so I suspect it will take
longer than my first algorithm will take to complete [999,998,...,1].

TDD doesn't work that way.  Tests are developed along the way.

How do you go about developing tests for the software you write?  I suspect
you try to find ways the software might fail and build test cases around
them.  Then if the software does fail, you fix it.  Am I even close?

Regards,
Robert Oliver


0
11/19/2003 11:10:24 PM
On Wed, 19 Nov 2003 09:47:51 -0500, "Robert Oliver" <oliverb@hfl.tc.faa.gov>
wrote:

>TDD helped by:
>  1. Making sure a few of the boundary cases worked, and kept working as the
>design evolved.
>  2. Helped me to understand the problem.  In particular, the {2 1} test
>case started me thinking about partitioning the sequence.
>
>TDD did not:
>  1. Provide me with a cookbook solution.
>  2. Substitute for thought or logic.
>  3. Prove that my logic was correct or that my algorithm works in any but
>the test cases.
>
>Pretty much what I expected.

That's what I'd expect too, though I often gain confidence in the algorithm
which seems to be borne out by later experience with the program.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/19/2003 11:35:15 PM
"Shane Mingins" <shanemingins@yahoo.com.clothes> wrote:

> remove clothes before replying

Done!        ;- }

> "It is not the strongest of the species that survive, nor the most
> intelligent, but the one most responsive to change." --- Charles
Darwin

I.)

Just a few words, and to begin, I don't  really disagree with this.

The essence of Darwin's ideas, considering them as a whole beyond just
the above remark, seems to me, to be that organisms that are able to
adapt to what they face tend to be successful.

Rather than the key being that a species is the "*most* responsive to
change", or some other slightly different formulation, it appears that
species have successfully adapted to contextual changes if their
adaptive response was "at least enuff", i.e. "at least *sufficient*".

II.)
It doesn't seem to be "survival of the fittest", that is salient to a
species ability to survive, but the ability of a species to survive in a
given context.  Historically it is evident that it has been the good
fortune of multiple species to simultaneously adapt successfully to an
environment.  Some species use the same, or a common genetic trait to
succeed, while other species use other genetic traits.

III.)
Although successful adaptation to contextual change involves having
genetic traits that confer the ability to adapt to the environment, I'm
not so sure this is "directly" true for hominids--especially the later
ones like homo-sapiens.

Genetic mechanisms interacting with the context of experience of
hominids has led to a large brain size.  There is no doubt that this
large brain has helped hominids to adapt successfully to many contextual
changes.  But it also seems that much of hominids ability to adapt has
come out of a scientific process of learning about the environment and
carrying out practices that enabled hominids to cope with their
environment.  In later times this social epistemological process seems
to play a more predominant role in the ability of homo-sapiens to adapt
successfully than does a kind of direct genetic evolution.

Elliott
-- 
                      *~* Theory Leads, Practice Verifies *~*
     *~*  Global Plans + IID (Iterative & Incremental Development) *~*
-- 
Be bold, be artistically imaginative, yet scientifically grounded -- DO
GREAT  OO  MODELLING  WITH  OBJECTS  OF  ALL  TYPES!!







0
universe2 (613)
11/19/2003 11:43:54 PM
Robert Oliver wrote:
> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bpgh0g$1ok02n$1@ID-152540.news.uni-berlin.de...
> 
> 
>>But I'm curious where the famous TDDers, Unit Tester have all gone ? I'd
>>have expected for such a trivial problem the tests were ready by now.
> 
> 
> Are you waiting for a set of unit tests that can be used to guarantee a
> correct solution to your posted problem?  

For the vast majority of problems in the wild no Unit Test, Integration 
Test, Automation Test, XP or non-XP can guarantee a correct solution.

However, some tests are deemed relevant, in the sense that they can 
inspire great confidence in the correctness of an implementation. In 
other words, given that the program passes a battery of such tests the 
likelyhood of the program still having bugs is minimal.

> If so I suspect it will take
> longer than my first algorithm will take to complete [999,998,...,1].
> 
> TDD doesn't work that way.  Tests are developed along the way.
> 

I know how TDD is supposed to work. The mantra is test first, no 
functionality added before a test is written. You have to either you 
write very irrelevant test cases like John did, or you did or not write 
any test at all, because only after you get a grasp of the design of the 
solution you are able to write relevant tests.


> How do you go about developing tests for the software you write?  I suspect
> you try to find ways the software might fail and build test cases around
> them.  Then if the software does fail, you fix it.  Am I even close?
> 

I very rarely if ever write the tests first. I write them post factum, 
and I write them very differently.

I also don't write tests for trivial things. And I have a QA department 
to save my ass :) In this case, I'd have a huge amount of random data 
with solutions and let your algorithm run overnight. In some cases, I 
had extensive tests run over the weekend, and even for a week.

This requires a totally separate engineering effort dedicated to writing 
tests, and in any way it cannot be "test first".


> Regards,
> Robert Oliver
> 
> 

best,
Costin

0
c_cozianu (217)
11/20/2003 1:40:41 AM
> For the vast majority of problems in the wild no Unit Test, Integration 
> Test, Automation Test, XP or non-XP can guarantee a correct solution.
> 

Actually it was meant *cannot* guarantee a correct solution.

0
c_cozianu (217)
11/20/2003 1:43:12 AM
"Universe" <universe@covad.net> wrote in message:

> ...
> II.)
> It doesn't seem to be "survival of the fittest", that is salient to a
> species ability to survive, but the ability of a species to survive in a
> given context.  Historically it is evident that it has been the good
> fortune of multiple species to simultaneously adapt successfully to
    *** the same niche(s) in ***
>  an environment.  Some species use the same, or a common genetic trait to
> succeed, while other species use other genetic traits.
> ...

Elliott


0
universe2 (613)
11/20/2003 2:43:52 AM
> Robert Oliver wrote:
<snip>
> > How do you go about developing tests for the software you write?  I
suspect
> > you try to find ways the software might fail and build test cases around
> > them.  Then if the software does fail, you fix it.  Am I even close?
> >
>"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bph64m$1msum1$1@ID-152540.news.uni-berlin.de...

> I very rarely if ever write the tests first. I write them post factum,
> and I write them very differently.
>
> I also don't write tests for trivial things. And I have a QA department
> to save my ass :) In this case, I'd have a huge amount of random data
> with solutions and let your algorithm run overnight. In some cases, I
> had extensive tests run over the weekend, and even for a week.

In this case, where would you find this "huge amount of random data with
solutions"?  So much, in fact, that you could reasonably "run overnight"?

> This requires a totally separate engineering effort dedicated to writing
> tests, and in any way it cannot be "test first".

Well, I've spent a fair amount of time testing Radar systems.  We were
required to write tests from the specification years before the product was
delivered.  Different? Somewhat.  But the basic principle was, "if you can't
test the requirement, it's not really required."   Many important tests can
be written first.

Regards,

Robert Oliver


0
11/20/2003 4:05:41 AM
Robert Oliver wrote:
>>Robert Oliver wrote:
> 
> <snip>
> 
>>>How do you go about developing tests for the software you write?  I
> 
> suspect
> 
>>>you try to find ways the software might fail and build test cases around
>>>them.  Then if the software does fail, you fix it.  Am I even close?
>>>
>>
>>"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> 
> news:bph64m$1msum1$1@ID-152540.news.uni-berlin.de...
> 
> 
>>I very rarely if ever write the tests first. I write them post factum,
>>and I write them very differently.
>>
>>I also don't write tests for trivial things. And I have a QA department
>>to save my ass :) In this case, I'd have a huge amount of random data
>>with solutions and let your algorithm run overnight. In some cases, I
>>had extensive tests run over the weekend, and even for a week.
> 
> 
> In this case, where would you find this "huge amount of random data with
> solutions"?  So much, in fact, that you could reasonably "run overnight"?
> 
> 
>>This requires a totally separate engineering effort dedicated to writing
>>tests, and in any way it cannot be "test first".
> 
> 
> Well, I've spent a fair amount of time testing Radar systems.  We were
> required to write tests from the specification years before the product was
> delivered.  Different? Somewhat.  But the basic principle was, "if you can't
> test the requirement, it's not really required."   Many important tests can
> be written first.
> 

Agreed. There are cases and cases. Other tests cannot be written first. 
Like in this instance. Having a solution of my own, I can produce tons 
of random samples and their solutions, quite easily. But without having 
the solution, you can only handpick a few samples, and even that is 
error prone.

Many tests can be written first, indeed, starting from requirements 
(although Uncle Bob, Phlip et. comp. claimed that requirements *should* 
be written as executable tests). Other tests involved individual modules 
that do not have a requirements document.

Yet other requirements can be proved to be met. Not that I write any of 
these proofs, of course, it is too tedious yet, but it is my 
responsibility that I should be able to write the proof should anybody 
ask. And trying to render the program provable can lead to very clean 
designs, much more than trying to write tests. That's why proof driven 
development, what Dijkstra recommended 3 decades ago is better for 
evolving a good design than test driven development.

> Regards,
> 
> Robert Oliver
> 

best,
Costin


0
c_cozianu (217)
11/20/2003 5:18:47 AM
On Wed, 19 Nov 2003 11:22:10 -0800, Costin Cozianu wrote:

> The spec is what I posted initially, and I stand by it, ok, I admit to 
> one inaccuracy, I should replace "the longest increasing subsequence" 
> with "any longest increasing subsequence". Bad English.
> 

"any longest non-decreasing subsequence"
0
droby (77)
11/20/2003 10:34:59 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bphito$1ns1m9$1@ID-152540.news.uni-berlin.de...
> Robert Oliver wrote:
<snip>
> > Well, I've spent a fair amount of time testing Radar systems.  We were
> > required to write tests from the specification years before the product
was
> > delivered.  Different? Somewhat.  But the basic principle was, "if you
can't
> > test the requirement, it's not really required."   Many important tests
can
> > be written first.
> >
Let me be clear.  We did not insist that "testing" alone verify
requirements.  Our so-called traceability matrix included methods of
verification like inspection and analysis in addition to testing.
>
> Agreed. There are cases and cases. Other tests cannot be written first.
> Like in this instance. Having a solution of my own, I can produce tons
> of random samples and their solutions, quite easily. But without having
> the solution, you can only handpick a few samples, and even that is
> error prone.

Where did your solution come from?
Who programmed it?
How can you be sure it is correct?
Even if the algorithm it uses is correct, is the implementation correct?

Yes indeed, I do not have the solution yet.  I am interested in exploring
how solutions are developed using TDD.  I'm trying to pay attention to the
insight I get from following the process and watch the algorithm develop.
Knowing the solution ahead of time is not helpful in this effort.  On the
job?  Well that's another story...

> Many tests can be written first, indeed, starting from requirements
> (although Uncle Bob, Phlip et. comp. claimed that requirements *should*
> be written as executable tests). Other tests involved individual modules
> that do not have a requirements document.

Do you agree that there is some value in expressing a requirement as an
executable test?

> Yet other requirements can be proved to be met. Not that I write any of
> these proofs, of course, it is too tedious yet, but it is my
> responsibility that I should be able to write the proof should anybody
> ask. And trying to render the program provable can lead to very clean
> designs, much more than trying to write tests. That's why proof driven
> development, what Dijkstra recommended 3 decades ago is better for
> evolving a good design than test driven development.

(Anybody know a good emoticon for "biting ones tongue"?)

The process you seem to be advocating is "too tedious" to use yet, but
better than TDD?  It doesn't seem to be much help.

Regards,

Robert Oliver


0
11/20/2003 11:30:11 AM
Robert Oliver wrote:
> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
> news:bphito$1ns1m9$1@ID-152540.news.uni-berlin.de...
> 
>>Robert Oliver wrote:
> 
> <snip>
> 
>>>Well, I've spent a fair amount of time testing Radar systems.  We were
>>>required to write tests from the specification years before the product
> 
> was
> 
>>>delivered.  Different? Somewhat.  But the basic principle was, "if you
> 
> can't
> 
>>>test the requirement, it's not really required."   Many important tests
> 
> can
> 
>>>be written first.
>>>
> 
> Let me be clear.  We did not insist that "testing" alone verify
> requirements.  Our so-called traceability matrix included methods of
> verification like inspection and analysis in addition to testing.
> 

Agreed.

>>Agreed. There are cases and cases. Other tests cannot be written first.
>>Like in this instance. Having a solution of my own, I can produce tons
>>of random samples and their solutions, quite easily. But without having
>>the solution, you can only handpick a few samples, and even that is
>>error prone.
> 
> 
> Where did your solution come from?

 From thinking. The exercised came from a book , long time ago, in the 
way it was discussed originally, only the maximum length was required.

> Who programmed it?

I did.

> How can you be sure it is correct?

Proof obligation.

> Even if the algorithm it uses is correct, is the implementation correct?
> 

Idem.

> Yes indeed, I do not have the solution yet.  I am interested in exploring
> how solutions are developed using TDD.  I'm trying to pay attention to the
> insight I get from following the process and watch the algorithm develop.

Now the point is that 3 decades ago Dijkstra claimed that in order to 
come up with algorithm designs is to search for a proof. He mentions 
that in his "memoirs" of OS design: they were trying to solve the mutual 
exclusion problem (now we take it for granted from the OS, but they were 
  writing the OS), and they weren't making much headway, until Dijkstra 
unhappy that he had to analyze all kinds of bogus solutions let his 
colleagues know that he wouldn't accept any more solutions unless 
acompanied by a proof. Then in a few hours one of his coleagues, Dekker 
came both with solution and the proof, because he was looking for how to 
make thing s provable.

This methodology is later documented in books like "A Discipline of 
Programming" Dijkstra 76, "The Science of Programming" Gries 80, "A 
Method Of Programming" by Dijkstra, Sterringa and Feijen. And in many 
short articles by Dijkstra all on non-trivial programming problems and 
all much shorter than our friend Ron Jeffries is spending on trivial things.

People seem to have forgotten these things, or if they are young they 
never learnt them.

> Knowing the solution ahead of time is not helpful in this effort.  On the
> job?  Well that's another story...
> 

On the job, I sometimes face non-trivial problems. But most of the times 
I can find analogies with something written by somebody esle, someplace 
else. Even if you know a solution to a similar problem, TDD is highly 
unlikely to get you anywhere for the hard nuts you have to crack.

> 
>>Many tests can be written first, indeed, starting from requirements
>>(although Uncle Bob, Phlip et. comp. claimed that requirements *should*
>>be written as executable tests). Other tests involved individual modules
>>that do not have a requirements document.
> 
> 
> Do you agree that there is some value in expressing a requirement as an
> executable test?
> 

I am very doubtful. They'd have to be expressed first as a combination 
of unambiguous english and light formalism (equations for example). I'd 
be worried that getting your customers to sign off requirements as code 
is inviting trouble.

After the reuirements are defined, yes there is value in writing the 
executable tests, but I guess not before that.

> 
>>Yet other requirements can be proved to be met. Not that I write any of
>>these proofs, of course, it is too tedious yet, but it is my
>>responsibility that I should be able to write the proof should anybody
>>ask. And trying to render the program provable can lead to very clean
>>designs, much more than trying to write tests. That's why proof driven
>>development, what Dijkstra recommended 3 decades ago is better for
>>evolving a good design than test driven development.
> 
> 
> (Anybody know a good emoticon for "biting ones tongue"?)
> 
> The process you seem to be advocating is "too tedious" to use yet, but
> better than TDD?  It doesn't seem to be much help.
> 

It is too "tedious" to do it formally (using proof tools). But you can 
still do it informally. I am hopeful that in maybe 2 decades we'll have 
good enough tools to make formality feasible. Trying to do it informally 
with pen and paper, still allows you to get to good design faster and 
better than with the latest refactoring wizard in Eclipse.

> Regards,
> 
> Robert Oliver
> 
> 


Best,
Costin

0
c_cozianu (217)
11/20/2003 3:23:55 PM
Costin,

First, let me apologize for snipping all the preceeding text.  My news
reader doesn't send replys with too much original text.

If I understand the thread so far you advocate the development of software
(at least the algorithm) by proof.
Yet, proof is too tedious in many (most?) cases.
Perhaps in two decades the tools will be available for a formal proof, until
then informal proofs can be used.

When you don't have a preexisting solution to the problem and the proof is
too tedious, what do you do?  For me, this is what mostly every interesting
program looks like.

I don't have much experience in trying to prove my software correct.  Would
it be possible to post your proof for this problem?  It would help me better
understand what you are saying.

Finally, it seems to me that TDD and searching for a proof are somewhat
orthogonal.  Couldn't a TDD practitioner use proofs and mathematical
techniques to advantage in the same way others do?

Regards,
Robert Oliver

Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpimca$1p243i$1@ID-152540.news.uni-berlin.de...


--
Opportunity is missed by most people because it is dressed in overalls and
looks like work. - T. A. Edison









0
oliverb (5)
11/21/2003 3:23:38 PM
Universe wrote:

>>"It is not the strongest of the species that survive, nor the most
>>intelligent, but the one most responsive to change." --- Charles
> 
> Darwin
> 
> I.)
> 
> Just a few words, and to begin, I don't  really disagree with this.

<snip>

You are responding to someone's .sig???

Firstly, why is it necessary to respond to a comment in a .sig?

Second, with all due respect, why are you telling us your opinion on 
Darwin's theories? (interesting though they are, and I was all set to 
respond to them until I realised how OT it all is).

Finally, I am sure there are plenty of other groups where this topic is 
discussed fervently and where your post would be welcomed (or flamed - 
I'm sure there are creationist groups out there too!).



0
11/21/2003 3:56:45 PM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message
news:bpgm3f$1ockuv$1@ID-152540.news.uni-berlin.de...
> Have you tried with [1,2,1,1]  ?

I'll be back after the holidays...


John


0
jelrick (16)
11/22/2003 6:04:34 PM
Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
written this on (or about)  Wed, 19 Nov 2003 11:40:03 -0800, :

>Uncle Bob (Robert C. Martin) wrote:
>
>> Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
>> written this on (or about)  Mon, 17 Nov 2003 21:27:31 -0800, :
>> 
>> 
>>>>  f([1,2,3,1,2,3,1,2,3]) -> ?
>>>
>>>f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])
>> 
>> 
>> Actually there are six solutions:
>> 0,1,2,5,8,
>> 0,1,4,5,8,
>> 0,1,4,7,8,
>> 0,3,4,5,8,
>> 0,3,4,7,8,
>> 0,3,6,7,8,
>> 
>
>Of course, I wouldn't even bother to enumerate all of them. The problem 
>is not about enumerating all solutions.
>
>Added bonus if one can prove the the number of possible solutions is in 
>the worst case exponential. 

That's easy.  Each digit may be present or not.  If there are N digits
there are 2^N possibilities.  So at least the search tree is 2**N
>
>But I'm curious where the famous TDDers, Unit Tester have all gone ? I'd 
>have expected for such a trivial problem the tests were ready by now.

It's an interesting problem.  I wrote up the test cases in a hotel in
amsterdam; and got the initial 2**N algorithm working one evening
after giving a talk at a trade show.  I managed to find a way to prune
the search tree while on a train from Eindhoven to Schipol.  I think I
see a few more prunings.  The test cases help me to make sure that my
prunings haven't broken anything.  I've had a busy weekend (a good
friend died), and I have a class to teach this week, and thanksgiving
to attend, and articles to write and -- well -- it may be another few
weeks before I can take the time.  It's not real high on my priority
list.  But it is a fun problem!


Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
11/23/2003 3:51:33 AM
Uncle Bob (Robert C. Martin) wrote:
> Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
> written this on (or about)  Wed, 19 Nov 2003 11:40:03 -0800, :
> 
> 
>>Uncle Bob (Robert C. Martin) wrote:
>>
>>
>>>Costin Cozianu <c_cozianu@hotmail.com> might (or might not) have
>>>written this on (or about)  Mon, 17 Nov 2003 21:27:31 -0800, :
>>>
>>>
>>>
>>>>> f([1,2,3,1,2,3,1,2,3]) -> ?
>>>>
>>>>f([1,2,3,1,2,3,1,2,3]) -> [0,1,4,7,8] ( also [0,1,2,5,8])
>>>
>>>
>>>Actually there are six solutions:
>>>0,1,2,5,8,
>>>0,1,4,5,8,
>>>0,1,4,7,8,
>>>0,3,4,5,8,
>>>0,3,4,7,8,
>>>0,3,6,7,8,
>>>
>>
>>Of course, I wouldn't even bother to enumerate all of them. The problem 
>>is not about enumerating all solutions.
>>
>>Added bonus if one can prove the the number of possible solutions is in 
>>the worst case exponential. 
> 
> 
> That's easy.  Each digit may be present or not.  If there are N digits
> there are 2^N possibilities.  So at least the search tree is 2**N
> 

This proves that the space to search with the dummy algorithm is 2**N.

What I was saying is that the space of real solutions can be 
exponential. So a program who will output all the solutions possible may 
run in exponential time.

0
c_cozianu (217)
11/23/2003 4:13:08 AM
Robert C. Martin wrote:

> ... on a train from Eindhoven to Schipol.

Nice airport.  Amsterdam is a nice place to "entrez-vous" the Continent.  I
like the vibe of Scandanavia and to me the Nederlands is "South
Scandanavia."

A salient feature of Scandanavia to me is how the intellectual
superstructure, not as wedded to the Pragmatist philosophy outlook of
Pierce, Dewey, James, etc., provided the nifty greenhouse fertilization for
maturation of the "world is collaborating objects" world view.

Next of course was the subsequent introduction of that viewpoint into
software engineering by the Norwegian Scandanavians Nygaard & Dahl.  At
their web sites they stated that they had reached the limits of conceptually
understanding and then handling use cases that were to assist a Norwegian
shipping firms in routing and scheduling their ships.  The system
requirements scope could be said to be "NP++".

It was  a good thing Nygaard & Dahl did not base themselves upon what was
popular, or in demand, but upon the objective truth as it was developing in
software engineering at the time.

Elliott
--
      Substituting Refactoring For Analysis Modelling and Review
            When Facing Major High Level Design Problems
                 Is Like Drawing a Shade When Totally Dark
--
                      *~* Theory Leads, Practice Verifies *~*
     *~*  Global Plans + IID (Iterative & Incremental Development) *~*
--
Though types only make sense relative to context, system worthwhile types
are *objectively* significant for the relative context.
--
Tis Season For All Reason, to be Laffing OO the Way!!!!!!!   :- }






0
universe2 (613)
11/23/2003 4:40:38 AM
John Elrick wrote:

>> When you have mathematics vs. words you should have trusted the math.
>
> <g>Never...I've worked too long.  See below...

I once saw an algebraic specification for a List, which besides other things
(indirectly) specified that true == false. I decided to trust the word
"List" more than the algebraic specification and assumed that there was a
little error in the math part. Possibly I was wrong... ;)

Take care, Ilja


0
preuss (368)
11/24/2003 9:25:23 AM
Costin Cozianu <c_cozianu@hotmail.com> wrote in message news:<bppc6l$1qlbbh$1@ID-152540.news.uni-berlin.de>...

> Uncle Bob (Robert C. Martin) wrote:

> > That's easy.  Each digit may be present or not.  If there are N digits
> > there are 2^N possibilities.  So at least the search tree is 2**N
 
> This proves that the space to search with the dummy algorithm is 2**N.

> What I was saying is that the space of real solutions can be 
> exponential. So a program who will output all the solutions possible may 
> run in exponential time.

It is quite simple to prove what the lower/upper bounds of the search
space are (where "bound" = length of valid subsequence) for any given
sequence. For example :

1. [123123123] : lower = 5, upper = 7
2. [1211] : lower = 2, upper = 3
3. [1000,999,998, ... , 1, 0] : lower = 1, upper = 1
4. [1000,999,998, ... , 1, 900] : lower = 2, upper = 2

The bounds can be computed with at worst O(2N) comparisons.

If someone can use examples 1 and 2 to derive a corresponding worst case
"big Oh" for an algorithm that searches all combinations within any
given bounds, Santa will no doubt reward you next month (I am too lazy)
.... :-)


Regards,
Steven Perryman
0
ggroups5 (201)
11/24/2003 11:20:02 AM
"Costin Cozianu" <c_cozianu@hotmail.com> wrote in message news:bph64m$1msum1$1@ID-152540.news.uni-berlin.de...
> I also don't write tests for trivial things. And I have a QA department
> to save my ass :) In this case, I'd have a huge amount of random data
> with solutions and let your algorithm run overnight.

I find this a very inconsistent standoint -  your entire argument upto this point
is predicated on the idea that more direct use of knowledge of the actual solution
itself is better (a proof being the most extreme example of the direct use of
of the solution logic). Yet now you turn around and say that the technique
which involves the *least* knowledge of the actual solution is better than
one that uses more ??.

Paul C.


0
11/24/2003 1:08:36 PM
Paul Campbell wrote:
> "Costin Cozianu" <c_cozianu@hotmail.com> wrote in message news:bph64m$1msum1$1@ID-152540.news.uni-berlin.de...
> 
>>I also don't write tests for trivial things. And I have a QA department
>>to save my ass :) In this case, I'd have a huge amount of random data
>>with solutions and let your algorithm run overnight.
> 
> 
> I find this a very inconsistent standoint -  your entire argument upto this point
> is predicated on the idea that more direct use of knowledge of the actual solution
> itself is better (a proof being the most extreme example of the direct use of
> of the solution logic). Yet now you turn around and say that the technique
> which involves the *least* knowledge of the actual solution is better than
> one that uses more ??.
> 

I'm not saying which one is better, they serve different purposes. It is 
always better to have an independent QA department because they will 
test including the things that you don't test. It is an indepenedent 
verification and independence in itself has a high value.

On the other hand,some of the tests QA department runs are either 
written by me or I contributed to them substantially. It works both ways.

But in some case cases such as the problem in discussion a reallistic 
test cannot be constructed at all without knowing a good solution.


Costin

0
c_cozianu (217)
11/24/2003 5:58:00 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
> Adventures in C#: The Bowling Game
> Ron Jeffries 
> 11/17/2003 
> 
>   When I demonstrate Test-Driven Development using the Bowling Game

Stumbled across "The Bowling Game"
http://www.xprogramming.com/xpmag/acsBowling.htm and happily "The
Story" is long enough that I didn't reach "The Design Idea" before I
decided to look for a solution. A back of the envelope, BUFD solution.

Must have seen bowling sometime in a movie, but I've never played and
didn't know the rules - I wasn't sure if "rolls" and "tries" and
"balls" were the same thing.


1) figure out what the problem is, what a solution would look like...
I scribbled various representations while trying to figure out what
the problem was:

- a list of score pairs [ (2,5) (5,6) (0,0) ...]
   but then I noticed that sometimes there was just one roll in a
'strike'

- a list of lists [ [10] [2,5] [5,6] ...]
   and noticed that we always had 10 things, so we could also do

- an array of lists { [10] [2,5] [5,6] ... }
   and then wondered what we were trying to find out...
   Oh - "the *total* score for the game" 

- a "sequence of rolls" [10, 2, 5, 5, 6 ...]
   can we simply accumulate the score?


2) case analysis
So if we take the "rolls" one at a time can we figure out what should
be added to the total?

   loop 
      total = total + ?
   end

   if pins = 10 then ...

   if pins = 10 then 
      total = total + pins + next2

OK

   else if pins1 + pins2 = 10 then 
      total = total + pins1 + pins2 + next1

OK

   else 
      total = total + pins1 + pins2    

OK, what does that look like for a sequence of rolls?

  if p(i) = 10 then 
      t = t + p(i) + ( p(i+1) + p(i+2) )

   else if p(i) + p(i+1) = 10 then 
      t = t + p(i) + p(i+1) + ( p(i+2) )

   else 
      t = t + p(i) + p(i+1)      

OK, do we need to check the index bounds everytime? 
Whatever! We do need to increment the index so

  if p(i) = 10 then 
      t = t + p(i) + ( p(i+1) + p(i+2) )
      i = i + 1

   else if p(i) + p(i+1) = 10 then 
      t = t + p(i) + p(i+1) + ( p(i+2) )

Oh! we skip over a "roll" - that's the difference between these cases!

   else if p(i) + p(i+1) = 10 then 
      t = t + p(i) + p(i+1) + ( p(i+2) )
      i = i + 2


3) iteration
How do we boundary check the index i?
We could use the size of the "sequence of rolls" and check explicitly?
We know that there are always 10 things - should we just count them
and halt after the last thing is processed?

while frames < 10
  if p(i) = 10 then 
      t = t + p(i) + ( p(i+1) + p(i+2) )
      i = i + 1
      frames = frames + 1
   else if p(i) + p(i+1) = 10 then 
      t = t + p(i) + p(i+1) + ( p(i+2) )
      i = i + 2
      frames = frames + 1
   else 
      t = t + p(i) + p(i+1)
      i = i + 2
      frames = frames + 1

Does that work? Need to look at the rules again!
Seems to - the game adds bonus balls so that scoring the tenth frame
is the same as scoring any of the other frames. For "a *valid*
sequence of rolls" we don't need to check the index bounds.


4) code (Nice http://nice.sourceforge.net/index.html )

void main(String[] args){  
   var frames = 0;
   var total = 0;
   var i = 0;

   let int[] pins = [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0,
0,0];

   while (frames<10){
      if (pins[i] == 10) {
         total += pins[i] + pins[i+1] + pins[i+2];
         i++;
      } 
      else if (pins[i] + pins[i+1] == 10) { 
         total += pins[i] + pins[i+1] + pins[i+2];
         i += 2;
      }
      else {
         total += pins[i] + pins[i+1];
         i += 2;
      }
      frames++;
   }

   println(total);
}


5) test
Silly. Went through testing changing the values in pins[] - must be
late.
Re-write to separate out the test values.
Pity not to have used a Unit test framework ;-)

int score(int[] pins){
   var frames = 0;
   var total = 0;
   var i = 0;

   while (frames<10){
      if (pins[i] == 10) {
         total += pins[i] + pins[i+1] + pins[i+2];
         i++;
      } 
      else if (pins[i] + pins[i+1] == 10) { 
         total += pins[i] + pins[i+1] + pins[i+2];
         i += 2;
      }
      else {
         total += pins[i] + pins[i+1];
         i += 2;
      }
      frames++;
   }
   return total; 
}


void main(String[] args){  
   let int[][] tests = [
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3],
         [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10, 5,3],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6, 5],
         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
         [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10]
      ];

   for(each:tests) println( score(each) );
}


6) observations
#1 #2 #3 #4 an hour, #5 an hour.

We've coded as though "sequence of rolls" is indexed - what would it
be like if we go back to #3 and get rid of the indices?

Representation is everything! Once we see that a bowling game can be
represented as a simple sequence of "the number of pins knocked down"
on each roll, encoding the game rules becomes straightforward. (I
wonder what ye olde JSP design would look like?)

Reducing the cost of testing matters: test framework == good thing


7) conclusions 
"But think about it. Less up-front design, and a better result. Could
it be true?"
http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm

The difference isn't "Less up-front design" it's "Less OO design"!

And, of course, it rather depends what we mean by "a better result". 


best wishes, Isaac
0
igouy (1009)
11/24/2003 7:07:58 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
> Adventures in C#: The Bowling Game
> Ron Jeffries 
> 11/17/2003 
> 
>   When I demonstrate Test-Driven Development using the Bowling Game

Following on from BUFD version of the Bowling Game (given that we've
figured out the solution as far as #3) a language with pattern
matching and list functions would allow a recursive solution (compare
to #4) like this:

   Start = score [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10,
5,3] 0 0
	
   score pins total 10 = total 

   score [x,y] total frame = total + x + y
	
   score [x,y,z:rest] total frame
      | x == 10    = score [y,z:rest] (total+x+y+z) (frame+1)
      | x+y == 10  = score [z:rest] (total+x+y+z) (frame+1)
      | otherwise  = score [z:rest] (total+x+y) (frame+1)


To explain:
- if we've done 10 frames we're finished
- if there are only 2 items we're finished (main clause needs 3 items
to match)
- 3 rules for calculating the score 

best wishes, Isaac
0
igouy (1009)
11/24/2003 9:50:14 PM
The TDD used in the example isn't as rigorous as I thought it would be.

I thought you only added code if a test requires it, and you then refactor
to *reduce* complexity. However, near the start of the example, you have the
code correctly calculating the score for 10*(3,3) frames by a simple summing
algorithm. You then refactor adding the OpenFrame class. This class is added
in anticipation of future need, making the current design more complicated
than the current tests require.

Also there are no tests that solely test the IFrame derived classes. I
imagined that in TDD every class should have their own tests.

John


"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
news:l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com...
> Adventures in C#: The Bowling Game
> Ron Jeffries
> 11/17/2003
>
>   When I demonstrate Test-Driven Development using the Bowling Game
>   example, I begin by describing the problem and inviting the attendees to
>   do a little up front design about what objcts we may need. Then I take a
>   very simple approach that produces a rather simple single-class
solution,
>   with none of the complexity we anticipated. I've been wondering how to
>   drive the development to cause the creation of some of the classes that
>   are anticipated, supposing that we might have some actual need for them.
>   Here's an example of doing TDD with a bit bigger "design" in mind.
>
>   http://www.xprogramming.com/xpmag/acsBowling.htm
>
> Ron Jeffries
> www.XProgramming.com
> Just because we learned something new today doesn't mean we were
> frickin' idiots yesterday.  -- Chris Morris, possibly paraphrasing
someone.
>
> -- 
> Ronald E Jeffries
> http://www.XProgramming.com
> http://www.objectmentor.com
> I'm giving the best advice I have. You get to decide whether it's true for
you.


0
11/25/2003 9:18:16 AM
On 24 Nov 2003 13:50:14 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Following on from BUFD version of the Bowling Game

Neat examples, Isaac ... though I don't understand why you called the first one
BUFD ... it seemed like you only designed for a few moments before turning to
code.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/25/2003 11:50:41 AM
On Tue, 25 Nov 2003 09:18:16 -0000, "John W. Wilkinson"
<john.wilkinson@spirentcom_removethis.com> wrote:

>The TDD used in the example isn't as rigorous as I thought it would be.
>
>I thought you only added code if a test requires it, and you then refactor
>to *reduce* complexity. 

Ideally I would only add code in response to a broken test.

However, we refactor to improve design, not just to reduce complexity. Basically
there are two ways to go, roughly like this:

1. We put in some code that is too complex and then rearrange the code to remove
it. This is my preferred way of working, for some reason having to do with
showing that it is always possible to get out of a hole easily or something like
that.

2. We are about to put in some code, but we see that it will be complex to put
it in. So we refactor to "make a place" to put the code.

Both are "approved practices". I do not know at this time how I decide which one
to do.

>However, near the start of the example, you have the
>code correctly calculating the score for 10*(3,3) frames by a simple summing
>algorithm. You then refactor adding the OpenFrame class. This class is added
>in anticipation of future need, making the current design more complicated
>than the current tests require.

Yes. I have a change in mind, and am making the code have a place for it. That
is definitely anticipatory.
>
>Also there are no tests that solely test the IFrame derived classes. I
>imagined that in TDD every class should have their own tests.

It can be argued that before classes are released into the wild they should have
their own tests. It often turns out, using TDD, that those tests do not
naturally arise. A "better" solution in this case might be to make those classes
private or package protected, so that they never go out on their own and get
into trouble.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
11/25/2003 11:55:20 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<cfg6svkvigf16srdar0o77m8f7u6tj282n@4ax.com>...
> On 24 Nov 2003 13:50:14 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
> 
> >Following on from BUFD version of the Bowling Game
> 
> Neat examples, Isaac ... though I don't understand why you called the first one
> BUFD ... it seemed like you only designed for a few moments before turning to
> code.

Depends what you mean by "turning to code" ;-)

I spent most of an hour before I wrote in some specific programming
language and tried to get it to compile.

#1 was exploring the problem 
#2 was writing down what the game rules were (in subscript notation)
#3 putting the rules into a terminating algorithm

#4 coding the algorithm

1-3 seems to be analysis and design
4   seems to be coding

The second example is BFUD as-well, it just uses the same analysis and
design and implements without using explicit indexing, which gives IMO
a clearer solution.

What do you mean by BFUD?

best wishes, Isaac
0
igouy (1009)
11/26/2003 8:02:22 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
> Adventures in C#: The Bowling Game
> Ron Jeffries 
> 11/17/2003 
> 
>   When I demonstrate Test-Driven Development using the Bowling Game
>   example, I begin by describing the problem and inviting the attendees to
>   do a little up front design about what objcts we may need. Then I take a
>   very simple approach that produces a rather simple single-class solution,
>   with none of the complexity we anticipated. I've been wondering how to
>   drive the development to cause the creation of some of the classes that
>   are anticipated, supposing that we might have some actual need for them.
>   Here's an example of doing TDD with a bit bigger "design" in mind.
> 
>   http://www.xprogramming.com/xpmag/acsBowling.htm


"But think about it. Less up-front design, and a better result. Could
it be true?"
http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm

Seems to me that the difference isn't "Less up-front design" it's "Less OO design"!

And, of course, it rather depends what we mean by "a better result".

best wishes, Isaac
0
igouy (1009)
11/26/2003 8:05:37 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0311261205.6802ee1c@posting.google.com>...
> Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
> > Adventures in C#: The Bowling Game
> > Ron Jeffries 
> > 11/17/2003 
> > 
> >   When I demonstrate Test-Driven Development using the Bowling Game
> >   example, I begin by describing the problem and inviting the attendees to
> >   do a little up front design about what objcts we may need. Then I take a
> >   very simple approach that produces a rather simple single-class solution,
> >   with none of the complexity we anticipated. 

> Seems to me that the difference isn't "Less up-front design" it's "Less OO design"!

Presuming we wanted an OO solution to the Bowling Game, the only
obvious responsibility is that something has to calculate the total
score for a game, and of the candidate objects the one we definitely
need is a BowlingGame.

The Nice OO solution requires a little more code than the procedural
solution, but not 5 times more code. Seems like the BUFD solution
given in http://www.xprogramming.com/xpmag/acsBowling.htm is a
strawman, loaded with detail that has no relevance to the problem.

(Frames hinder rather than help, because the score for a ball involve
pin-scores from 3 frames.)


class BowlingGame {
   int[] balls;

   int score(){
      var frames = 0;
      var total = 0;
      var i = 0;

      while (frames<10){
         if (balls[i] == 10) {
            total += balls[i] + balls[i+1] + balls[i+2];
            i++;
         } 
         else if (balls[i] + balls[i+1] == 10) { 
            total += balls[i] + balls[i+1] + balls[i+2];
            i += 2;
         }
         else {
            total += balls[i] + balls[i+1];
            i += 2;
         }
         frames++;
      }
      return total; 
   }
}


void main(String[] args){  
   let BowlingGame[] testGames = [
      new BowlingGame(
         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
      new BowlingGame(
         balls: [3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3]),
      new BowlingGame(
         balls: [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
      new BowlingGame(
         balls: [4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
      new BowlingGame(
         balls: [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
      new BowlingGame(
         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10,
5,3]),
      new BowlingGame(
         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6,
5]),
      new BowlingGame(
         balls: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]),
      new BowlingGame(
         balls: [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10])
      ];

   for(each:testGames) println( each.score() );
}


best wishes, Isaac
0
igouy (1009)
11/29/2003 3:53:02 PM
igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
(or about)  29 Nov 2003 07:53:02 -0800, :

>igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0311261205.6802ee1c@posting.google.com>...
>> Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
>> > Adventures in C#: The Bowling Game
>> > Ron Jeffries 
>> > 11/17/2003 
>> > 
>> >   When I demonstrate Test-Driven Development using the Bowling Game
>> >   example, I begin by describing the problem and inviting the attendees to
>> >   do a little up front design about what objcts we may need. Then I take a
>> >   very simple approach that produces a rather simple single-class solution,
>> >   with none of the complexity we anticipated. 
>
>> Seems to me that the difference isn't "Less up-front design" it's "Less OO design"!
>
>Presuming we wanted an OO solution to the Bowling Game, the only
>obvious responsibility is that something has to calculate the total
>score for a game, and of the candidate objects the one we definitely
>need is a BowlingGame.
>
>The Nice OO solution requires a little more code than the procedural
>solution, but not 5 times more code. 

The solution below doesn't seem to have any OO characteristics.  The
fact that you've got some variables and function in a class does not
seem any different than having some variables and functions in a
program without a class.  

>Seems like the BUFD solution
>given in http://www.xprogramming.com/xpmag/acsBowling.htm is a
>strawman, loaded with detail that has no relevance to the problem.

I agree with the last clause, but not the strawman.  I use this
problem a *lot* when teaching classes; and the vast majority of BUFD
solutions involve frames.  Specifically something like this:

                   +-+ next
                   | V
    |Game|------>|Frame|---------->|Ball|
             10     A          1..2   ^ 1
                    |                 |
                |TenthFrame|----------+

I posted this problem on the net a little over two years ago.  Back
then there was an outcry against the solution *because* it did not
contain a Frame class.  Indeed, someone posted a frame based program
that was five times larger than the procedural solution and argued
that it was superior.

So, though the frame based solution is clearly inefficient and a poor
match for the problem, it isn't a strawman.

>
>class BowlingGame {
>   int[] balls;
>
>   int score(){
>      var frames = 0;
>      var total = 0;
>      var i = 0;
>
>      while (frames<10){
>         if (balls[i] == 10) {
>            total += balls[i] + balls[i+1] + balls[i+2];
>            i++;
>         } 
>         else if (balls[i] + balls[i+1] == 10) { 
>            total += balls[i] + balls[i+1] + balls[i+2];
>            i += 2;
>         }
>         else {
>            total += balls[i] + balls[i+1];
>            i += 2;
>         }
>         frames++;
>      }
>      return total; 
>   }
>}
>
>
>void main(String[] args){  
>   let BowlingGame[] testGames = [
>      new BowlingGame(
>         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
>      new BowlingGame(
>         balls: [3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3]),
>      new BowlingGame(
>         balls: [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
>      new BowlingGame(
>         balls: [4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
>      new BowlingGame(
>         balls: [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0]),
>      new BowlingGame(
>         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10,
>5,3]),
>      new BowlingGame(
>         balls: [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6,
>5]),
>      new BowlingGame(
>         balls: [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]),
>      new BowlingGame(
>         balls: [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10])
>      ];
>
>   for(each:testGames) println( each.score() );
>}
>
>
>best wishes, Isaac

Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
11/29/2003 6:14:34 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<h3ohsvo0dkmddbtb4onbgs1odbo9uft2pa@4ax.com>...
> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
> (or about)  29 Nov 2003 07:53:02 -0800, :

> >The Nice OO solution requires a little more code than the procedural
> >solution, but not 5 times more code. 
> 
> The solution below doesn't seem to have any OO characteristics.  The
> fact that you've got some variables and function in a class does not
> seem any different than having some variables and functions in a
> program without a class.  

In the other solutions, the score function could be applied to any
int[] (or in the pure functional implementation any list of Int). In
this solution the score method is sent to a BowlingGame object.
Isn't that a fundamental OO characteristic - structuring behaviour
around objects?

(For encapsulation, we could move the intialization out of the
constructor, initialize to some empty collection and have an instance
method 'balls:' - there's nothing about the solution that requires an
array)

The solution is trivial - how many OO characteristics should we
expect?


> >Seems like the BUFD solution
> >given in http://www.xprogramming.com/xpmag/acsBowling.htm is a
> >strawman, loaded with detail that has no relevance to the problem.
> 
> I agree with the last clause, but not the strawman.  I use this
> problem a *lot* when teaching classes; and the vast majority of BUFD
> solutions involve frames.  Specifically something like this:
> 
>                    +-+ next
>                    | V
>     |Game|------>|Frame|---------->|Ball|
>              10     A          1..2   ^ 1
>                     |                 |
>                 |TenthFrame|----------+
> 
> I posted this problem on the net a little over two years ago.  Back
> then there was an outcry against the solution *because* it did not
> contain a Frame class.  Indeed, someone posted a frame based program
> that was five times larger than the procedural solution and argued
> that it was superior.

Did you agree with them that it was superior ;-)

> So, though the frame based solution is clearly inefficient and a poor
> match for the problem, it isn't a strawman.

When someone in a class comes up with a solution that is clearly
inefficient and a poor match for the problem, we guide them to a
better solution.

When someone who already knows that this is "clearly inefficient and a
poor match for the problem" uses it as an example of BUFD, it's a
strawman.

And in the hope of avoiding misunderstanding: What do you mean by
BUFD?

best wishes, Isaac
0
igouy (1009)
11/30/2003 11:27:00 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote
in message

> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on

> >igouy@yahoo.com (Isaac Gouy) wrote in message

> >> Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message
> >> > Adventures in C#: The Bowling Game
> >> > Ron Jeffries
> >> > 11/17/2003
> >> >
> >> >   When I demonstrate Test-Driven Development using the Bowling
Game
> >> >   example, I begin by describing the problem and inviting the
attendees to
> >> >   do a little up front design about what objcts we may need. Then
I take a
> >> >   very simple approach that produces a rather simple single-class
solution,
> >> >   with none of the complexity we anticipated.

> >> Seems to me that the difference isn't "Less up-front design" it's
"Less OO design"!

Right!

> >Seems like the BUFD solution
> >given in http://www.xprogramming.com/xpmag/acsBowling.htm is a
> >strawman, loaded with detail that has no relevance to the problem.

And that is the  CHIEF  problem with the compare a so-called 'budf'
design against a tdd xp/allie design charades put on by those of
xp/allie persuasion.

These comparisons have no objectively validated, analysis of the domain,
use cases and resources available to the project.

I.e. These comparisons are typically exercises in unscientific xp/allie
wish fulfillment much more than anything else.

Elliott
-- 
      Substituting Refactoring For Analysis Modelling and Review
            When Facing Major High Level Design Problems
                 Is Like Drawing a Shade When Totally Dark
-- 
                      *~* Theory Leads, Practice Verifies *~*
     *~*  Global Plans + IID (Iterative & Incremental Development) *~*



0
universe2 (613)
12/1/2003 8:53:19 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<nu0jrvgoku03escn2o9lus0o8nkl5bho62@4ax.com>...
> On Mon, 17 Nov 2003 16:31:05 -0600, "Uncle Bob (Robert C. Martin)"
> <u.n.c.l.e.b.o.b@objectmentor.com> wrote:

Now that I've looked at the code
   http://www.xprogramming.com/xpmag/acsBowling.htm 
three 'bad things' stand-out:

1) as Robert C. Martin mentioned

> >One complaint I have is that the tests are doing the parsing of
> >whether a frame is Open, Spare, Strike, or Bonus.  It seems to me that
> >this should be in the BowlingGame class itself.  The tests shouldn't
> >know about whether a throw is a strike, spare, open, or bonus.  The
> >tests should just know which balls where thrown IMHO.
> 
> I think of it as the interface to BowlingGame is coming from the playing 
> of the game itself. The players know whether they bowled a strike, spare, 
> open frame, and so on, and they say so. 
> 
> It would certainly be possible to do the parsing readily enough, but my
> intuition said (and I bet you'll agree) that by the time we parse, we 
> might as well just add up the score rather than create all those cute 
> objects.

The problem description said "given a valid sequence of rolls for one
line of American Ten-Pin Bowling, produces the total score for the
game." The solution given doesn't solve that problem - it requires a
sequence of 'frames'.

There may be "no conditionals in the scoring code" but only because
"the parsing" has been pushed outside the problem scope!


2) From the problem description a game has 10 Frames, and bonus rolls
are part of the 10th frame; in the solution a game may have 12 Frames:
each BonusRoll is implemented as a Frame.

3) Frames share the 'throws' collection with Game (loss of
encapsulation - more code to update when the collection type is
changed)

best wishes, Isaac
0
igouy (1009)
12/2/2003 6:24:28 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0311261205.6802ee1c@posting.google.com>...

> Seems to me that the difference 
> isn't "Less up-front design" it's "Less OO design"!

Let me sharpen that: the difference is bad OO analysis and design ;-)

All the way back to the original article, frames get shoved into the
solution without giving any reason why they are necessary or useful to
the solution: "OK, clearly a game object consists of a sequence of ten
frames"
   http://www.objectmentor.com/resources/articles/xpepisode.htm

When we look at a bowling game scorecard (Figure 1 or better
http://www.bowling.uklinux.net/bowling/score.php3 ) we can see:
- a sequence of 'frames'
- a sequence of 'throws' (given as 'X' or '/' or the pin score)

So, while it's true that a game consists of a sequence of ten frames,
it's also true that a game consists of a sequence of throws. Why would
we assume that 'frames' will be a better representation than throws?

Here's an alternative OO BUFD

       throws
 |Game|------------->|Throw|
               11..21| pins|
                        |
                        A
                        |
              +---------+---------+
              |         |         |
          |Strike|    |Spare|   |Bonus|


Using Nice once more ( http://nice.sourceforge.net/index.html ) we
have a fairly straightforward solution:

   class BowlingGame {
      Throw[] throws;
      int i = 0;

      int score(){
         var total = 0;     
         for (i = 0; i < throws.size; i++)
            total += throws[i].score + throws[i].bonus(this);
         return total;      
      }
   
      int bonusOne() = throws[i+1].pinCount;
      int bonusTwo() = throws[i+2].pinCount;   
   }

   class Throw {
      int pins;

      int pinCount() = pins;
      int score() = pins;
      int bonus(BowlingGame game) = 0;
   }

   class Strike extends Throw {
      bonus(BowlingGame game) = game.bonusOne + game.bonusTwo;
   }

   class Spare extends Throw {
      bonus(BowlingGame game) = game.bonusOne;
   }

   class Bonus extends Throw {
      score() = 0;
   }


Seems cleaner than the 'frames' based solution doesn't it?

best wishes, Isaac



Like http://www.xprogramming.com/xpmag/acsBowling.htm we could assume
that the input is in-terms of Strike or Bonus throws rather than pin
counts.
Oh, let's do something directly comparable:

   void main(String[] args){  
      let BowlingGame[] testGames = [

         new BowlingGame(throws: manyOpenFrames(10,0,0)),
         new BowlingGame(throws: manyOpenFrames(10,3,3)),

         new BowlingGame(throws: 
            spareFrame(4,6) + 
            openFrame(3,5) + 
            manyOpenFrames(8,0,0)
            ),

         new BowlingGame(throws: 
            spareFrame(4,6) + 
            openFrame(5,3) + 
            manyOpenFrames(8,0,0)
            ),

         new BowlingGame(throws: 
            strikeFrame() + 
            openFrame(5,3) +
            manyOpenFrames(8,0,0)
            ),

         new BowlingGame(throws: 
            manyOpenFrames(9,0,0) + 
            strikeFrame() +
            bonusRoll(5) +
            bonusRoll(3)
            ),

         new BowlingGame(throws: 
            manyOpenFrames(9,0,0) +
            spareFrame(4,6) + 
            bonusRoll(5)
            ),

         new BowlingGame(throws: 
            strikeFrame() + strikeFrame() +
            strikeFrame() + strikeFrame() +
            strikeFrame() + strikeFrame() +
            strikeFrame() + strikeFrame() +
            strikeFrame() + strikeFrame() +         
            bonusRoll(10) +
            bonusRoll(10)
            ),

         new BowlingGame(throws: 
            strikeFrame() + spareFrame(4,6) + 
            strikeFrame() + spareFrame(4,6) + 
            strikeFrame() + spareFrame(4,6) + 
            strikeFrame() + spareFrame(4,6) + 
            strikeFrame() + spareFrame(4,6) + 
            bonusRoll(10)
            )        
         ];

      for(each:testGames) println( each.score() );
   }


   // utility functions for test cases

   Throw[] manyOpenFrames(int count, int firstThrow, int secondThrow)
{
      let throwCount = count*2;
      Throw[] t = cast(new Throw[throwCount]);
      for (int i = 0; i < throwCount; i+=2) {
         t[i] = new Throw(pins: firstThrow);
         t[i+1] = new Throw(pins: secondThrow);
      }
      return t;
   }

   Throw[] openFrame(int firstThrow, int secondThrow) {
      return [ new Throw(pins: firstThrow), new Throw(pins:
secondThrow) ];
   }

   Throw[] spareFrame(int firstThrow, int secondThrow) {
      return [ new Throw(pins: firstThrow), new Spare(pins:
secondThrow) ];
   }

   Throw[] strikeFrame() {
      return [ new Strike(pins: 10) ]; 
   }

   Throw[] bonusRoll(int roll) {
      return [ new Bonus(pins: roll) ]; 
   }
0
igouy (1009)
12/2/2003 7:48:56 PM
I'm sorry to say, but your BDUF solution is as clear or as clean as mud.

The bonusOne() and bonusTwo() methods depend in a funny way of the "i" 
instance variable, and this only make sense in a very particular loop, 
creating a nasty sequential dependency, plus you had to take the loop 
variable at the instance level because of that. Don't know about your 
tastes, but by my taste this is uggggggly.

Now there's a relationship between Game and Throw, however the 
information seems to be somehow lost so it is redundantly recovered by
calling :
	
	throws[i].bonus(this);

Which is yet one more ugly and unnecessary dependency. This all makes 
the class Throw just a bunch of code chopped out of a loop and promoted 
to the status of class. Just about the same as Ron's "OO" solutions

Costin


Isaac Gouy wrote:
> igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0311261205.6802ee1c@posting.google.com>...
> 
> 
>>Seems to me that the difference 
>>isn't "Less up-front design" it's "Less OO design"!
> 
> 
> Let me sharpen that: the difference is bad OO analysis and design ;-)
> 
> All the way back to the original article, frames get shoved into the
> solution without giving any reason why they are necessary or useful to
> the solution: "OK, clearly a game object consists of a sequence of ten
> frames"
>    http://www.objectmentor.com/resources/articles/xpepisode.htm
> 
> When we look at a bowling game scorecard (Figure 1 or better
> http://www.bowling.uklinux.net/bowling/score.php3 ) we can see:
> - a sequence of 'frames'
> - a sequence of 'throws' (given as 'X' or '/' or the pin score)
> 
> So, while it's true that a game consists of a sequence of ten frames,
> it's also true that a game consists of a sequence of throws. Why would
> we assume that 'frames' will be a better representation than throws?
> 
> Here's an alternative OO BUFD
> 
>        throws
>  |Game|------------->|Throw|
>                11..21| pins|
>                         |
>                         A
>                         |
>               +---------+---------+
>               |         |         |
>           |Strike|    |Spare|   |Bonus|
> 
> 
> Using Nice once more ( http://nice.sourceforge.net/index.html ) we
> have a fairly straightforward solution:
> 
>    class BowlingGame {
>       Throw[] throws;
>       int i = 0;
> 
>       int score(){
>          var total = 0;     
>          for (i = 0; i < throws.size; i++)
>             total += throws[i].score + throws[i].bonus(this);
>          return total;      
>       }
>    
>       int bonusOne() = throws[i+1].pinCount;
>       int bonusTwo() = throws[i+2].pinCount;   
>    }
> 
>    class Throw {
>       int pins;
> 
>       int pinCount() = pins;
>       int score() = pins;
>       int bonus(BowlingGame game) = 0;
>    }
> 
>    class Strike extends Throw {
>       bonus(BowlingGame game) = game.bonusOne + game.bonusTwo;
>    }
> 
>    class Spare extends Throw {
>       bonus(BowlingGame game) = game.bonusOne;
>    }
> 
>    class Bonus extends Throw {
>       score() = 0;
>    }
> 
> 
> Seems cleaner than the 'frames' based solution doesn't it?
> 
> best wishes, Isaac
> 
> 
> 
> Like http://www.xprogramming.com/xpmag/acsBowling.htm we could assume
> that the input is in-terms of Strike or Bonus throws rather than pin
> counts.
> Oh, let's do something directly comparable:
> 
>    void main(String[] args){  
>       let BowlingGame[] testGames = [
> 
>          new BowlingGame(throws: manyOpenFrames(10,0,0)),
>          new BowlingGame(throws: manyOpenFrames(10,3,3)),
> 
>          new BowlingGame(throws: 
>             spareFrame(4,6) + 
>             openFrame(3,5) + 
>             manyOpenFrames(8,0,0)
>             ),
> 
>          new BowlingGame(throws: 
>             spareFrame(4,6) + 
>             openFrame(5,3) + 
>             manyOpenFrames(8,0,0)
>             ),
> 
>          new BowlingGame(throws: 
>             strikeFrame() + 
>             openFrame(5,3) +
>             manyOpenFrames(8,0,0)
>             ),
> 
>          new BowlingGame(throws: 
>             manyOpenFrames(9,0,0) + 
>             strikeFrame() +
>             bonusRoll(5) +
>             bonusRoll(3)
>             ),
> 
>          new BowlingGame(throws: 
>             manyOpenFrames(9,0,0) +
>             spareFrame(4,6) + 
>             bonusRoll(5)
>             ),
> 
>          new BowlingGame(throws: 
>             strikeFrame() + strikeFrame() +
>             strikeFrame() + strikeFrame() +
>             strikeFrame() + strikeFrame() +
>             strikeFrame() + strikeFrame() +
>             strikeFrame() + strikeFrame() +         
>             bonusRoll(10) +
>             bonusRoll(10)
>             ),
> 
>          new BowlingGame(throws: 
>             strikeFrame() + spareFrame(4,6) + 
>             strikeFrame() + spareFrame(4,6) + 
>             strikeFrame() + spareFrame(4,6) + 
>             strikeFrame() + spareFrame(4,6) + 
>             strikeFrame() + spareFrame(4,6) + 
>             bonusRoll(10)
>             )        
>          ];
> 
>       for(each:testGames) println( each.score() );
>    }
> 
> 
>    // utility functions for test cases
> 
>    Throw[] manyOpenFrames(int count, int firstThrow, int secondThrow)
> {
>       let throwCount = count*2;
>       Throw[] t = cast(new Throw[throwCount]);
>       for (int i = 0; i < throwCount; i+=2) {
>          t[i] = new Throw(pins: firstThrow);
>          t[i+1] = new Throw(pins: secondThrow);
>       }
>       return t;
>    }
> 
>    Throw[] openFrame(int firstThrow, int secondThrow) {
>       return [ new Throw(pins: firstThrow), new Throw(pins:
> secondThrow) ];
>    }
> 
>    Throw[] spareFrame(int firstThrow, int secondThrow) {
>       return [ new Throw(pins: firstThrow), new Spare(pins:
> secondThrow) ];
>    }
> 
>    Throw[] strikeFrame() {
>       return [ new Strike(pins: 10) ]; 
>    }
> 
>    Throw[] bonusRoll(int roll) {
>       return [ new Bonus(pins: roll) ]; 
>    }

0
c_cozianu (217)
12/2/2003 8:08:13 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0311241350.3d6a6b0e@posting.google.com>...

We can use a similar approach to accumlate the intermediate bowling
scores for a possibly incomplete sequence of valid throws - update the
sequence after each throw and recalculate the scores.

Start = scores [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6] [] 0 0

scores pins a total 10    = a	
scores []   a total count = a
scores [x]  a total count = a

scores [x,y] a total count 
   | x == 10   = a
   | x+y == 10 = a
   | otherwise = a++[total+x+y]	
	
scores [x,y,z:rest] a total count
   | x == 10   = scores [y,z:rest] (a++[total+x+y+z]) (total+x+y+z)
(count+1)
   | x+y == 10 = scores   [z:rest] (a++[total+x+y+z]) (total+x+y+z)
(count+1)
   | otherwise = scores   [z:rest] (a++[total+x+y])   (total+x+y)  
(count+1)


The answer in this case would be [20,40,60,80,100,120,140] - the 8th
total for the spare cannot be calculated until the next throw.

best wishes, Isaac
0
igouy (1009)
12/4/2003 7:57:33 AM
"Isaac Gouy" <igouy@yahoo.com> wrote in message

> igouy@yahoo.com (Isaac Gouy) wrote in message
> We can use a similar approach to accumulate the intermediate bowling
> scores for a possibly incomplete sequence of valid throws - update the
> sequence after each throw and recalculate the scores.

Yeah bouyeee!!!!

Just excited, man!  I got it!  Shweet worked out overall architectural
plan and real, actual proven hooks for then nitty gritty, lower  level,
"how" aspect of the design.

[Aside:  if a single class with "everything in it" effectively models
essential logical domain role abstractions and collaboration dynamics,
then it does.  It would be silly to condemn such an OO design out of
hand, purely on the basis of it using only a single class.  Some domains
are logically anchored in only 1 single major abstraction, or a single
abstraction period.  Avoid dogmatism.]  Check use of lighthearted,
*Logical* OOPL:

class Scorer
{
private:
    ref2FrameStructure      Frame;
    ref2BagO'Frame           FrameBag;
    bool                                 isFoul;
    bool                                 isBonus;
    int                                     PinArray
    int                                     ThrowCount;
public
    Scorer( "init privates"; );
    throw(Pins;...)
    tally(Pins; ThrowCount; isFoul; FrameBag... )
};

structure Frames
{
private:
    int    FrameNum;
    int    mainDigit;
    int    upperLeftDigit;
    int    upperRightDigit;
public:
    Frames(0, 0, 0, _FrameNum)  { FrameNum=_FrameNum; };
};

~ must know specific pins up/down after a throw.
    e.g if Pin 1 is standing after a throw( ) it affects the way Frame
score is calculated
~ must know if bowler committed line isFoul
~ must know specific

It seems the "tuned" following code might work as my 'tally( );'

Elliott

> Start = scores [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6] [] 0 0
>
> scores pins a total 10    = a
> scores []   a total count = a
> scores [x]  a total count = a
>
> scores [x,y] a total count
>    | x == 10   = a
>    | x+y == 10 = a
>    | otherwise = a++[total+x+y]
>
> scores [x,y,z:rest] a total count
>    | x == 10   = scores [y,z:rest] (a++[total+x+y+z]) (total+x+y+z)
> (count+1)
>    | x+y == 10 = scores   [z:rest] (a++[total+x+y+z]) (total+x+y+z)
> (count+1)
>    | otherwise = scores   [z:rest] (a++[total+x+y])   (total+x+y)
> (count+1)
>
>
> The answer in this case would be [20,40,60,80,100,120,140] - the 8th
> total for the spare cannot be calculated until the next throw.
>
> best wishes, Isaac

+
-- 
Rather than try to make object design as much like procedural design as
possible, I have found that the most effective way of teaching the
idiomatic way
of thinking with objects is to immerse the learner in the "object-ness"
of the
material.


0
universe2 (613)
12/4/2003 9:19:29 AM
[Typo]

structure Frame                //not Frame"s"
> {
> private:
>     int    FrameNum;
>     int    mainDigit;
>     int    upperLeftDigit;
>     int    upperRightDigit;
> public:
>     Frames(0, 0, 0, _FrameNum)  { FrameNum=_FrameNum; };

Elliott
-- 
Once a fortnight, Audrey and her close companion were driven to
re-experience bathing in the pure sound of shattering panes of glass.




0
universe2 (613)
12/4/2003 9:43:32 AM
Costin Cozianu <c_cozianu@hotmail.com> wrote in message news:<bqirh6$23ha52$1@ID-152540.news.uni-berlin.de>...
> I'm sorry to say, but your BDUF solution is as clear or as clean as mud.

Don't apologize - justify! 
BDUF solution - the Big Dumb Up Front solution? ;-)

> The bonusOne() and bonusTwo() methods depend in a funny way of the "i" 
> instance variable, and this only make sense in a very particular loop, 
> creating a nasty sequential dependency, plus you had to take the loop 
> variable at the instance level because of that. Don't know about your 
> tastes, but by my taste this is uggggggly.

I agree!
Using a loop variable outside the scope of the loop is sooo very
error-prone.

-snip-
> This all makes the class Throw just a bunch of code chopped out of a loop
> and promoted to the status of class. 

It's just another desperate attempt to somehow push responsibility
into the 'Throw' objects, when fulfilling that responsibility requires
information about other 'Throw' objects. Seems like the responsibility
should be given to some object that "knows" about all the 'Throw'
objects - like BowlingGame.

Once we move that responsibility back to BowlingGame, we're left with
something like this:

class BowlingGame {
   Throw[] throws;
   int i = 0;

   int score(){
      var total = 0;     
      for (i = 0; i < throws.size; i++){
         if (throws[i].isStrike) {
            total += throws[i].score + throws[i+1].bonus +
throws[i+2].bonus;
         } 
         else if (throws[i].isSpare) { 
            total += throws[i].score + throws[i+1].bonus;
         }
         else {
            total += throws[i].score;
         }
      }
      return total;      
   } 
}


class Throw {
   int pins;

   int score() = pins;
   int bonus() = pins;
   boolean isStrike() = false;
   boolean isSpare() = false;
}

class Strike extends Throw {
   isStrike() = true;
}

class Spare extends Throw {
   isSpare() = true;
}

class Bonus extends Throw {
   score() = 0;
}


Perhaps we should conclude that the Throw objects don't do anything
interesting and the OO benefit in-this-case comes from structuring
code around a BowlingGame (like this
http://groups.google.com/groups?dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0311290753.6c4cd29a%40posting.google.com
)

best wishes, Isaac
0
igouy (1009)
12/4/2003 6:05:02 PM
To accumlate the intermediate bowling scores for a possibly incomplete
sequence of valid throws, we need to make 2 design changes: 1) we need
to return a sequence of scores; 2) we need to change the loop
invariant.

1) seems easy enough

   ArrayList<int> scores(int[] pins){
      var frames = 0;
      var total = 0;
      var i = 0;
      let ArrayList<int> knownScores = new ArrayList();

      while (frames<10){
         if (pins[i] == 10) {
            total += pins[i] + pins[i+1] + pins[i+2];
            i++;
         } 
         else if (pins[i] + pins[i+1] == 10) { 
            total += pins[i] + pins[i+1] + pins[i+2];
            i += 2;
         }
         else {
            total += pins[i] + pins[i+1];
            i += 2;
         }
         frames++;

         knownScores.add(total);
      }
      return knownScores; 
   }

which with the previous test cases gives

   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
   [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]
   [13, 21, 21, 21, 21, 21, 21, 21, 21, 21]
   [15, 23, 23, 23, 23, 23, 23, 23, 23, 23]
   [18, 26, 26, 26, 26, 26, 26, 26, 26, 26]
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 18]
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 15]
   [30, 60, 90, 120, 150, 180, 210, 240, 270, 300]
   [20, 40, 60, 80, 100, 120, 140, 160, 180, 200]


2) as the sequence of valid throws may be incomplete, we need to check
that there are enough throws in the sequence to calculate the next
score - if not we are finished.

ArrayList<int> scores(int[] pins){
   var frames = 0;
   var total = 0;
   var i = 0;
   let ArrayList<int> knownScores = new ArrayList();
   var n = 0;

   while (frames<10 && (n=pins.size-i)>1){
      if (n==2) {
         if (pins[i] + pins[i+1] < 10) {
            total += pins[i] + pins[i+1];         
            i++;
         }
         else break;
      }

      else if (pins[i] == 10) {
         total += pins[i] + pins[i+1] + pins[i+2];
         i++;
      } 
      else if (pins[i] + pins[i+1] == 10) { 
         total += pins[i] + pins[i+1] + pins[i+2];
         i += 2;
      }
      else {
         total += pins[i] + pins[i+1];
         i += 2;
      }
      frames++;

      knownScores.add(total);
   }
   return knownScores; 
}


void main(String[] args){  
   let int[][] tests = [
         [],
         [3],
         [3,3],
         [3,3, 3],
         [4,6],
         [4,6, 3],
         [4,6, 3,5],
         [10],
         [10, 5],
         [10, 5,3],
         [10, 5,3, 0],
         [10, 4,6],
         [10, 4,6, 10],
         [10, 4,6, 10, 4],
         [10, 4,6, 10, 4,6],

         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3],
         [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10, 5,3],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6, 5],
         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
         [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10]
      ];

   for(each:tests) println( scores(each) );
}

which gives

   []
   []
   [6]
   [6]
   []
   [13]
   [13, 21]
   []
   []
   [18, 26]
   [18, 26]
   [20]
   [20, 40]
   [20, 40]
   [20, 40, 60]
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
   [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]
   [13, 21, 21, 21, 21, 21, 21, 21, 21, 21]
   [15, 23, 23, 23, 23, 23, 23, 23, 23, 23]
   [18, 26, 26, 26, 26, 26, 26, 26, 26, 26]
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 18]
   [0, 0, 0, 0, 0, 0, 0, 0, 0, 15]
   [30, 60, 90, 120, 150, 180, 210, 240, 270, 300]
   [20, 40, 60, 80, 100, 120, 140, 160, 180, 200]

best wishes, Isaac
0
igouy (1009)
12/4/2003 10:01:43 PM
This overall design is meant to address the "where does scoring info
come from" question.

And yes it seems class Scorer might work as class Game.  Not sure
thinking about it.  Would like to hear peoples pros and cons.  IsaacG is
seems to have a very good design and algorithms.  I'm thinking the
following might supplement, fill out and possible provide a larger
"setting" for Isaac's designs and algorithms.

I know this is not syntactically correct.  I'm mainly getting at overall
logical design solutions.

class Sensor
{
public:
    virtual read( *Scorer, Lane ) { ... };
};


 ... };

class PinSensor : class Sensor
{
private:
    int                    PinState[10];
    PinState[10]      PinStateArray[15];
public:
    PinSensor ( "init privates" ) { ... };
    read ( *Scorer, Lane ) { ... };         //get lane pin state
    pinVal getPinVal( PinStateNDX ) { return PinState[PinStateNDX]; };
    PinStateArrayCopy getPinStateArrayCopy( ) { ... };
};

class LineSensor : class Sensor
{
private:
    bool             isFoul;
    isFoul[10]    isFouldArray[15];
public:
    LineSensor ( "init privates" ) { ... };
    read (*Scorer, Lane ) { ... };
    isFoul getisFoulVal( ) { ... };
    isFoulArrayCopy( ) { ... };
};

 class Scorer
 {
 private:
     FrameStructure       Frame;
     FrameStructure       Frames[15]
     int                          FrameArrayNDX;
     bool                        isFoul;
     bool                        isBonus;
     Pins[]*                    ThrowPins;
     PinArray                  PinStates[10];
     int                          PinArrayNDX;
 public
     Scorer( "init privates" ) { ... };
     throw(ThrowPins, *Sensor, ...) { ... };
     tally(ThrowPins, ThrowCount, isFoul; FrameBag, ... ) { ... };
 };

 structure Frame
 {
 private:
     int    FrameNum;
     int    mainDigit;
     int    upperLeftDigit;
     int    upperRightDigit;
 public:
     Frame( "init privates" )  { ... };
 };





> ~ must know specific pins up/down after a throw.
>     e.g if Pin 1 is standing after a throw( ) it affects the way Frame
> score is calculated
> ~ must know if bowler committed line isFoul
> ~ must know specific
>
> It seems the "tuned" following code might work as my 'tally( );'
>
> Elliott
>
> > Start = scores [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6] [] 0 0
> >
> > scores pins a total 10    = a
> > scores []   a total count = a
> > scores [x]  a total count = a
> >
> > scores [x,y] a total count
> >    | x == 10   = a
> >    | x+y == 10 = a
> >    | otherwise = a++[total+x+y]
> >
> > scores [x,y,z:rest] a total count
> >    | x == 10   = scores [y,z:rest] (a++[total+x+y+z]) (total+x+y+z)
> > (count+1)
> >    | x+y == 10 = scores   [z:rest] (a++[total+x+y+z]) (total+x+y+z)
> > (count+1)
> >    | otherwise = scores   [z:rest] (a++[total+x+y])   (total+x+y)
> > (count+1)
> >
> >
> > The answer in this case would be [20,40,60,80,100,120,140] - the 8th
> > total for the spare cannot be calculated until the next throw.
> >
> > best wishes, Isaac
>
> +
> -- 
> Rather than try to make object design as much like procedural design
as
> possible, I have found that the most effective way of teaching the
> idiomatic way
> of thinking with objects is to immerse the learner in the
"object-ness"
> of the
> material.
>
>


0
universe2 (613)
12/5/2003 6:44:00 PM
>  class Scorer
>  {
>  private:
>      FrameStructure       Frame;
>      int                          FrameArrayNDX;
>      bool                        isFoul;
>      bool                        isBonus;
        int*                         PinArray[]
>      int                          PinArrayNDX;
        int                          ThrowCount;
        Sensor*                    ptr2Sensor00
        Sensor*                    ptr2Sensor01
>  public
>      Scorer( "init privates" ) { ... };
>      throw(ptr2Sensor00, ptr2Sensor01, ...) { ... };
>      tally(ptr2Sensor00, ThrowCount, isFoul; Frame, ... ) { ... };
>  };
>
>  structure Frame
>  {
>  private:
>      int    FrameNum;
>      int    mainDigit;
>      int    upperLeftDigit;
>      int    upperRightDigit;
>  public:
>      Frame( "init privates" )  { ... };
>  };


> This overall design is meant to address the "where does scoring info
> come from" question.
>
> And yes it seems class Scorer might work as class Game.  Not sure
> thinking about it.  Would like to hear peoples pros and cons.  IsaacG
is
> seems to have a very good design and algorithms.  I'm thinking the
> following might supplement, fill out and possible provide a larger
> "setting" for Isaac's designs and algorithms.
>
> I know this is not syntactically correct.  I'm mainly getting at
overall
> logical design solutions.
>
> class Sensor
> {
> public:
>     virtual read( *Scorer, Lane ) { ... };
> };
>
>
>  ... };
>
> class PinSensor : class Sensor
> {
> private:
>     int                    PinState[10];
>     PinState[10]      PinStateArray[15];
> public:
>     PinSensor ( "init privates" ) { ... };
>     read ( *Scorer, Lane ) { ... };         //get lane pin state
>     pinVal getPinVal( PinStateNDX ) { return PinState[PinStateNDX]; };
>     PinStateArrayCopy getPinStateArrayCopy( ) { ... };
> };
>
> class LineSensor : class Sensor
> {
> private:
>     bool             isFoul;
>     isFoul[10]    isFouldArray[15];
> public:
>     LineSensor ( "init privates" ) { ... };
>     read (*Scorer, Lane ) { ... };
>     isFoul getisFoulVal( ) { ... };
>     isFoulArrayCopy( ) { ... };
> };
>
>
>
>
>
>
> > ~ must know specific pins up/down after a throw.
> >     e.g if Pin 1 is standing after a throw( ) it affects the way
Frame
> > score is calculated
> > ~ must know if bowler committed line isFoul
> > ~ must know specific
> >
> > It seems the "tuned" following code might work as my 'tally( );'
> >
> > Elliott
> >
> > > Start = scores [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6] [] 0 0
> > >
> > > scores pins a total 10    = a
> > > scores []   a total count = a
> > > scores [x]  a total count = a
> > >
> > > scores [x,y] a total count
> > >    | x == 10   = a
> > >    | x+y == 10 = a
> > >    | otherwise = a++[total+x+y]
> > >
> > > scores [x,y,z:rest] a total count
> > >    | x == 10   = scores [y,z:rest] (a++[total+x+y+z])
(total+x+y+z)
> > > (count+1)
> > >    | x+y == 10 = scores   [z:rest] (a++[total+x+y+z])
(total+x+y+z)
> > > (count+1)
> > >    | otherwise = scores   [z:rest] (a++[total+x+y])   (total+x+y)
> > > (count+1)
> > >
> > >
> > > The answer in this case would be [20,40,60,80,100,120,140] - the
8th
> > > total for the spare cannot be calculated until the next throw.
> > >
> > > best wishes, Isaac
> >
> > +
> > -- 
> > Rather than try to make object design as much like procedural design
> as
> > possible, I have found that the most effective way of teaching the
> > idiomatic way
> > of thinking with objects is to immerse the learner in the
> "object-ness"
> > of the
> > material.
> >
> >
>
>


0
universe2 (613)
12/5/2003 7:42:56 PM
Tweaked class Sensor structure wrt interface

> This overall design is meant to address the "where does scoring info
> come from" question.
>
> I know this is not syntactically correct.  I'm mainly getting at
>  overall logical design solutions.

structure ThrowData
{
public:
    int     Pins[10];
    bool   isFoul;
};

> class SensorQuery
 {
 public:
       SensorQuery( ) { ... };
       ThrowData* getThrowData( *Scorer, Lane ) { ... };  // this querys
pin and Foul line senors
};


  class Scorer
  {
  private:
      FrameStructure       Frame;
      int                          FrameArrayNDX;
      bool                        isFoul;
      bool                        isBonus;
      int*                         PinArray[]
      int                          PinArrayNDX;
      int                          ThrowCount;
      SensorQuery*            ptr2SensorQuery;
  public
      Scorer( "init privates" ) { ... };
      throw(ptr2Sensor00, ptr2Sensor01, ...) { ... };
      tally(ptr2SensorQuery, ThrowCount, isFoul; Frame, ... ) { ... };
  };

  structure Frame
  {
  private:
      int    FrameNum;
      int    mainDigit;
      int    upperLeftDigit;
      int    upperRightDigit;
  public:
      Frame( "init privates" )  { ... };
  };

Elliott
-- 
Rather than try to make object design as much like procedural design as
possible, I have found that the most effective way of teaching the
idiomatic
way of thinking with objects is to immerse the learner in the
"object-ness"
of the material.


0
universe2 (613)
12/6/2003 1:58:00 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...

"What makes this game interesting to score is the lookahead in the
scoring for strike and spare. At the time we throw a strike or spare,
we cannot calculate the frame score: we have to wait one or two frames
to find out what the bonus is."
http://www.xprogramming.com/xpmag/acsBowling.htm

To say it differently, we have to *queue* the individual roll scores
until we can calculate the frame score. Which leads to a solution that
will produce the frame scores as they become available:

   class ScoreKeeper {
      ArrayList<int> q = new ArrayList(3);
      int total = 0;
      int frames = 0;

      void addScore(int pins){
         q.add(pins);
         if (q.size==3 && frames<10){
            total += q[0]+q[1]+q[2];
            frames++;
            if (q[0]<10) q.remove(q[1]);
            q.remove(q[0]); 
            totalScore(total);        
          }
         if (q.size==2 && frames<10){
            let frameScore = q[0]+q[1];
            if (frameScore<10){
               total += frameScore;
               frames++;
               q.clear;
               totalScore(total);  
            }      
         }
      }
   }

   void totalScore(int score){
      print(score); print(" ");
   }

   void main(String[] args){  
      let int[][] tests = [
         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
         [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10]
      ];
      for(pinScores:tests) {
         let sk = new ScoreKeeper();
         for(each:pinScores) sk.addScore(each);
         print("\n");
      }
   }

Which gives these results
   30 60 90 120 150 180 210 240 270 300
   20 40 60 80 100 120 140 160 180 200

Of course, we could configure the ScoreKeeper to not announce the
intermediate frame scores, and instead ask directly for the total
score once a line had been completed.

best wishes, Isaac
0
igouy (1009)
12/6/2003 2:24:46 AM
Scorer::throw()      // tweaked

>   class Scorer
>   {
>   private:
>       FrameStructure       Frame;
>       int                          FrameArrayNDX;
>       bool                        isFoul;
>       bool                        isBonus;
>       int*                         PinArray[]
>       int                          PinArrayNDX;
>       int                          ThrowCount;
>       SensorQuery*           ptr2SensorQuery;
>   public
>       Scorer( "init privates" ) { ... };
         throw(ptr2SensorQuery* ...) { ... };
>       tally(ptr2SensorQuery*, ThrowCount, isFoul; Frame, ... ) {
.... };
>   };

> structure ThrowData
> {
> public:
>     int     Pins[10];
>     bool   isFoul;
> };
>
> > class SensorQuery
>  {
>  public:
>        SensorQuery( ) { ... };
>        ThrowData* getThrowData( *Scorer, Lane ) { ... };  // this
querys
> pin and Foul line senors
> };
>
>   structure Frame
>   {
>   private:
>       int    FrameNum;
>       int    mainDigit;
>       int    upperLeftDigit;
>       int    upperRightDigit;
>   public:
>       Frame( "init privates" )  { ... };
>   };
>
> Elliott
> -- 
> Rather than try to make object design as much like procedural design
as
> possible, I have found that the most effective way of teaching the
> idiomatic way of thinking with objects is to immerse the learner in
the
> "object-ness" of the material.
>
>


0
universe2 (613)
12/6/2003 2:40:12 AM
On Fri, 5 Dec 2003 21:40:12 -0500, "Universe" <universe@covad.net> wrote:

>Scorer::throw()      // tweaked

Does it work yet? Does it /compile/ yet?

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/6/2003 4:31:35 AM
"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
news:tsm2tv4htdolasqmpen402jf10md58ujot@4ax.com...
> On Fri, 5 Dec 2003 21:40:12 -0500, "Universe" <universe@covad.net>
wrote:
>
> >Scorer::throw()      // tweaked
>
> Does it work yet? Does it /compile/ yet?

Why would you doubt that it does?

That's characteristic of XP'ers.  Seems to me it's obvious to most the
design is workable.  And the best part is that it's deeply rooted in the
domain semantics.

Elliott


0
universe2 (613)
12/6/2003 4:51:59 AM
On 5 Dec 2003 18:24:46 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>   class ScoreKeeper {
>      ArrayList<int> q = new ArrayList(3);
>      int total = 0;
>      int frames = 0;
>
>      void addScore(int pins){
>         q.add(pins);
>         if (q.size==3 && frames<10){
>            total += q[0]+q[1]+q[2];
>            frames++;
>            if (q[0]<10) q.remove(q[1]);
>            q.remove(q[0]); 
>            totalScore(total);        
>          }
>         if (q.size==2 && frames<10){
>            let frameScore = q[0]+q[1];
>            if (frameScore<10){
>               total += frameScore;
>               frames++;
>               q.clear;
>               totalScore(total);  
>            }      
>         }
>      }
>   }

I found that code very hard to understand, though it might just be that I'm dull
or that it is after midnight.

I think it would help if the q.size==2 code was first, rather than the size 3
code, since q size == 2 occurs first in time. Then we can read:

>         if (q.size==2 && frames<10){
>            let frameScore = q[0]+q[1];
>            if (frameScore<10){
>               total += frameScore;
>               frames++;
>               q.clear;
>               totalScore(total);  
>            }      
>         }

As saying (but not expressing very well IMO), if we have two balls and the frame
score is less than ten (which means it's not a strike and not a spare), then the
total is just the sum of the two balls in the queue, and it's a new frame.
Forget the balls.

The only time this code doesn't clear the queue is if frameScore >= 10. (This is
very odd, since it is calling a strike followed by a 5 a "frame score" of 15,
which is not the score for the frame at all. Nonetheless ...

>         if (q.size==3 && frames<10){
>            total += q[0]+q[1]+q[2];
>            frames++;
>            if (q[0]<10) q.remove(q[1]);
>            q.remove(q[0]); 
>            totalScore(total);        
>          }

Occurs only if it's a strike or spare. The code sums the three balls, which is
either the strike plus its two bonuses, or the spare plus its one bonus.

	I occasionally refactor the procedural version into a state like this
one, noting that in the case of a "mark" (strike or spare), the frame score is
the same, namely the sum of three balls starting with the first ball of the
frame. Classes watching this demonstration seem uniformly to agree that this
removal of duplication is not expressive, even though it's correct. Basically
the same trick is being used here. Anyway ...

.... we sum the three balls, tick the frame, then we do a curious little
ball-removal thing. We remove the first ball unconditionally (but we have chosen
to do it last). If the first ball is not 10, then we are on a spare, and both
the first and second ball (0 and 1) are used up. So we remove 1 if it's a spare,
and 0 it if's a spike.

I do think this code could be far more clear but it does work. Definitely an
interesting algorithm.

Regards,

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/6/2003 5:14:51 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<pto2tvsas9kad4o4i1cb4o3j39fl3mamko@4ax.com>...
-snip-

> I found that code very hard to understand, though it might just be that I'm
> dull or that it is after midnight.

Not too suprising since you don't seem to have seen this approach
before, and it comes without any explanation.


> I think it would help if the q.size==2 code was first, rather than the size 3
> code, since q size == 2 occurs first in time. 

For addScore(10) addScore(5) addScore(3) that would give the
totalScore 18; where it should give the totalScores 18 & 26.

q.size == 3 needs to come first to produce correct results.


> >         if (q.size==2 && frames<10){
> >            let frameScore = q[0]+q[1];
> >            if (frameScore<10){
> >               total += frameScore;
> >               frames++;
> >               q.clear;
> >               totalScore(total);  
> >            }      
> >         }
> 
> As saying (but not expressing very well IMO) 
-snip-
> The only time this code doesn't clear the queue is if frameScore >= 10.
> (This is very odd, since it is calling a strike followed by a 5 a "frame
> score" of 15, which is not the score for the frame at all. 

Suggestions for improved forms of expression would be more interesting
IMO ;-)

For instance, we could name the temporary variable qTotal or we could
just get rid of it:

   if (q.size==2 && frames<10){
      if (q[0]+q[1]<10){
         total += q[0]+q[1];
         frames++;
         q.clear;
         totalScore(total);  
      }  

> I occasionally refactor the procedural version into a state like this
> one, noting that in the case of a "mark" (strike or spare), the frame score
> is the same, namely the sum of three balls starting with the first ball of
> the frame. Classes watching this demonstration seem uniformly to agree that
> this removal of duplication is not expressive, even though it's correct.
> Basically the same trick is being used here. 

No "trick" is being used here.
We're waiting until we can calculate a total - if there are 3 rolls in
the queue we always can, and if there are 2 rolls in the queue we
maybe can. These are conditions basic to the algorithm - there's no
"duplication" to remove.


> We remove the first ball unconditionally (but we have chosen
> to do it last). 

If we did it first then we would have changed the value of q[0] before
we tested it in the condition...


> I do think this code could be far more clear but it does work. Definitely an
> interesting algorithm.

I'll post a C# version later, maybe you could refactor that to be more
clear?

best wishes, Isaac
0
igouy (1009)
12/6/2003 7:06:03 PM
And in C#
---------

using System;
using System.Collections;

namespace TenPin
{
   class ScoreKeeper
   {
      ArrayList q = new ArrayList(3);
      int total = 0;
      int frames = 1;
      BowlingGame game;

      public void AddScore(int pins)
      {
         q.Add(pins);
         if (q.Count==3 && frames<=10)
         {
            total += (int)q[0]+(int)q[1]+(int)q[2];
            frames++;
            if ((int)q[0]<10) q.RemoveAt(1);
            q.RemoveAt(0); 
            game.totalScore(total);        
         }
         if (q.Count==2 && frames<=10)
         {
            if ((int)q[0]+(int)q[1] < 10)
            {
               total += (int)q[0]+(int)q[1];
               frames++;
               q.Clear();
               game.totalScore(total);  
            }      
         }
      }

      public ScoreKeeper(BowlingGame aGame)
      {
         game = aGame;
      }
   }

   class BowlingGame
   {
      public void totalScore(int score)
      {
         Console.Write(score+" ");
      }

      static void Main(string[] args)
      {
         BowlingGame g = new BowlingGame();
         foreach(int[] pinScores in tests) {
            ScoreKeeper sk = new ScoreKeeper(g);
            foreach(int each in pinScores) sk.AddScore(each);
            Console.WriteLine();
         }
      }

      static int[][] tests = {
         new int[]{0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0},
         new int[]{3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3},
         new int[]{4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0},
         new int[]{4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0},
         new int[]{10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0},
         new int[]{0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10, 5,3},
         new int[]{0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6, 5},
         new int[]{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
         new int[]{10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10},
         new int[]{},
         new int[]{3},
         new int[]{3,3},
         new int[]{3,3, 3},
         new int[]{4,6},
         new int[]{4,6, 3},
         new int[]{4,6, 3,5},
         new int[]{10},
         new int[]{10, 5},
         new int[]{10, 5,3},
         new int[]{10, 5,3, 0},
         new int[]{10, 4,6},
         new int[]{10, 4,6, 10},
         new int[]{10, 4,6, 10, 4},
         new int[]{10, 4,6, 10, 4,6},
         new int[]{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
      };
   }
}
0
igouy (1009)
12/6/2003 7:14:14 PM
"Universe" <universe@covad.net> wrote in message news:<25568$3fd0d1f2$4069d048$14604@msgid.meganewsservers.com>...

> This overall design is meant to address the "where does scoring info
> come from" question.
> 
> And yes it seems class Scorer might work as class Game.  Not sure
> thinking about it.  Would like to hear peoples pros and cons.  IsaacG is
> seems to have a very good design and algorithms.  I'm thinking the
> following might supplement, fill out and possible provide a larger
> "setting" for Isaac's designs and algorithms.

Flattery is good! Although the knickname is too much like Ali G ;-)

And I'm too set in my ways to learn C++.

One of the issues I have with the BowlingGame is with the problem
description - the "where does scoring info come from" question. That's
a separate thread.

best wishes, Isaac
0
igouy (1009)
12/7/2003 12:46:30 AM
On 6 Dec 2003 11:06:03 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<pto2tvsas9kad4o4i1cb4o3j39fl3mamko@4ax.com>...
>-snip-
>
>> I found that code very hard to understand, though it might just be that I'm
>> dull or that it is after midnight.
>
>Not too suprising since you don't seem to have seen this approach
>before, and it comes without any explanation.

Yes. I mention that it is hard to understand because I'm sure you'd like it to
be easy to understand. I figured some feedback might be helpful.
>
>
>> I think it would help if the q.size==2 code was first, rather than the size 3
>> code, since q size == 2 occurs first in time. 
>
>For addScore(10) addScore(5) addScore(3) that would give the
>totalScore 18; where it should give the totalScores 18 & 26.
>
>q.size == 3 needs to come first to produce correct results.

I didn't see that in the code, and I'm not convinced. The two ifs, with the code
removed, look like this:

         if (q.size==3 && frames<10){
          }
         if (q.size==2 && frames<10){
         }

It seems to me that these two blocks are entirely independent of order, since
only one of them can ever be true at any given time. Am I missing something?
>
>> I occasionally refactor the procedural version into a state like this
>> one, noting that in the case of a "mark" (strike or spare), the frame score
>> is the same, namely the sum of three balls starting with the first ball of
>> the frame. Classes watching this demonstration seem uniformly to agree that
>> this removal of duplication is not expressive, even though it's correct.
>> Basically the same trick is being used here. 
>
>No "trick" is being used here.
>We're waiting until we can calculate a total - if there are 3 rolls in
>the queue we always can, and if there are 2 rolls in the queue we
>maybe can. These are conditions basic to the algorithm - there's no
>"duplication" to remove.

The point is, it doesn't seem right to someone who knows the domain. It is
entirely true, I agree, that both a strike frame and a spare frame score the
same three balls, by simply adding them up. That's an observation that /bowlers/
don't resonate with. They feel better when the concept of strike and spare are
separate in the code.

I've done that final removal of duplication in front of a number of audiences,
and they always object along the lines of yes, it's correct, but it's so
different from how people think of bowling that its expressiveness is wanting.
Too cryptic.
>
>
>> We remove the first ball unconditionally (but we have chosen
>> to do it last). 
>
>If we did it first then we would have changed the value of q[0] before
>we tested it in the condition...

Yes, true, I see that now. The code remains odd, because it's not usually kosher
to remove things from the middle of a queue. I'm not sure that I like this any
better:

         if (q.size==3 && frames<10){
            total += q[0]+q[1]+q[2];
            frames++;
            firstBall = q.remove[q[0]);
            if (firstball<10) q.remove(q[1]);
            totalScore(total);        
          }

I think I'd like it better if we just popped the queue rather than use
remove,(), which I guess is actually searching the queue? 
>
>> I do think this code could be far more clear but it does work. Definitely an
>> interesting algorithm.
>
>I'll post a C# version later, maybe you could refactor that to be more
>clear?

No promises. I'll be traveling the next couple of weeks, and my programming is
aimed at Extended Set Theory right now. But I might give it a shot.

Interesting example, never seen it done that way before. I love the smell of a
new idea in the morning!

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/7/2003 2:27:19 AM
On 6 Dec 2003 11:14:14 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>And in C#

I'll see about taking a look at it. Odd tests -- we have to check them by
inspection? 

The callback to the game is interesting -- I hadn't noticed that before. Could
almost use it to drive a score panel, but the program still doesn't really know
how many frames have been bowled ... or does it. Maybe it does. It could pass
that in and we'd almost have scoring for each frame done ...

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/7/2003 2:31:28 AM
Isaac Gouy wrote:
>>>         if (q.size==2 && frames<10){
>>>            let frameScore = q[0]+q[1];
>>>            if (frameScore<10){
>>>               total += frameScore;
>>>               frames++;
>>>               q.clear;
>>>               totalScore(total);
>>>            }
>>>         }

Ron Jeffries:
>> The only time this code doesn't clear the queue is if frameScore >= 10.
>> (This is very odd, since it is calling a strike followed by a 5 a "frame
>> score" of 15, which is not the score for the frame at all.

Isaac Gouy wrote:
> Suggestions for improved forms of expression would be more interesting
> IMO ;-)
> 
> For instance, we could name the temporary variable qTotal or we could
> just get rid of it:
> 
>    if (q.size==2 && frames<10){
>       if (q[0]+q[1]<10){
>          total += q[0]+q[1];
>          frames++;
>          q.clear;
>          totalScore(total);
>       }

Or put frameScore inside the if block:

  if (q.size==2 && frames<10){
     if (q[0]+q[1]<10){
        let frameScore = q[0]+q[1];
        total += frameScore;
        frames++;
        q.clear;
        totalScore(total);
     }
  }

0
12/7/2003 2:56:41 AM
Ron Jeffries:
> Isaac Gouy:
>> Ron Jeffries:
>>> I think it would help if the q.size==2 code was first, rather than the
>>> size 3 code, since q size == 2 occurs first in time.

>>For addScore(10) addScore(5) addScore(3) that would give the
>>totalScore 18; where it should give the totalScores 18 & 26.
>>
>>q.size == 3 needs to come first to produce correct results.

> I didn't see that in the code, and I'm not convinced. The two ifs, with
> the code removed, look like this:
> 
>          if (q.size==3 && frames<10){
>           }
>          if (q.size==2 && frames<10){
>          }
> 
> It seems to me that these two blocks are entirely independent of order,
> since only one of them can ever be true at any given time. Am I missing
> something?

You are. The contents of the first if block modify q. So it is possible for
them both to be true given a strike followed by an open frame.

0
12/7/2003 3:05:59 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<k145tv031pajjhpk7ign7i7kd8auhqk19v@4ax.com>...
> On 6 Dec 2003 11:14:14 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
> 
> >And in C#
> 
> I'll see about taking a look at it. Odd tests -- we have to check them by
> inspection? 

Nothing stopping you writing them in NUnit ;-)
 

> The callback to the game is interesting 

The Nice version calls a package level function rather than a method,
that's probably why you missed it. It's a simple enough idea that
follows from working within a richer 'problem context' - I'll talk
about that another day - this bit of software receives events
<addScore> and sends events <totalScore>.


> Could almost use it to drive a score panel, but the program still doesn't
> really know how many frames have been bowled ... or does it. 

The ScoreKeeper is counting the number of frames that have been
'scored' it needs to do that to deal with the special case treatment
of bonus rolls on the 10th frame.

The ScoreKeeper will produce the next known total (when it's known),
the bit of software that drives the score panel can keep track of
where to display the next known total. (And something would have to
keep track of displaying the individual roll scores - but that's a
different problem which hasn't been described).

> Maybe it does. It could pass that in and we'd almost have scoring for each
> frame done ...

For the simple rules given, it seems done. 

best wishes, Isaac
0
igouy (1009)
12/7/2003 7:37:45 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<q935tv8v2r4c2pbcarqdsbjn7tte494j05@4ax.com>...

> >For addScore(10) addScore(5) addScore(3) that would give the
> >totalScore 18; where it should give the totalScores 18 & 26.
> >
> >q.size == 3 needs to come first to produce correct results.
> 
> I didn't see that in the code, and I'm not convinced. 

Well, you have the C# version - so you can run the code and check the
test results, if that's what it takes to convince you ;-)


> >No "trick" is being used here.
> >We're waiting until we can calculate a total - if there are 3 rolls in
> >the queue we always can, and if there are 2 rolls in the queue we
> >maybe can. These are conditions basic to the algorithm - there's no
> >"duplication" to remove.
> 
> The point is, it doesn't seem right to someone who knows the domain.

We've left the domain of bowling - we're in the domain of computation.

> It is entirely true, I agree, that both a strike frame and a spare frame
> score the same three balls, by simply adding them up. That's an observation
> that /bowlers/ don't resonate with. They feel better when the concept of
> strike and spare are separate in the code.

"Frankly, my dear, I don't give a damn."

Do we ask racing car drivers to design racing cars?
We ask designers to design racing cars, and drivers to race 'em.


> I've done that final removal of duplication in front of a number of
> audiences, and they always object along the lines of yes, it's correct,
> but it's so different from how people think of bowling that its
> expressiveness is wanting.

Does Quicksort express how people think about sorting ;-)


> Too cryptic.

Oh! I didn't post the cryptic algorithm (because it's only useful for
incrementally calculating the final total).
   total += pins * m1;
   m1 = m2; m2 = 1;
Just increment m1 & m2 appropriately to give the correct Strike/Spare
bonus values when we finally get the score for roll i+1 i+2


> Yes, true, I see that now. The code remains odd, because it's not usually
> kosher to remove things from the middle of a queue. 

Well, the 'implementation' isn't actually a queue is it ;-)

> I think I'd like it better if we just popped the queue rather than use
> remove,(), which I guess is actually searching the queue? 

Maybe I can see what you are getting hooked on: in Java 'remove' is
used both to remove an item at an index position, and to search and
remove an object. How that should work with generic collections in
Nice I haven't checked recently. Pretend the method is 'removeAt:'

      if (q.size==3 && frames<10){
         total += q[0]+q[1]+q[2];
         if (q[0]<10) q.removeAt[1];
         q.removeAt[0];
         frames++;
         totalScore(total);        
      }

or

      if (q.size==3 && frames<10){
         total += q[0]+q[1]+q[2];
         if (q[0]<10){
            q.removeAt[0];
            q.removeAt[0];
         }
         else {
            q.removeAt[0];
         }
         frames++;
         totalScore(total);        
      }


> Interesting example, never seen it done that way before

Isn't this what folk do to manually calculate scores?
Get a new roll score, check back to see if there are any unknown frame
scores which now have roll scores to calculate. The unknown frame
scores are 'in effect' queued. When we fill in a score for that frame,
we 'in effect' remove it from future consideration.

best wishes, Isaac
0
igouy (1009)
12/7/2003 8:43:34 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd2997c@news.totallyobjects.com>...

> > It seems to me that these two blocks are entirely independent of order,
> > since only one of them can ever be true at any given time. Am I missing
> > something?
> 
> You are. The contents of the first if block modify q. So it is possible for
> them both to be true given a strike followed by an open frame.

Exactly.
   X 5 
then with the next roll we calculate 2 frame totals
   X 5 3 => 18 26

best wishes, Isaac
0
igouy (1009)
12/7/2003 8:48:37 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd2974d@news.totallyobjects.com>...

What's the benefit?

> Or put frameScore inside the if block:
> 
>   if (q.size==2 && frames<10){
>      if (q[0]+q[1]<10){
>         let frameScore = q[0]+q[1];
>         total += frameScore;
>         frames++;
>         q.clear;
>         totalScore(total);
>      }
>   }
0
igouy (1009)
12/7/2003 8:50:10 AM
Isaac Gouy:
> Paul Sinnett:
>> Or put frameScore inside the if block:
>> 
>>   if (q.size==2 && frames<10){
>>      if (q[0]+q[1]<10){
>>         let frameScore = q[0]+q[1];
>>         total += frameScore;
>>         frames++;
>>         q.clear;
>>         totalScore(total);
>>      }
>>   }

> What's the benefit?

Same as before, but without the quibble.
0
12/7/2003 10:45:37 AM
On Sun, 07 Dec 2003 03:05:59 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:

>> It seems to me that these two blocks are entirely independent of order,
>> since only one of them can ever be true at any given time. Am I missing
>> something?
>
>You are. The contents of the first if block modify q. So it is possible for
>them both to be true given a strike followed by an open frame.

Ah, yes, I see that now. Of course I am a bear of very little brain, but that
makes me think there's something perhaps too tricky about that code.

Too tricky for me, anyway ...  ;->

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/7/2003 12:16:17 PM
On 7 Dec 2003 00:43:34 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>> >No "trick" is being used here.
>> >We're waiting until we can calculate a total - if there are 3 rolls in
>> >the queue we always can, and if there are 2 rolls in the queue we
>> >maybe can. These are conditions basic to the algorithm - there's no
>> >"duplication" to remove.
>> 
>> The point is, it doesn't seem right to someone who knows the domain.
>
>We've left the domain of bowling - we're in the domain of computation.

Well, that may be. There are many people, however, who believe that it is best
for software to have a clear mapping back to the original domain. I'm not as
fanatical about that, as sometimes (as with the Quicksort example I snipped out)
a really hot algorithm can have value even if it is not obvious.

However, that sort of thing comes at a price, and this example shows that price
in a very simple situation.

There are a number of simple programs out there which are no more complex than
the queueing example -- and some are arguably less complex -- and which retain
the flavor of bowling to a greater degree than the queueing example.

When the next person comes along, he is likely to understand bowling, and to be
looking for bowling ideas like strike, spare, and such in the code. If he
doesn't find them, his work will be deterred.
>
>> It is entirely true, I agree, that both a strike frame and a spare frame
>> score the same three balls, by simply adding them up. That's an observation
>> that /bowlers/ don't resonate with. They feel better when the concept of
>> strike and spare are separate in the code.
>
>"Frankly, my dear, I don't give a damn."
>
>Do we ask racing car drivers to design racing cars?
>We ask designers to design racing cars, and drivers to race 'em.

I fear that this analogy is weak on two accounts. Drivers and designers
collaborate very strongly on race cars, because even with all the amazing
real-time instrumentation they have, the driver's perception of the car controls
how well the race will be run. Racing is a collaboration, not a process where
cars are thrown over the wall (!) to the drivers.

Second, software isn't racing. In our work, the program has two purposes. One is
to compute something. The other is to communicate. The program under discussion
is an example of that -- since none of us needs to score a bowling game, the
program's /sole/ purpose is to help us communicate about algorithms, design,
practices and so on.

If this were a real application, we would have additional need to communicate,
with the programmers of the future who will need to maintain the program or
interface to it. 

Like you, I discovered that strike and spare both process the same three balls
to get the frame score, and I was rather enamored of the discovery that I could
merge those two ideas into one chunk of code.

But I consider the fact that a very large proportion of my peers find that to be
too cryptic and say that they wouldn't do it to be a very important bit of
feedback on how cryptic I should make my code.

In some situation where it was critical to save space or something, I might
still do it. But I would be aware that the code would be confusing (as is
Quicksort, at least to me) and I'd try to do something to clarify or explain it
to the next reader. If there was no critical need to tighten it up, I'd leave it
in the form which has been found to be more clear by a large number of
programmers.

We don't often get feedback on our coding ideas from a large number of our
peers. When I'm lucky enough to get it, I try to listen and learn.

Regards,

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/7/2003 12:29:26 PM
On 6 Dec 2003 23:37:45 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<k145tv031pajjhpk7ign7i7kd8auhqk19v@4ax.com>...
>> On 6 Dec 2003 11:14:14 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
>> 
>> >And in C#
>> 
>> I'll see about taking a look at it. Odd tests -- we have to check them by
>> inspection? 
>
>Nothing stopping you writing them in NUnit ;-)

Nothing stopping you from doing it either. It would make transmitting the code
much easier, as you're trying to do right now, since I'll have to manually
figure out the scores to see if they are correct.

And it could even make your own work on programs go faster, as if you ever did
make a mistake -- quite rare, I'm sure -- it would jump out at you in a way that
it might not amid a big list of numbers.

Of course you should do as you wish. But I find that my code communicates better
with tests that self-verify, that my programming goes more smoothly, and that
the tests help me come back up to speed on code I've been away from for a while.
The same effects might be there for you, if you want them.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/7/2003 12:33:19 PM
Isaac Gouy:
> Ron Jeffries:
>> The point is, it doesn't seem right to someone who knows the domain.

> We've left the domain of bowling - we're in the domain of computation.

I think we're in the intersection.

>> It is entirely true, I agree, that both a strike frame and a spare frame
>> score the same three balls, by simply adding them up. That's an
>> observation that /bowlers/ don't resonate with. They feel better when the
>> concept of strike and spare are separate in the code.

> "Frankly, my dear, I don't give a damn."
> 
> Do we ask racing car drivers to design racing cars?
> We ask designers to design racing cars, and drivers to race 'em.

Yes.

But I think Ron may have a point, albeit for the wrong reason. It's not the
bowlers who are going to have a problem, it's the non-bowlers. If you were
to ask a bowler how many balls count towards scoring a strike or a spare
I'd be surprised if they had to stop to work it out. Even more so if this
was the first time that they noticed that it was the same for both "mark"
types.

IANAB. Although I had bowled before, I hadn't looked into the scoring system
until this subject came up a year or so ago. I understood how the scoring
worked from Bob's example code before I knew what the actual rules were:

  if (strike())
    score += 10 + nextTwoBalls();
  else if (spare())
    score += 10 + nextBall();
  else
    score += twoBallsInFrame();

And it wasn't until I recently tried to remove the duplication that I
noticed the relationship between how "mark" frames are scored. At this
point I might have remarked to a bowler, possibly our customer, "hey, did
you know that the total score for a strike or spare is the number of pins
knocked down by three balls?" To which, I imagine the reply would be:
"well, DUH?!"

However, coming to your code as a non-bowling programmer I'd be unlikely to
intuitively guess that q[0]+q[1]+q[2] was the frame score for strikes and
spares. I'd probably need a bowler with me to make that leap. Some clues
would help, eg:

  if (q.size==3 && frames<10){
    assert(isStrike(q) || isSpare(q));
    total += q[0]+q[1]+q[2];
    ...

This tells me that the programmer was asserting that if q.size==3 and
frames<10 within this function, then it must be true that q contains either
a strike or a spare.

Another potential problem is that we are wiring an accidental relationship
between the rules into the algorithm. (By accidental I mean that there is
no explicit rule that states: strikes and spares must count the same number
of balls - and yet the algorithm relies on it.) 

Although the rules of ten-pin bowling are unlikely to change any time soon,
they certainly have changed. And there are many bowling variants including
five-pin, nine-pin, duckpin, and candlepin. If I had to adapt the algorithm
to work with a variant, and different accidental relationships, it would
help, as a non-bowling programmer, if I could see where these accidental
relationships came from, eg:

  total += sum(q, ballsInFrame(q)+bonusBalls(q));
  pop(q, ballsInFrame(q));

where:

  int bonusBalls( ArrayList<int> q ){
    assert(isStrike(q) || isSpare(q) || isOpen(q));
    if (isStrike(q)) return 2;
    if (isSpare(q))  return 1;
    if (isOpen(q))   return 0;
  }

  int ballsInFrame( ArrayList<int> q ){
    assert(isStrike(q) || isSpare(q) || isOpen(q));
    if (isStrike(q)) return 1; 
    if (isSpare(q))  return 2;
    if (isOpen(q))   return 2;
  }

0
12/7/2003 12:37:41 PM
Ron Jeffries wrote:
> When the next person comes along, he is likely to understand bowling, and
> to be looking for bowling ideas like strike, spare, and such in the code.
> If he doesn't find them, his work will be deterred.

I think when the next person comes along, if he understood bowling, he would
not have much trouble. However if he didn't understand bowling but only had
the requirements, then he'd be looking for ideas like strike, spare, and
such.
0
12/7/2003 12:40:41 PM
Paul Sinnett wrote:
> Ron Jeffries:
>> I didn't see that in the code, and I'm not convinced. The two ifs, with
>> the code removed, look like this:
>> 
>>          if (q.size==3 && frames<10){
>>           }
>>          if (q.size==2 && frames<10){
>>          }
>> 
>> It seems to me that these two blocks are entirely independent of order,
>> since only one of them can ever be true at any given time. Am I missing
>> something?

> You are. The contents of the first if block modify q. So it is possible
> for them both to be true given a strike followed by an open frame.

However I think it would be possible to refactor them such that they are the
same: 

  if (q.size==ballsRequiredForScore(q) && frames<10){
    total += sum(q, ballsRequiredForScore(q));
    pop(q, ballsInFrame(q));
    frames++;
    totalScore(total);
  }
  if (q.size==ballsRequiredForScore(q) && frames<10){
    total += sum(q, ballsRequiredForScore(q));
    pop(q, ballsInFrame(q));
    frames++;
    totalScore(total);
  }

where:

  int ballsRequiredForScore( ArrayList<int> q ){
    let minBallsRequiredForScore = 2;
    if (q.size<minBallsRequiredForScore){
      return minBallsRequiredForScore;
    } 
    else {
      return ballsInFrame(q)+bonusBalls(q);
    }
  }

Then we could remove the duplication by putting them in a loop:

  while (q.size==ballsRequiredForScore(q) && frames<10){
    total += sum(q, ballsRequiredForScore(q));
    pop(q, ballsInFrame(q));
    frames++;
    totalScore(total);
  }

0
12/7/2003 1:36:12 PM
Paul Sinnett wrote:
> Then we could remove the duplication by putting them in a loop:
> 
>   while (q.size==ballsRequiredForScore(q) && frames<10){
>     total += sum(q, ballsRequiredForScore(q));
>     pop(q, ballsInFrame(q));
>     frames++;
>     totalScore(total);
>   }

Perhaps make sum() sum the contents of q:

  while (q.size==ballsRequiredForScore(q) && frames<10){
    total += sum(q);
    pop(q, ballsInFrame(q));
    frames++;
    totalScore(total);
  }

0
12/7/2003 1:44:08 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...

>   When I demonstrate Test-Driven Development using the Bowling Game
>   example, I begin by describing the problem and inviting the attendees to
>   do a little up front design about what objcts we may need."
> 
>   http://www.xprogramming.com/xpmag/acsBowling.htm

1,2,3

1) *describing the problem*
Seems like we describe 'how to score the game'.
Have we described 'the problem' or 'how the game operates' or 'what to
do with the score'?

*the problem* 
- maybe we intend to bring scoring to bowling centers that don't have
automatic pinsetter machines! We'll have ScoreKeeper kiosk application
with a touch screen, and the bowlers will manually enter the roll by
pressing HTML buttons: Strike, Spare, (0..9) Pins Down on roll.

- maybe we are providing a tool to verify that previously recorded
scores are correct (the scores are in a spreadsheet being used as a
database).

- maybe we are providing a bowling center information system that will
record scores, display them on screens by the lane, and keep them for
patrons to review at a later date. (We want to keep track of splits,
washouts, fouls, strikes, spares...)

- maybe we are providing a basic score screen, fed with pin counts
from the pinsetter ccd camera or 'feelers'.

- maybe ...

These are all different problems. They'll have different solutions. We
need to choose one problem.

*how the game operates*
From the perspective of the bowler and the scorer - 'Each game, or
"line" of bowling, includes ten turns, or "frames" for the bowler'.
From the perspective of the pinsetter - each game includes 10..12
'racks' with 1..2 rolls.

*what to do with the score*
Do we pass the score to some other system or display it or...?
Do we need to associate the score with other information (the
individual roll scores)?

Given a specific problem context maybe we'll be able to figure out
which domains are static or dynamic (inert,reactive,active). "Software
Requirements & Specifications" Michael Jackson.

Maybe after some work, we'll know if the score calculator receive
rolls one-at-a-time or all-at-once or...; what is in the sequence of
rolls, an integer value? (what does it represent 'pins standing' or
'pins knocked down'); some other value? (Strike, Spare, Open,
Bonus...)

2) Maybe we don't know what 'the problem' is - but someone has done
the problem analysis and we do know what data will be received, what
data will be produced, and the rules for transforming input to output.
We are to design software for that specification.

We receive one-integer-at-a-time (representing 'pins knocked down'),
and produce the final total score whenever it can be calculated
(according to the scoring rules). For the sake of simplicity, we'll
use a synchronous call interface to deal with these events.

   void addScore(int pins)    // implemented by score calculator
   void totalScore(int score) // implemented by some other software

Or, we receive one-integer-at-a-time (representing 'pins knocked
down'), and produce a new running total whenever it can be calculated
(according to the scoring rules).

3) Seems like TDD initial tests for both acsBowlingProcedural &
acsBowling would check they supported the call interface? Then we
would have an apples apples comparison. We could work through a BUFD
(whatever that is) for the same specification, and maybe then make
some claim about which required less-work and which gave a
better-result.

best wishes, Isaac

"Perspective is worth 80 IQ points"
0
igouy (1009)
12/7/2003 5:35:45 PM
"Isaac Gouy" <igouy@yahoo.com> wrote in message

> "Universe" <universe@covad.net> wrote in message
news:<25568$3fd0d1f2$4069d048
> > This overall design is meant to address the "where does scoring info
> > come from" question.
> >
> > And yes it seems class Scorer might work as class Game.  Not sure
> > thinking about it.  Would like to hear peoples pros and cons.
IsaacG is
> > seems to have a very good design and algorithms.  I'm thinking the
> > following might supplement, fill out and possible provide a larger
> > "setting" for Isaac's designs and algorithms.

> Flattery is good!

Just telling it like it "t-i-s", 'tis.

> Although the knickname is too much like Ali G ;-)

?  Yours, mine?  How either way. Sorry I'm missing it.

> And I'm too set in my ways to learn C++.

Java/C# are 80% C++, or C++ is 80% Java/C#.

> One of the issues I have with the BowlingGame is with the problem
> description - the "where does scoring info come from" question. That's
> a separate thread.

I'm trying to get at a realistic context for the algorithms, and thus
have them shine even more.  Right one may just posit data, but in
trying to "frame" <g> a realistic context for the algorithms, fleshing
out the data sources seems to be major factor.

And really I hope that contextually "framing" things sheds light on
areas in everyone's nitty gritty algorithms that might call for
improvement.

Personally from my understanding of ten-pin rules, I find significant
things missing in all algorithms posted to date and at Jeffries site.

I really don't want to explicitly state what I find missing, but rather
hope that realistic context "framing" will make them apparent.  I want
to demonstrate that there is a need to allot sharp attention to major
domain abstractions because they have significant impact on code and its
algorithms.  It also demonstrates that leading coding by testing fails
to provide the most effective - quickest, most pervasive - basis for
achieving ultimately correctamundo code.

Elliott
-- 
The thing is that doing holistic investigation and creating
holistic plans at all times takes us as far ahead in terms of
understanding, as rapidly as possible while enabling us to
implement at all times in a way that supports what we
know will shortly be incorporated.


0
universe2 (613)
12/8/2003 5:03:41 AM
"Isaac Gouy" <igouy@yahoo.com> wrote in message


> - maybe we are providing a bowling center information system that will
> record scores, display them on screens by the lane, and keep them for
> patrons to review at a later date. (We want to keep track of splits,
> washouts, fouls, strikes, spares...)
>
> - maybe we are providing a basic score screen, fed with pin counts
> from the pinsetter ccd camera or 'feelers'.
>
> - maybe ...
>
> These are all different problems. They'll have different solutions. We
> need to choose one problem.
>
> *how the game operates*
> From the perspective of the bowler and the scorer - 'Each game, or
> "line" of bowling, includes ten turns, or "frames" for the bowler'.
> From the perspective of the pinsetter - each game includes 10..12
> 'racks' with 1..2 rolls.

It seems the XP sites are focused upon generating the score.  I get the
sense from them that the algorithm is being generated real time as a
game proceeds.

Hence I introduced the Sensor class to influence constructing code
system input and output flows facilitating the code's use to do a per
throw, real time scoring.

class Sensor { };
class PinSensor : extends Sensor { };
class LineSensor : extends Sensor { };
class GutterSensor : extends Sensor { };

Now I guess I'll be specific since wer are right in the middle of what I
have to say.

Pin state, whether or not the bowler crossed the foul line - enforcing
minimum game distance from the pins, and what Jeffries reminded about,
whether or not a gutter ball was thrown are major factors that have a
mandatory impact upon scoring.

Pin state is the term I use, because the first web on ten pin scroring
that Gouy posted [thanks beaucoup for all the pages.  All of Gouy's web
page references are informative, accurately objective and "fresh" fonts
of ten pin game info, especially on scoring of course.] made the point
that whether or not the pin triangle's leading #1 pin is left standing
has real impact the scoring is computed.

Hence I emphasized passing Pin[] and not just PiinNum to the scoring
algorithm.  The scoring algorithm, must be aware of what specific pins
are up/down:

    if (Pin[1])
    {
         // Do X;
         // ...
    }
    Else
    {
        // Do Y;
        // ...
    };


Elliott
-- 
      Substituting Refactoring For Analysis Modelling and Review
            When Facing Major High Level Design Problems
                 Is Like Drawing a Shade When Totally Dark



0
universe2 (613)
12/8/2003 5:47:57 AM
"Paul Sinnett" <paul.sinnett@btinternet.com> wrote in message

> Ron Jeffries wrote:
> > When the next person comes along, he is likely to understand
bowling, and
> > to be looking for bowling ideas like strike, spare, and such in the
code.
> > If he doesn't find them, his work will be deterred.

> I think when the next person comes along, if he understood bowling, he
would
> not have much trouble. However if he didn't understand bowling but
only had
> the requirements, then he'd be looking for ideas like strike, spare,
and
> such.

Still what would be the more intuitive, hence likely more "readable",
least likely to be misunderstood, and significantly have common
reference landmarks associated with all primary domain abstractions and
relationships?

Especially if the OO (more OO) spirited code did not have qualitatively
greater time, or space overhead, the wisdom of going with that code
fairly shouts itself out.

It's in large part about maintenance and enhancement developers being
empowered to optimally fulfill client use case requirements.

Elliott
-- 
Be bold, be artistically imaginative, yet scientifically grounded -- DO
GREAT  OO  MODELLING  WITH  EXPRESSIVE DOMAIN OBJECTS  OF  ALL  TYPES!!


0
universe2 (613)
12/8/2003 6:31:35 AM
On Sun, 07 Dec 2003 12:40:41 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:

>Ron Jeffries wrote:
>> When the next person comes along, he is likely to understand bowling, and
>> to be looking for bowling ideas like strike, spare, and such in the code.
>> If he doesn't find them, his work will be deterred.
>
>I think when the next person comes along, if he understood bowling, he would
>not have much trouble. However if he didn't understand bowling but only had
>the requirements, then he'd be looking for ideas like strike, spare, and
>such.

I thought so too. My audiences disagree with us, about 5 to 1.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/8/2003 7:14:55 AM
On Mon, 8 Dec 2003 00:47:57 -0500, "Universe" <universe@covad.net> wrote:

>> *how the game operates*
>> From the perspective of the bowler and the scorer - 'Each game, or
>> "line" of bowling, includes ten turns, or "frames" for the bowler'.
>> From the perspective of the pinsetter - each game includes 10..12
>> 'racks' with 1..2 rolls.
>
>It seems the XP sites are focused upon generating the score.  I get the
>sense from them that the algorithm is being generated real time as a
>game proceeds.
>
>Hence I introduced the Sensor class to influence constructing code
>system input and output flows facilitating the code's use to do a per
>throw, real time scoring.

That's fine. Recognize that you're solving a different problem than the article
asks to have solved, however. Maybe a more interesting problem, maybe one that
is somehow more like real bowling.

But when the instructor asks you for the sum of 2 and 2, and you give him the
product of 9 and 15 because it's a better problem, don't expect a good grade.

The exercise is chosen because it is the right size for an hour's work, and
because even though it is small, it leads to interesting results, as the
discussion here shows.

Other problems are interesting too. Pretty soon you'll give us a holistic
description of all the key use cases for whatever it is you are sketching, and
then maybe at some future time you'll write code that will actually compile, and
then maybe even test it. All those things will be interesting.

To me, code that runs and is clear and is tested is most valuable. Ideas do
attract my attention, I freely grant, but I try to focus on the point, which is
usually to create a program that someone wants. 

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/8/2003 7:19:37 AM
"Paul Sinnett" <paul.sinnett@btinternet.com> wrote in message

> Isaac Gouy:

> > Ron Jeffries:

>> Isaac Gouy:
> >> The point is, it doesn't seem right to someone who knows the
domain.

> > We've left the domain of bowling - we're in the domain of
computation.

> I think we're in the intersection.

Right.  Where Domain Experts and domain knowledgeable analysts, and
domain knowledgeable system architectects should be guiding the tone,
nature and direction of the system.

Who would doubt that domain based and referenced sytem code design
facilitates communication amongst the aforementioned individuals.

> >> It is entirely true, I agree, that both a strike frame and a spare
frame
> >> score the same three balls, by simply adding them up. That's an
> >> observation that /bowlers/ don't resonate with. They feel better
when the
> >> concept of strike and spare are separate in the code.

Right.  And using these abstractions "carves" up the code design into
corresponding code unit modules in such a way that boundaries of system
code modules more closely match potential reuse boundaries and other
code change, extension and enhancement boundary tectonics.

> > "Frankly, my dear, I don't give a damn."

The unfortunate xp/allie attitude that starts descent on the slippery
slope toward the kind of narrow, rigid and self-limiting viewpoint
characteristic of some well known die-hard xp/allie types.

> > Do we ask racing car drivers to design racing cars?
> > We ask designers to design racing cars, and drivers to race 'em.

Sorry, you don't what you are talking about.  Drivers are essential to
the design of racing boats - which I have piloted - racing cars, from my
close observation of the sport and in most other racing domains, from
what I best understand from various racing industry periodicals and
scientific journals like Scientific American, Popular Science,
Discovery, Physics Today, Science, etc.

Elliott
-- 
The thing is that doing holistic investigation and creating
holistic plans at all times takes us as far ahead in terms of
understanding, as rapidly as possible while enabling us to
implement at all times in a way that supports what we
know will shortly be incorporated.
-- 
                   *~* Theory Leads, Practice Verifies *~*


0
universe2 (613)
12/8/2003 7:21:03 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<rh66tvca7unpe1eb4o1k8t49u76jjgfpvm@4ax.com>...

> There are a number of simple programs out there which are no more complex
> than the queueing example -- and some are arguably less complex -- and which
> retain the flavor of bowling to a greater degree than the queueing example.

It would be fun to see the code for those simple programs.

Do they calculate incremental total scores - or just the final total?

We already know a simple algorithm for the final total.

The queueing version becomes interesting for incremental total scores.
Do we have a simple algorithm for incremental total scores?


> When the next person comes along, he is likely to understand bowling, and to
> be looking for bowling ideas like strike, spare, and such in the code. If he
> doesn't find them, his work will be deterred.

Is it really so likely that they will understand bowling? 
I didn't. 
What if the development team is in China?


> >> It is entirely true, I agree, that both a strike frame and a spare frame
> >> score the same three balls, by simply adding them up. That's an 
> >> observation that /bowlers/ don't resonate with. They feel better when the
> >> concept of strike and spare are separate in the code.
> >
> >"Frankly, my dear, I don't give a damn."
> >
> >Do we ask racing car drivers to design racing cars?
> >We ask designers to design racing cars, and drivers to race 'em.
-snip-

> Racing is a collaboration, not a process where cars are thrown over the wall 

I agree. And yet we don't ask racing drivers to design racing cars. We
bring their ongoing experience of the car into the design process. And
then we ask designers to design.

Why do we care if /bowlers/ feel better when the concept of strike and
spare are separate in the code? Why are /bowlers/ reading the code?

-snip-
> Like you, I discovered that strike and spare both process the same three
> balls to get the frame score...

I looked at a bowling scorecard, and manually worked through scoring.
http://www.bowling.uklinux.net/bowling/score.php3

The scorecard displays scores for the game.
It also provides a visual structure for calculating scores.

Do you manually work through a bowling scorecard when you're teaching
class?

> I consider the fact that a very large proportion of my peers find that to be
> too cryptic and say that they wouldn't do it to be a very important bit of
> feedback on how cryptic I should make my code.

Maybe I will too. When I see the other implementions for this problem.

> I'd try to do something to clarify or explain it to the next reader.
 
Yes, there are algorithms that really do benefit from code comments. 
But you have to love 'q' it's so 1967, so BCPL ;-)

> If there was no critical need to tighten it up, I'd leave it
> in the form which has been found to be more clear by a large number of
> programmers.

I'm looking forward to seeing that clear code.

best wishes, Isaac
0
igouy (1009)
12/8/2003 8:39:34 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<ib76tvsaee4oct8vujeqett5i6ekqealpm@4ax.com>...

> Of course you should do as you wish. 

That's very gracious. 

best wishes, Isaac
0
igouy (1009)
12/8/2003 8:47:13 AM
Ron Jeffries:
> Paul Sinnett:
>> Ron Jeffries:

>>> When the next person comes along, he is likely to understand bowling,
>>> and to be looking for bowling ideas like strike, spare, and such in the
>>> code. If he doesn't find them, his work will be deterred.

>> I think when the next person comes along, if he understood bowling, he
>> would not have much trouble. However if he didn't understand bowling but
>> only had the requirements, then he'd be looking for ideas like strike,
>> spare, and such.

> I thought so too. My audiences disagree with us, about 5 to 1.

They disagree that the code should contain bowling terms?
0
12/8/2003 9:36:31 AM
On Mon, 08 Dec 2003 09:36:31 +0000, Paul Sinnett <paul.sinnett@btinternet.com>
wrote:

>>>> When the next person comes along, he is likely to understand bowling,
>>>> and to be looking for bowling ideas like strike, spare, and such in the
>>>> code. If he doesn't find them, his work will be deterred.
>
>>> I think when the next person comes along, if he understood bowling, he
>>> would not have much trouble. However if he didn't understand bowling but
>>> only had the requirements, then he'd be looking for ideas like strike,
>>> spare, and such.
>
>> I thought so too. My audiences disagree with us, about 5 to 1.
>
>They disagree that the code should contain bowling terms?

No, sorry for the confusion. I was talking about that final optimization where
spare and strike merge. I believe that the optimization where we have the same
code for strike and spare, adding up three balls starting from the first throw,
is an interesting and perhaps reasonable way to express the code.

My audiences prefer the version where strike and spare are separated in the
code, even though the combination is possible.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/8/2003 2:06:09 PM
On 8 Dec 2003 00:39:34 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>
>> When the next person comes along, he is likely to understand bowling, and to
>> be looking for bowling ideas like strike, spare, and such in the code. If he
>> doesn't find them, his work will be deterred.
>
>Is it really so likely that they will understand bowling? 
>I didn't. 

That may be why your algorithm looks so little like the actual domain. On the
one hand, perhaps not being tied to the domain may make it easier to find
creative solutions. On the other hand, I prefer to see code that looks like the
problem as understood by the customer.

>What if the development team is in China?

Even in China, I think that code should look like the domain where possible.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/8/2003 2:08:17 PM
On 8 Dec 2003 00:47:13 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<ib76tvsaee4oct8vujeqett5i6ekqealpm@4ax.com>...
>
>> Of course you should do as you wish. 
>
>That's very gracious. 

Well, I suppose it is, but that's not my intent. I just don't think that people
should be forced to do things in ways that are not to their liking. What I would
like is for people to try ideas that are new to them and of potential benefit,
and to make their decision based on actual experimentation and not on emotional
reaction. (I'm not suggesting that you are doing that, but we do see it
happening here from time to time.)

Regards,

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/8/2003 2:10:20 PM
"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message

> My audiences prefer the version where strike and spare are separated
in the
> code, even though the combination is possible.

Good for them!

Elliott
-- 
Be bold, be artistically imaginative, yet scientifically grounded -- DO
GREAT  OO  MODELLING  WITH  EXPRESSIVE DOMAIN OBJECTS  OF  ALL  TYPES.


0
universe2 (613)
12/8/2003 2:52:18 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<v998tvkg3cc77ska646suc0no27ira8u6o@4ax.com>...

> Recognize that you're solving a different problem than the article
> asks to have solved, however. 
-snip- 
> But when the instructor asks you for the sum of 2 and 2, and you give him the
> product of 9 and 15 because it's a better problem, don't expect a good grade.

Let's apply that standard.

acsBowling solves a different problem than acsBowlingProcedural -
which will the instructor fail?

best wishes, Isaac
0
igouy (1009)
12/8/2003 4:26:21 PM
"Universe" <universe@covad.net> wrote in message news:<86afa$3fd41090$42a7ec70$1694@msgid.meganewsservers.com>...

> It seems the XP sites are focused upon generating the score.

Well, they have different ideas about what 'the score' means, and they
have different ideas about what data is available for calculating the
score.

We're asked to compare Apples, Lemons, and Oranges.


The interface for this one, accepts int one-at-a-time, and provides
running total scores:
   http://www.objectmentor.com/resources/articles/xpepisode.htm

The interface for this one, accepts OpenFrame, Spare, Strike,
BonusRoll one-at-a-time, and provides the final total score for a
complete game:
   http://www.xprogramming.com/xpmag/acsBowling.htm

The interface for this one, accepts int one-at-a-time, and provides
the final total score for a complete game:
   http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm


Seems perfectly reasonable to have a limited problem for teaching
purposes. When we're asked to compare different designs, then they
should be doing the same thing.

best wishes, Isaac
0
igouy (1009)
12/8/2003 5:26:23 PM
"Universe" <universe@covad.net> wrote in message news:<24967$3fd49026$40692e39$7599@msgid.meganewsservers.com>...
> "Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
> 
> > My audiences prefer the version where strike and spare are separated
> > in the code, even though the combination is possible.

I'm surprised that this is being discussed as though there's some kind
of difficulty in taking 'the queue algorithm' and refactoring it to
some desired state of 'domain expressiveness'.

As it's so trivial, let's add a Nice contract assertion on the input
values:

   class ScoreKeeper {
      ArrayList<int> q = new ArrayList(3);
      int total = 0;
      int frames = 1;

      void addScore(int pinsKnockedDown)
         requires
            pinsKnockedDown>=0 && pinsKnockedDown<=10 
               :"pinsKnockedDown invalid score";


      addScore(int pinsKnockedDown){
         q.add(pinsKnockedDown);

         if (q.size==3 && frames<=10){
            if (this.isStrike){
               total += this.strikeScore + this.strikeBonusScore;
               this.fillStrikeScore();   
            } 
            else if (this.isSpare){
               total += this.spareScore + this.spareBonusScore;
               this.fillFrameScore();    
            }      
          }

         if (q.size==2 && frames<=10){
            if (this.isOpen){
               total += this.openScore;
               this.fillFrameScore();  
            }      
         }
      }

      boolean isStrike() = q[0] == 10;
      int strikeScore() = q[0];
      int strikeBonusScore() = q[1] + q[2];

      boolean isSpare() = q[0] + q[1] == 10;
      int spareScore() = q[0] + q[1];
      int spareBonusScore() = q[2];

      boolean isOpen() = q[0] + q[1] < 10;
      int openScore() = q[0] + q[1];

      void fillStrikeScore(){
         this.fillScore(); 
      }

      void fillFrameScore(){
         q.removeAt(0); 
         this.fillScore(); 
      }

      void fillScore(){
         q.removeAt(0); 
         frames++;
         totalScore(total); 
      }
   }

Still doesn't answer the interesting question, the question that
distinguishes this from other algorithms - what's 'q' for?

best wishes, Isaac
0
igouy (1009)
12/8/2003 8:06:45 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<6b19tvo5j2ksb8u9tiscvbrccrhptt6ku1@4ax.com>...
> On 8 Dec 2003 00:39:34 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
> >Is it really so likely that they will understand bowling? 
> >I didn't. 
> 
> That may be why your algorithm looks so little like the actual domain. 

Seemed like we'd already established that the algorithm is a faithful
representation of scoring a bowling game?

> On the one hand, perhaps not being tied to the domain may make it easier to
> find creative solutions. On the other hand, I prefer to see code that looks
> like the problem as understood by the customer.

Looking for the structure underlying surface differences may make it
easier to find algorithms - abstraction? Maybe being too caught up in
the surface details can get in the way of problem solving.

best wishes, Isaac
0
igouy (1009)
12/8/2003 8:39:59 PM
"Universe" <universe@covad.net> wrote in message news:<9ada4$3fd42684$42a7ebfb$3109@msgid.meganewsservers.com>...

> "Paul Sinnett" <paul.sinnett@btinternet.com> wrote in message
> > > We've left the domain of bowling - we're in the domain of
> computation.
> 
> > I think we're in the intersection.

Which is to say, both in the domain of bowling and in the domain of
computation. Or as Michael Jackson might say, the domains share
phenomena.

> > > "Frankly, my dear, I don't give a damn." 
> The unfortunate xp/allie attitude

Probably not the attitude I was expressing ;-)
 
> > > Do we ask racing car drivers to design racing cars?
> > > We ask designers to design racing cars, and drivers to race 'em. 
> Sorry, you don't what you are talking about.  

That's frequently true, but not necessarily so in this case.

> Drivers are essential to the design of racing boats... racing cars...

Did I say otherwise? We don't ask racing drivers to design racing
cars. We bring their ongoing experience of the car into the design
process. And then we ask designers to design.

best wishes, Isaac
0
igouy (1009)
12/8/2003 10:09:28 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<6b19tvo5j2ksb8u9tiscvbrccrhptt6ku1@4ax.com>...
-snip-
> I prefer to see code that looks like the problem as understood by the
> customer.

Then we should be concerned about the mismatch between the 10 frames
that make up a bowling game, and the up to 12 frames that can make up
a game in the implementation?

   class BonusRoll : Frame
In the game, the bonus rolls are part of the 10th frame.
In the implementation, the bonus rolls are an 11th and 12th frame.

http://www.xprogramming.com/xpmag/acsBowling.htm

best wishes, Isaac
0
igouy (1009)
12/8/2003 10:59:27 PM
Isaac Gouy wrote:

> I'm surprised that this is being discussed as though there's some kind
> of difficulty in taking 'the queue algorithm' and refactoring it to
> some desired state of 'domain expressiveness'.
> 
> As it's so trivial, let's add a Nice contract assertion on the input
> values:
>
> <snip example>

This is good in that you have placed the algorithm within a domain context.
But you have also added duplication to do so. Still, it is not hard, as you
say, to remove this duplication without losing the context. To do that I'd
first exaggerate the duplication.

The pair of conditionals:

  if (q.size==3 && frames<=10){

and:

  if (q.size==2 && frames<=10){
    if (this.isOpen){

are actually duplicates. It's not immediately obvious but they could both be
written:

  if (isScorable(q) && frames<=10){

where:

  boolean isScorable(ArrayList<int> q) {
    return q.size==3 || (q.size==2 && isOpen(q));
  }

  boolean isOpen(ArrayList<int> q){
    return q[0]+q[1]<10;
  }

Now the contents of both conditionals can be written:

  if (this.isStrike){
    total += this.strikeScore + this.strikeBonusScore;
    this.fillStrikeScore();
  }
  else if (this.isSpare){
    total += this.spareScore + this.spareBonusScore;
    this.fillFrameScore();
  }
  else{
    assert(this.isOpen);
    total += this.openScore;
    this.fillFrameScore();
  }

The preconditions inside both conditionals prevent either isStrike or
isSpare from being true when q.size==2 and (as I mentioned in an earlier
post) if q.size==3 then isOpen must be false.

Of course then the conditionals are exact duplicates, so we can remove them
by making them into a loop:

  while (isScorable(q) && frames<=10){
    if (this.isStrike){
      total += this.strikeScore + this.strikeBonusScore;
      this.fillStrikeScore();
    }
    else if (this.isSpare){
      total += this.spareScore + this.spareBonusScore;
      this.fillFrameScore();
    }
    else{
      assert(this.isOpen);
      total += this.openScore;
      this.fillFrameScore();
    }
  }

There is still duplication here and we can make it more obvious still by
filling in some blanks:

  void fillSpareScore(){
    q.removeAt(0);
    this.fillScore();
  }

  void fillOpenScore(){
    q.removeAt(0);
    this.fillScore();
  }

  int openBonusScore() = 0;

Now we can write 3 statements of the form:

  if (this.isX){
    total += this.XScore + this.XBonusScore;
    this.fillXScore();
    continue;
  }

where X is strike, spare, and open. So it ought to be possible to remove
this duplication and yet still have the concepts of open, strike, and spare
expressed in the code.

You (and Ron) have a slight domain concept error in isSpare. More correctly
isSpare would be:

  boolean isSpare() = !isStrike && q[0]+q[1]==10;

With this the three statements can be processed in any order - otherwise
spare must be checked after strike to avoid the possibility of a strike
being processed as a spare.

An interesting fact here is that the frame total for each variants can be
expressed more simply as:

  total += sum(q);

Is there a way to code this simplification without losing the domain
concepts of open, strike, and spare?

> Still doesn't answer the interesting question, the question that
> distinguishes this from other algorithms - what's 'q' for?

'q' acts both as a state to tell us when a frame can be scored and the total
of that score when we can. Maybe there's an object here?

0
12/9/2003 2:01:39 AM
Isaac Gouy wrote:

> Then we should be concerned about the mismatch between the 10 frames
> that make up a bowling game, and the up to 12 frames that can make up
> a game in the implementation?
> 
>    class BonusRoll : Frame
> In the game, the bonus rolls are part of the 10th frame.
> In the implementation, the bonus rolls are an 11th and 12th frame.

Indeed.

0
12/9/2003 2:04:46 AM
On 8 Dec 2003 12:39:59 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<6b19tvo5j2ksb8u9tiscvbrccrhptt6ku1@4ax.com>...
>> On 8 Dec 2003 00:39:34 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
>> >Is it really so likely that they will understand bowling? 
>> >I didn't. 
>> 
>> That may be why your algorithm looks so little like the actual domain. 
>
>Seemed like we'd already established that the algorithm is a faithful
>representation of scoring a bowling game?

No. I agree that it gets the right answer. I do not agree that it is a
representation of people's understanding of how to score bowling.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/9/2003 6:12:33 AM
On 8 Dec 2003 14:59:27 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<6b19tvo5j2ksb8u9tiscvbrccrhptt6ku1@4ax.com>...
>-snip-
>> I prefer to see code that looks like the problem as understood by the
>> customer.
>
>Then we should be concerned about the mismatch between the 10 frames
>that make up a bowling game, and the up to 12 frames that can make up
>a game in the implementation?

Yes, in my opinion we should. Someone who understands bowling knows that there
are only ten frames. Seeing larger frame numbers will beging by confusing him,
even if later on he figures it out.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/9/2003 6:13:38 AM
On 8 Dec 2003 08:26:21 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<v998tvkg3cc77ska646suc0no27ira8u6o@4ax.com>...
>
>> Recognize that you're solving a different problem than the article
>> asks to have solved, however. 
>-snip- 
>> But when the instructor asks you for the sum of 2 and 2, and you give him the
>> product of 9 and 15 because it's a better problem, don't expect a good grade.
>
>Let's apply that standard.
>
>acsBowling solves a different problem than acsBowlingProcedural -
>which will the instructor fail?

Neither. Each solves the problem it was asked to solve.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/9/2003 6:14:23 AM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312061114.7fec9af2@posting.google.com>...

> [ snip ... ]

Isaac,

Here is a different variation on the theme.
The core of this Java impl was done by me many moons ago against the
original bowling game article on the Object Mentor website. Unlike
their pair-developed impl, mine correctly computed scores after any
throw (amongst other things) .

I have quickly rearranged stuff cos bowling game semantics (ie valid
throw correctness) is not required for this thread. It is not compiled
or tested, as I only want to show the (different) logic.

Basically, each time a frame is completed, one needs to indicate
whether the score for that frame needs to be adjusted, and by how many
successive throws. At any time during a game. either zero/one/two
frames will be subject to score adjustment. Each time the next throw
is made, the frames in question have score updates, and amend their
remaining adjustment. Once a frame has zero adjustments, its score is
by definition completed.


Regards,
Steven Perryman

-------------------------------------------

public
class TPScorer
{

// Constructors/Destructors

public
TPScorer() {}


// pre: PinsHit IN [0,PINS]

public
void
attemptMade(short iPinsHit)
{
    ivThrow++ ;
    recordThrow(ivFrame,iPinsHit) ;

    if(ivFrame < FRAMES)
    {
             if(ivScore[ivFrame - 1] == PINS)
        {
           adjustScores(ivFrame, 3 - ivThrow) ;
           newFrame() ;
        }
        else if(ivThrows == 2) { newFrame() ; }
    }
}

private
void
newFrame()
{
    ivFrame++ ;
    ivThrows = 0 ;
}


public
short
frames() { return ( (short) (ivFrame + 1) ; }


// post: RESULT IN [0, frames() * 3 * PINS]

public
short
total()
{
    short oTotal = 0 ;

    for( short iter = 0 ; iter != ivFrame ; iter++ )
    {
        oTotal += ivScore[iter] ;
    }

    return oTotal ;
}


// pre: Frame IN [1, frames() ]
// post: RESULT IN [0, 3 * PINS]

public
short
score(short iFrame) { return ivScore[iFrame - 1] ; }


// Indicate that the score for the given frame will be adjusted by the
// scores made by the given successive throws.

private
void
adjustScores(short iFrame,
             short iThrows)
{
    short iter = 0 ;

    if(ivAdjustThrows[iter] > 0) { iter++ ; }

    ivAdjustFrame[iter] = (short) (iFrame - 1) ;
    ivAdjustThrows[iter] = iThrows ;
}

private
void
recordThrow(short iFrame,
            short iPinsHit)
{
    // Update the score of the given frame
    ivScore[iFrame - 1] += iPinsHit ;

    // Adjust the scores of the previous denoted frames (if any)
    for( short iter = 0 ; iter != TPScorer.CanAdjust ; iter++ )
    {
        if(ivAdjustThrows[iter] > 0)
        {
            ivScore[ivAdjustFrame[iter] ] += iPinsHit ;
            ivAdjustThrows[iter]-- ;
        }
    }
}


// Constants

// The maximum number of frames whose scores can be adjusted by any
// throw made.
private
static final
short CanAdjust = 2 ;


// Instance variables

private
short ivFrame = 1 ;

private
short ivThrow = 0 ;

private
short[] ivScore = new short[FRAMES] ;

private
short[] ivAdjustFrame = new short[TPScorer.CanAdjust] ;

private
short[] ivAdjustThrows = new short[TPScorer.CanAdjust] ;


}
0
ggroups5 (201)
12/9/2003 11:23:20 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<j1qatv4an6js2hcapbv67074lfvj7r4g68@4ax.com>...
> On 8 Dec 2003 08:26:21 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
> 
> >Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<v998tvkg3cc77ska646suc0no27ira8u6o@4ax.com>...
> >
> >> Recognize that you're solving a different problem than the article
> >> asks to have solved, however. 
>  -snip- 
> >> But when the instructor asks you for the sum of 2 and 2, and you give him the
> >> product of 9 and 15 because it's a better problem, don't expect a good grade.
> >
> >Let's apply that standard.
> >
> >acsBowling solves a different problem than acsBowlingProcedural -
> >which will the instructor fail?
> 
> Neither. Each solves the problem it was asked to solve.

How can that be?

"The Story, Again ...
I'm solving the same problem as in the preceding BowlingGame article."

http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm

best wishes, Isaac
0
igouy (1009)
12/9/2003 3:02:55 PM
I know that I'm more interested in creating an optimal OO design
Scorer/ScoreKeeper code for the real/actual tenpin bowling game.

Then there are no shenanigans on changing the reqs. and about  what the
scope of the code is about in terms of all use cases as a whole.

And so far I think there have been good design ideas and formulations
put out.

Gouy may be finished, not sure.

Within a week I'll post my final design based upon what I have posted so
far. And I'll include code.

Elliott


0
universe2 (613)
12/9/2003 3:33:04 PM
"S Perryman" <ggroups@bigfoot.com> wrote in message

> Basically, each time a frame is completed, one needs to indicate
> whether the score for that frame needs to be adjusted, and by how many
> successive throws. At any time during a game. either zero/one/two
> frames will be subject to score adjustment. Each time the next throw
> is made, the frames in question have score updates, and amend their
> remaining adjustment. Once a frame has zero adjustments, its score is
> by definition completed.

Right.  And it's along these scoring guidelines that I'm designing code.

Elliott


0
universe2 (613)
12/9/2003 3:35:07 PM
ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0312090323.26f0049c@posting.google.com>...

> Here is a different variation on the theme.
> The core of this Java impl was done by me many moons ago against the
> original bowling game article on the Object Mentor website.

Thank you.

Both these implementations provide a method to return the ith frame
total. I've been looking at something slightly different: providing
the frame total whenever it becomes available, using a simple call-
void totalScore(int score).

Where would you do that call?
What would you do differently if you didn't need to return the ith
frame total?

So I can compare to this:

   class ScoreKeeper {
      ArrayList<int> q = new ArrayList(3);
      int total = 0;
      int frames = 0;
      boolean noteEachTotal;

      void addScore(int pinsKnockedDown){
         q.add(pinsKnockedDown);

         if (q.size==3 && frames<10){
            total += q[0]+q[1]+q[2];

            if (q[0]==10){ q.removeAt(0); }
            else         { q.removeAt(0); q.removeAt(0); }

            frames++;
            if (noteEachTotal||frames==10) totalScore(total);        
          }

         if (q.size==2 && frames<10){
            if (q[0]+q[1]<10){
               total += q[0]+q[1];

               q.removeAt(0); q.removeAt(0);

               frames++;
               if (noteEachTotal||frames==10) totalScore(total);   
            }      
         }
      }
   }


void totalScore(int score){
   print(score); print(" ");
}


best wishes, Isaac
0
igouy (1009)
12/9/2003 9:07:26 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<supatvgbpms8lspumjbu1q1efngrjc54r0@4ax.com>...

> >Then we should be concerned about the mismatch between the 10 frames
> >that make up a bowling game, and the up to 12 frames that can make up
> >a game in the implementation?
> 
> Yes, in my opinion we should. Someone who understands bowling knows that
> there are only ten frames. Seeing larger frame numbers will begin by
> confusing him, even if later on he figures it out.

Let me thank you for writing the articles, and engaging in these
public discussions. To me, the interesting thing about this particular
issue is the design you didn't do rather than what you did. Curiousity
- rather than criticism.

If we look at the article, the 10th frame differences are there in the
scoring rules. They aren't mentioned in the 'design idea'.

Later:
'Now let's get to the tenth frame issue.... It seems to me that the
bonus roll really only needs to add the balls to the throws table --
it need not store any objects in the frames list. But it seems wrong
to do that, because right now, the frame list correctly describes the
structure of the game, and every throw is "owned" by a Frame.'

So-far so-good, then:
'To preserve that symmetry, I'm inclined to build a new Frame subclass
even though it is unnecessary. It seems a reasonable decision.'

For reason or reasons unknown, we broke the correspondence between the
frame list and the structure of the game - even while we were trying
to preserve it.


Maybe it's a reaction against some of the designs that come up during
TDD demonstrations? (TenthFrame classes; conditional test within each
Strike, Spare and Open?)

Would more up-front design have avoided this issue?

best wishes, Isaac
0
igouy (1009)
12/9/2003 10:04:42 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd52d69@news.totallyobjects.com>...

> Still, it is not hard, as you
> say, to remove this duplication without losing the context.
-snip-

Perhaps you'd like to post the final version? 

-snip-
> > Still doesn't answer the interesting question, the question that
> > distinguishes this from other algorithms - what's 'q' for?
> 
> 'q' acts both as a state to tell us when a frame can be scored and the total
> of that score when we can. Maybe there's an object here?

'q' is a computational artifact something that has no obvious presence
in the applicaton domain. Although it is possible to see glimpse it on
the bowling scorecard, as you add new roll scores and incrementally
calculate totals.

best wishes, Isaac
0
igouy (1009)
12/9/2003 10:22:31 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<ptpatvsi2irprr1s8grj5dcma1ene4er4d@4ax.com>...

> >> That may be why your algorithm looks so little like the actual domain. 
> >
> >Seemed like we'd already established that the algorithm is a faithful
> >representation of scoring a bowling game?
> 
> No. I agree that it gets the right answer. I do not agree that it is a
> representation of people's understanding of how to score bowling.

Are we talking about how they would describe the scoring rules of
bowling to someone, or are we talking about what they would say
as-they-filled in a paper bowling scorecard during a game?

(Do paper bowling scorecards exist? Did they ever exist?)

best wishes, Isaac
0
igouy (1009)
12/9/2003 10:33:18 PM
"Isaac Gouy" <igouy@yahoo.com> wrote in message
news:ce7ef1c8.0312091433.1f2d7734@posting.google.com...
> Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message
news:<ptpatvsi2irprr1s8grj5dcma1ene4er4d@4ax.com>...
>
> > >> That may be why your algorithm looks so little like the actual
domain.
> > >
> > >Seemed like we'd already established that the algorithm is a
faithful
> > >representation of scoring a bowling game?
> >
> > No. I agree that it gets the right answer. I do not agree that it is
a
> > representation of people's understanding of how to score bowling.
>
> Are we talking about how they would describe the scoring rules of
> bowling to someone, or are we talking about what they would say
> as-they-filled in a paper bowling scorecard during a game?

I'm striving to automate precisely this.

> (Do paper bowling scorecards exist? Did they ever exist?)

Yes, I've seen them reflected up into rear projectors so that all
concerned on the benches could see the scores.  The image had the
horizontal line of 3 part frames just like the graphic of frames on the
page of the 1st ten pin scoring url you referred us to.  "Bowled" frames
were scored and "unbowled" frames were empty.

Elliott
-- 
Though types only make sense relative to context, system worthwhile
types
are *objectively* significant for the relative context.


0
Universe
12/10/2003 2:33:12 AM
On 9 Dec 2003 07:02:55 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<j1qatv4an6js2hcapbv67074lfvj7r4g68@4ax.com>...
>> On 8 Dec 2003 08:26:21 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
>> 
>> >Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<v998tvkg3cc77ska646suc0no27ira8u6o@4ax.com>...
>> >
>> >> Recognize that you're solving a different problem than the article
>> >> asks to have solved, however. 
>>  -snip- 
>> >> But when the instructor asks you for the sum of 2 and 2, and you give him the
>> >> product of 9 and 15 because it's a better problem, don't expect a good grade.
>> >
>> >Let's apply that standard.
>> >
>> >acsBowling solves a different problem than acsBowlingProcedural -
>> >which will the instructor fail?
>> 
>> Neither. Each solves the problem it was asked to solve.
>
>How can that be?
>
>"The Story, Again ...
>I'm solving the same problem as in the preceding BowlingGame article."
>
>http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm

Yes, but I'm taking a different, and simpler /approach/. A different initial
start, different design idea. Same problem, same requirements, different
implementation. N'est pas?

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/10/2003 3:35:27 AM
On Tue, 9 Dec 2003 10:33:04 -0500, "Universe" <universe@covad.net> wrote:

>I know that I'm more interested in creating an optimal OO design
>Scorer/ScoreKeeper code for the real/actual tenpin bowling game.
>
>Then there are no shenanigans on changing the reqs. and about  what the
>scope of the code is about in terms of all use cases as a whole.
>
>And so far I think there have been good design ideas and formulations
>put out.
>
>Gouy may be finished, not sure.
>
>Within a week I'll post my final design based upon what I have posted so
>far. And I'll include code.

Please keep track of the amount of time you spend. I wish I had kept closer
track of mine, but I can do the procedural version in a public demonstration,
explaining each step and answering questions, in about an hour or ninety
minutes.

One reason for wishing people would solve the same problem would be to get some
kind of very rough comparison of the cost of the approaches. It would be rough
for many reasons, not least being that I rarely make a misstep in doing the
procedural version because I know the path fairly well. Someone doing this for
the first time doesn't have that advantage.

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/10/2003 3:37:53 AM
"Ron Jeffries" <ronjeffries@REMOVEacm.org> wrote in message
news:j25dtv4i0a5qvie0sfs14vf3s9p2t9cnk2@4ax.com...

> Yes, but I'm taking a different, and simpler /approach/. A different
initial
> start, different design idea. Same problem, same requirements,

No, of course not.  Similar, but the real game scoring is more complex.
Also I don't know if you were doing per throw Frame scroring as soon as
it's possible to update Frames, like a real bowling games scoring.

Eliott
-- 
                   ~ Theory Leads, Practice Verifies ~
-- 
      We Don't Need US Fat Cat's Geopolitical Hegemony!
           People of the World Demand US Stop Now!


0
Universe
12/10/2003 8:27:27 AM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312091307.4b430b0d@posting.google.com>...

> ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0312090323.26f0049c@posting.google.com>...

> > Here is a different variation on the theme.
> > The core of this Java impl was done by me many moons ago against the
> > original bowling game article on the Object Mentor website.

> Thank you.

No problem.

> Both these implementations provide a method to return the ith frame
> total. I've been looking at something slightly different: providing
> the frame total whenever it becomes available, using a simple call-
> void totalScore(int score).

> Where would you do that call?

TPScorer.total() ?? :-)


> What would you do differently if you didn't need to return the ith
> frame total?

Good point.
See a posting of mine today to Ron Jeffries in the "Test Driven
Development" thread.

Because I use far more formal/rigorous testing techniques (DFT,
Specification Directed Testing etc) than most, all I would do is make
the score(Frame) op private. I would then provide an invariant that
calls score(Frame) for frames [1,ivFrame] to ensure the subject domain
invariant (score(Frame) IN [0,30] ) has not been violated.

What you have to understand is that I am not driven by trivia such as
LOC counts. I am driven by issues such as the ROI required to ensure my
code is as testable as possible with the minimum number of black box
tests (ie the Specification Directed Testing method) .

If that means higher LOC counts, so be it.


> [ snip ... ]


Regards,
Steven Perryman
0
ggroups5 (201)
12/10/2003 11:01:43 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<j25dtv4i0a5qvie0sfs14vf3s9p2t9cnk2@4ax.com>...

> >"The Story, Again ...
> >I'm solving the same problem as in the preceding BowlingGame article."
> >
> >http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm
> 
> Yes, but I'm taking a different, and simpler /approach/. A different initial
> start, different design idea. Same problem, same requirements, different
> implementation. N'est pas?

Non. Similar requirements - not the same.

Either they meet different requirements or one of the implementations
is incomplete.

acsBowling is missing conversion from 'Roll(int roll)' to 'OpenFrame,
Spare, Strike, BonusRoll'; or acsBowlingProcedural is missing
conversion from 'OpenFrame, Spare, Strike, BonusRoll' to 'Roll(int
roll)'.

(We wouldn't even be able to compile acsBowling with the tests from
acsBowlingProcedural, let alone have them succeed.)

best wishes, Isaac
0
igouy (1009)
12/10/2003 4:16:09 PM
"Universe" <universe.covad.com> wrote in message news:<17fa8$3fd685ed$4069d019$1003@msgid.meganewsservers.com>..

> > Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message
>  news:<ptpatvsi2irprr1s8grj5dcma1ene4er4d@4ax.com>...
> > > No. I agree that it gets the right answer. I do not agree that it is
> > > a representation of people's understanding of how to score bowling.

> > (Do paper bowling scorecards exist? Did they ever exist?)
> 
> Yes, I've seen them reflected up into rear projectors so that all
> concerned on the benches could see the scores.  The image had the
> horizontal line of 3 part frames just like the graphic of frames on the
> page of the 1st ten pin scoring url you referred us to.  "Bowled" frames
> were scored and "unbowled" frames were empty.

Then it seems possible, that we may have people's 'formal'
understanding of how to score bowling, and people's 'operational'
understanding of how to score bowling. The usual difference between
what people tell you they do, if you ask them; and what you find out
they do, if you watch them perform the task.

best wishes, Isaac
0
igouy (1009)
12/10/2003 5:18:58 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...

"What makes this game interesting to score is the lookahead in the
scoring for strike and spare. At the time we throw a strike or spare,
we cannot calculate the frame score: we have to wait one or two frames
to find out what the bonus is."
http://www.xprogramming.com/xpmag/acsBowling.htm

Alternatively, we can 'weight' the contribution each roll score makes
to the final total with a multiplier 'm'.

For example, rolls 5,3 in the sequence - X 5 3 - will contribute once
as part of the bonus score for X, and once more in their own right. So
at the time we throw a strike, we know that the next two rolls will
contribute by 2x (multiplier=2) to the total score.

There may be sequences like X X 5 3 where the third roll will
contribute once for the first strike, once for the second strike, and
once in it's own right (multiplier=3).

Like the 'q' implementation, this 'm' implementation takes
responsibility for knowing when the final total score can be
calculated - so we need 10th frame book-keeping which some of the
other implementations avoid (they provide a score when asked, not when
it's ready).

best wishes, Isaac


   class ScoreKeeper {
      int total = 0;
      int m1 = 1;
      int m2 = 1;
      int knockedDown = 0;
      boolean hasNewRack = true;
      int racks = 0;

      int bonus = 0;
      int count = 0;

      void addScore(int pinsKnockedDown){
         total += pinsKnockedDown * m1;
         m1 = m2; m2 = 1; 
       
         if (pinsKnockedDown == 10){
            m1++; m2 = 2;
            racks++;
         }
         else {
            if (hasNewRack){
               hasNewRack = false;
               knockedDown = pinsKnockedDown;
            }
            else {
               if (knockedDown + pinsKnockedDown == 10) m1++;
               hasNewRack = true;
               racks++;
            }
         }

      // 10th frame - check if we've calculated the final score
         if (racks>=10) {
            if (bonus==0){
               if (pinsKnockedDown == 10) bonus = 2;
               else 
                  if (knockedDown + pinsKnockedDown == 10) bonus = 1;
            }
            if (count==bonus) totalScore(total);
            count++;

      // adjust the multiplier on 10th frame bonus rolls     
            if (m1>1) m1--;
            m2 = 1;
         }
      }
   }
0
igouy (1009)
12/10/2003 11:17:55 PM
Isaac Gouy:
> Ron Jeffries:
>> Isaac Gouy:
>>> "The Story, Again ...
>>> I'm solving the same problem as in the preceding BowlingGame article."
>>>
>>> http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm

>> Yes, but I'm taking a different, and simpler /approach/. A different
>> initial start, different design idea. Same problem, same requirements,
>> different implementation. N'est pas?

> Non. Similar requirements - not the same.
> 
> Either they meet different requirements or one of the implementations
> is incomplete.

.... or the requirements are vague enough that we can say they are met by
both solutions. But what Ron is pointing out (half a dozen posts back) is
the introduction of a new (and probably equally vague) game rules. At
least, that's all I could extract from the rhetoric.

> acsBowling is missing conversion from 'Roll(int roll)' to 'OpenFrame,
> Spare, Strike, BonusRoll'; or acsBowlingProcedural is missing
> conversion from 'OpenFrame, Spare, Strike, BonusRoll' to 'Roll(int
> roll)'.
> 
> (We wouldn't even be able to compile acsBowling with the tests from
> acsBowlingProcedural, let alone have them succeed.)

Yes. I think the acsBowling example is cheeky in requiring the caller to
create derived frame objects for it. It reminds me of the contrived
examples that we often see comparing polymorphism with procedural switch
statements.

0
12/10/2003 11:39:17 PM
Isaac Gouy wrote:

> Then it seems possible, that we may have people's 'formal'
> understanding of how to score bowling, and people's 'operational'
> understanding of how to score bowling. The usual difference between
> what people tell you they do, if you ask them; and what you find out
> they do, if you watch them perform the task.
> 
> best wishes, Isaac

Yes. An acute observation.

0
12/10/2003 11:48:31 PM
Isaac Gouy:
> Paul Sinnett:

>> Still, it is not hard, as you
>> say, to remove this duplication without losing the context.

> Perhaps you'd like to post the final version?

I don't have C#, but I could knock something up in C++ that would be
essentially the same if you like?

>>> Still doesn't answer the interesting question, the question that
>>> distinguishes this from other algorithms - what's 'q' for?

>> 'q' acts both as a state to tell us when a frame can be scored and the
>> total of that score when we can. Maybe there's an object here?

> 'q' is a computational artifact something that has no obvious presence
> in the applicaton domain. Although it is possible to see glimpse it on
> the bowling scorecard, as you add new roll scores and incrementally
> calculate totals.

If we extracted q as an object in it's own right it could represent the
frame score for the frame we're currently calculating. It would know if it
was complete, it could return its total, and it could advance to the next
frame. Your main loop would then look like:

  void addScore( int pinsKnockedDown ) {
    currentFrameScore.AddRoll( pinsKnockedDown );
    while (currentFrameScore.IsComplete() && frames<=10){
      total+=currentFrameScore.Total();
      totalScore(total);
      currentFrameScore.NextFrame();
      frames++;
    }
  }


0
12/11/2003 12:28:37 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd7ba9e@news.totallyobjects.com>...

> I don't have C#, but I could knock something up in C++ that would be
> essentially the same if you like?

The code you were incrementally refactoring was written in Nice.
http://nice.sourceforge.net/index.html (So we can use ArrayList<int> etc)

I was just looking for the result, rather than repeating them piecemeal.

> >>> Still doesn't answer the interesting question, the question that
> >>> distinguishes this from other algorithms - what's 'q' for?
>  
> >> 'q' acts both as a state to tell us when a frame can be scored and the
> >> total of that score when we can. Maybe there's an object here?
>  
> > 'q' is a computational artifact something that has no obvious presence
> > in the applicaton domain. Although it is possible to see glimpse it on
> > the bowling scorecard, as you add new roll scores and incrementally
> > calculate totals.
> 
> If we extracted q as an object in it's own right it could represent the
> frame score for the frame we're currently calculating. It would know if it
> was complete, it could return its total, and it could advance to the next
> frame. Your main loop would then look like:

or perhaps

void addScore( int pinsKnockedDown ) {
   frameScore.addRoll( pinsKnockedDown );
   while (frameScore.isComplete() && frames<=10){
      total+=frameScore.total();
      totalScore(total);
      frameScore.next();
      frames++;
   }
}

best wishes, Isaac
0
igouy (1009)
12/11/2003 2:55:07 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd7af0f@news.totallyobjects.com>...
> Isaac Gouy:
> > Ron Jeffries:
> >> Isaac Gouy:
> >>> "The Story, Again ...
> >>> I'm solving the same problem as in the preceding BowlingGame article."
> >>>
> >>> http://www.xprogramming.com/xpmag/acsBowlingProcedural.htm
>  
> >> Yes, but I'm taking a different, and simpler /approach/. A different
> >> initial start, different design idea. Same problem, same requirements,
> >> different implementation. N'est pas?
>  
> > Non. Similar requirements - not the same.
> > 
> > Either they meet different requirements or one of the implementations
> > is incomplete.
> 
> ... or the requirements are vague enough that we can say they are met by
> both solutions.

These are demonstrations of TDD.
The unit tests represent the requirements (did I misunderstand?)

If the code doesn't pass the same unit tests it doesn't meet the same
requirements.


> > acsBowling is missing conversion from 'Roll(int roll)' to 'OpenFrame,
> > Spare, Strike, BonusRoll'; or acsBowlingProcedural is missing
> > conversion from 'OpenFrame, Spare, Strike, BonusRoll' to 'Roll(int
> > roll)'.
> > 
> > (We wouldn't even be able to compile acsBowling with the tests from
> > acsBowlingProcedural, let alone have them succeed.)
> 
> Yes. I think the acsBowling example is cheeky in requiring the caller to
> create derived frame objects for it. It reminds me of the contrived
> examples that we often see comparing polymorphism with procedural switch
> statements.

IMO 2 basic issues reflect badly on the articles:

- the claim "Less up-front design, and a better result" which turns
out to be based on a 'comparison' with some purely 'notional' BUFD!

http://groups.google.com/groups?selm=qm8qsvcjlssi390dandqt2ni3rfratge08%404ax.com

- this 'side by side' comparison of code that won't pass the same unit
tests.
(Which seems inconsistent with TDD? and is easy to fix)


The articles in themselves are interesting - the problem is with the
editorial.

best wishes, Isaac
0
igouy (1009)
12/11/2003 3:46:18 PM
class ScoreKeeper {
      int total = 0;
      int m1 = 1;
      int m2 = 1;
      int knockedDown = 0;
      boolean hasNewRack = true;
      int racks = 0;

      void addScore(int pinsKnockedDown){
         total += pinsKnockedDown * m1;
         m1 = m2; m2 = 1; 
     
         if (pinsKnockedDown == 10){
            m1++; m2 = 2;
            racks++;
         }
         else {
            if (hasNewRack){
               hasNewRack = false;
               knockedDown = pinsKnockedDown;
            }
            else {
               if (knockedDown + pinsKnockedDown == 10) m1++;
               hasNewRack = true;
               racks++;
            }
         }

         if (racks>=10) { // adjust bonus roll multipliers
            if (m1>1) m1--;
            m2 = 1;
         }
      }

      void score(){ totalScore(total); }
   }
0
igouy (1009)
12/11/2003 4:26:42 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...

>   Then I take a very simple approach that produces a rather simple single-
>   class solution, with none of the complexity we anticipated.

Maybe the simplest approach to the addScore/totalScore variant of the
bowling game is a variant of the 'procedural' approach.

This implementation 'x' seems to be comparable to implementation 'q'
(again, when a strike is followed by an open frame, 2 frame scores are
ready).

Instead of creating the correct state for the total score calculation,
always do the calculation and "fail fast".

   class ScoreKeeper {
      ArrayList<int> pins = new ArrayList(21);
      int total = 0;
      int frames = 0;
      int i = 0;
      boolean noteEachTotal;


      void addScore(int pinsKnockedDown){
         pins.add(pinsKnockedDown);

         try { 
            total += this.frameScore;
            if ((noteEachTotal&&frames<10)||frames==10)
totalScore(total);

            total += this.frameScore;
            if ((noteEachTotal&&frames<10)||frames==10)
totalScore(total);
         }
         catch (IndexOutOfBoundsException ex){}
      }


      int frameScore(){
         var score = 0;

         if (pins[i] == 10) {
            score = pins[i] + pins[i+1] + pins[i+2];
            i++;
         }          
         else if (pins[i] + pins[i+1] == 10) { 
            score = pins[i] + pins[i+1] + pins[i+2];
            i += 2;
         }
         else {
            score = pins[i] + pins[i+1];
            i += 2;
         }
         frames++;

         return score;    
      }
   }
0
igouy (1009)
12/13/2003 12:04:02 AM
ggroups@bigfoot.com (S Perryman) wrote in message 

> > Both these implementations provide a method to return the ith frame
> > total. I've been looking at something slightly different: providing
> > the frame total whenever it becomes available, using a simple call-
> > void totalScore(int score).
>  
> > Where would you do that call?
> 
> TPScorer.total() ?? :-)

The TPScorer implementation you posted (like the others) expects that
'some other software' calls TPScorer.total(). Seemed unreasonable to
me that 'some other software' should have to figure-out when to make
those calls.

Instead, I've been trying to write implementations that will provide a
frame score as-soon-as it's available using:
someOtherSoftware.total(oTotal);

So my question was - when does TPScorer know that a frame total can be
calculated and passed to 'some other software'?

(Maybe that isn't an appropriate modification of this code?)

-snip-
> What you have to understand is that I am not driven by trivia such as
> LOC counts.

LOC is more than trivia ;-)
More LOC = more code to understand, test, produce...
LOC is less than the most important metric ;-)

best wishes, Isaac
0
igouy (1009)
12/13/2003 1:44:29 AM
Isaac Gouy <igouy@yahoo.com> wrote in message
news:ce7ef1c8.0312121744.3498dadb@posting.google.com...
> ggroups@bigfoot.com (S Perryman) wrote in message

IG> Both these implementations provide a method to return the ith frame
IG> total. I've been looking at something slightly different: providing
IG> the frame total whenever it becomes available, using a simple call-
IG> void totalScore(int score).

> > > Where would you do that call?

> > TPScorer.total() ?? :-)

> The TPScorer implementation you posted (like the others) expects that
> 'some other software' calls TPScorer.total(). Seemed unreasonable to
> me that 'some other software' should have to figure-out when to make
> those calls.

// Provides the current running total of a tenpin bowling game based on any
// throws (and the pins that have been hit) to date.
short total() ;

Is the above still "unreasonable" ??
Does the documentation not convey the information that the total score
can be requested at any point during a game ??


> Instead, I've been trying to write implementations that will provide a
> frame score as-soon-as it's available using:
> someOtherSoftware.total(oTotal);

> So my question was - when does TPScorer know that a frame total can be
> calculated and passed to 'some other software'?

> (Maybe that isn't an appropriate modification of this code?)

The code does not need to be modified (other than to remove the typos that
prevent
its compilation :-) ) . It will give you the running total at any time, and
the score
(current or completed) for any frame completed or in progress.


> > What you have to understand is that I am not driven by trivia such as
> > LOC counts.

> LOC is more than trivia ;-)
> More LOC = more code to understand, test, produce...
> LOC is less than the most important metric ;-)

You need to see the "Obsfucated C" competitions. :-)


Regards,
Steven Perryman


0
S
12/13/2003 1:59:59 PM
Isaac Gouy wrote:
> I was just looking for the result, rather than repeating them piecemeal.

Okay, here's a translation to C++. I'm passing in a pointer to a result
vector so that my test code can check the results rather than just have
them print to the output:

  using namespace std;
  
  class ScoreKeeper {
    deque<int> q;
    vector<int>* results;
    int total;
    int frames;
  public:
    ScoreKeeper( vector<int>& resultArray ) {
      results = &resultArray;
      total = 0;
      frames = 1;
    }
    
    void addScore(int pinsKnockedDown) {
      
      q.push_back(pinsKnockedDown);
      
      if (q.size()==3 && frames<=10){
        if (isStrike()){
          total += strikeScore() + strikeBonusScore();
          fillStrikeScore();   
        } 
        else if (isSpare()){
          total += spareScore() + spareBonusScore();
          fillFrameScore();    
        }      
      }
      
      if (q.size()==2 && frames<=10){
        if (isOpen()){
          total += openScore();
          fillFrameScore();  
        }      
      }
    }
    
    bool isStrike() {
      return q[0] == 10; 
    }
    int strikeScore() { 
      return q[0]; 
    }
    int strikeBonusScore() { 
      return q[1] + q[2]; 
    }
    
    bool isSpare() { 
      return q[0] + q[1] == 10; 
    }
    int spareScore() { 
      return q[0] + q[1]; 
    }
    int spareBonusScore() { 
      return q[2]; 
    }
    
    bool isOpen() { 
      return q[0] + q[1] < 10; 
    }
    int openScore() { 
      return q[0] + q[1]; 
    }
    
    void fillStrikeScore(){
      fillScore(); 
    }
    
    void fillFrameScore(){
      q.pop_front(); 
      fillScore(); 
    }
    
    void fillScore(){
      q.pop_front(); 
      frames++;
      results->push_back( total );
    }
  };

Now here's a version with a Frame::Score object extracted and most of the
duplicate logic removed:

  using namespace std;

  namespace Frame {
    
    class Score;
    typedef bool(Score::*predicate)(void);
    
    struct Type {
      static int maxFrameSize;
      int rollsInFrame;
      int rollsInScore;
      predicate match;
      Type( int rolls, int bonus, predicate isType ) {
        rollsInFrame = rolls;
        rollsInScore = rolls + bonus;
        match = isType;
        maxFrameSize = max(rolls, maxFrameSize);
      }
    };
    
    class Score {
      static const Type strike;
      static const Type spare;
      static const Type open;

      deque<int> rolls;
      
      bool isType(const Type& type){
        assert(int(rolls.size())>=type.rollsInFrame);
        return (this->*type.match)();
      }
      const Type& type(){
        if (isType(strike))   return strike;
        if (isType(spare))    return spare;
        assert(isType(open)); return open;
      }
      bool isStrike(void){
        return rolls[0] == 10; 
      }
      bool isSpare(void){
        return !isStrike() && rolls[1] == 10 - rolls[0]; 
      }
      bool isOpen(void){
        return rolls[0] + rolls[1] < 10; 
      }
    public:
      void addRoll(int pinsKnockedDown){
        assert(!isComplete());
        rolls.push_back(pinsKnockedDown);
      }
      bool isComplete() {
        int rollCount = int(rolls.size());
        if (rollCount<Type::maxFrameSize) return false;
        return rollCount>=type().rollsInScore;
      }
      int score() {
        assert(isComplete());
        return accumulate(rolls.begin(),rolls.end(),0);
      }
      void next() {
        int frameSize = type().rollsInFrame;
        for (int i=0; i<frameSize; i++){
          rolls.pop_front();
        }
      }
    };
  
    int Type::maxFrameSize = 0;
    
    //                type    rolls, bonus, isType
    //                -----------------------------------------
    const Type Score::strike(     1,     2, &Score::isStrike );
    const Type Score::spare (     2,     1, &Score::isSpare  );
    const Type Score::open  (     2,     0, &Score::isOpen   );
  }
  
  class ScoreKeeper {
    Frame::Score frameScore;
    vector<int>* results;
    int total;
    int frames;
  public:
    ScoreKeeper( vector<int>& resultArray ) {
      results = &resultArray;
      total = 0;
      frames = 1;
    }
    
    void addScore(int pinsKnockedDown) {
      frameScore.addRoll(pinsKnockedDown);
      while (frameScore.isComplete() && frames<=10){
        total += frameScore.score();
        frameScore.next(); 
        frames++;
        results->push_back( total );
      }
    }
  };

0
12/13/2003 3:53:34 PM
"S Perryman" <q@q.com> wrote in message news:<brf5tt$2lpo4$1@ID-75294.news.uni-berlin.de>...
 
> The code does not need to be modified ... 

That would depend on what we're trying to get it to do ;-)

best wishes, Isaac
0
igouy (1009)
12/14/2003 8:36:22 PM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
 
>   When I demonstrate Test-Driven Development using the Bowling Game
>   example, I begin by describing the problem and inviting the attendees to
>   do a little up front design about what objcts we may need. 

Let's do a naive full-on everything-is-an-object design!

Much of the effort in implementation 'o' goes into constructing Strike
and Spare and Open frame objects from the sequence of Roll objects.

By the time we have refactored 'q' & 'x' to 'express the domain' are
they as verbose as implemention 'o'?

best wishes, Isaac


   class ScoreKeeper {
      ArrayList<Roll> rolls = new ArrayList(21);
      ArrayList<Frame> frames = new ArrayList(10);
      ?Frame pending = null;
      boolean isTenthFrame = false;
      
      void addScore(int pinsKnockedDown){
         let r = new Roll(pins: pinsKnockedDown);
         rolls.add(r);      
         this.fillInFrame(r);
         this.fillInScores;
      }         
  
      void fillInFrame(Roll aRoll){      
         var pendingFrame = pending;
 
         if (pendingFrame == null){
            if (aRoll.isStrike && !isTenthFrame)
               this.addFrame( newStrike(aRoll) );
            else 
               pending = newFrame(aRoll);
         }
         else {
            pendingFrame.addRoll(aRoll);
            pendingFrame = pending = pendingFrame.asSpecificFrame;

            if (pendingFrame.isComplete(isTenthFrame)) 
               this.addFrame(pendingFrame);
         }    
      }

      void addFrame(Frame aFrame){
         frames.add(aFrame);             
         pending = null;
         isTenthFrame = frames.size >= 9;
      }

      void fillInScores(){
         var total = 0;

         for (eachFrame : frames){
            if (eachFrame.hasFrameScore){
               total = eachFrame.frameScore;
            }
            else {                            
               var i = rolls.indexOf(eachFrame.lastRoll);  
                     
               if (i + eachFrame.bonusRolls < rolls.size){ 
                                         
                  var score = eachFrame.score;

                  for (var bonus = 1; bonus <= eachFrame.bonusRolls;
bonus++)
                     score += rolls[i + bonus].score; 
                   
                  total += score;                                 
                  eachFrame.frameScore = total;  
  
                  totalScore(total);
               }                                         
            }
         }                
      }
      
   }


Frame newFrame(Roll aRoll) = (new Frame()).addRoll(aRoll);  
     
Strike newStrike(Roll aRoll) = (new Strike()).addRoll(aRoll);   


class Frame {
   ArrayList<Roll> rolls = new ArrayList(3);
   int frameScore = -1;

   boolean hasFrameScore() = frameScore > -1;

   boolean isComplete(boolean isTenth) = false;

   boolean isStrike() = isStrike(rolls[0]);

   boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);

   boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);

   Frame asSpecificFrame(){
      if (this.isStrike) return this.asStrike;
      else if (this.isSpare) return this.asSpare;
      else return this.asOpen;
   }

   Strike asStrike() = new Strike(rolls: rolls);

   Spare asSpare() = new Spare(rolls: rolls);

   Open asOpen() = new Open(rolls: rolls);
   
   alike addRoll(Roll aRoll){
      rolls.add(aRoll);
      return this;
   }
   
   int scoreRolls() = 0; 
   int bonusRolls() = 0;
   
   Roll lastRoll() = rolls[this.scoreRolls - 1];   
   
   int score(){
      var score = 0;
      for (var i = 0; i < this.scoreRolls; i++) 
         score += rolls[i].score;
      return score;   
   }  
}


class Strike extends Frame {
   isStrike() = true;
   asStrike() = this;

   isComplete(boolean isTenth) = 
      (!isTenth && rolls.size==1) || rolls.size==3;

   scoreRolls() = 1;      
   bonusRolls() = 2;
}


class Spare extends Frame {
   isSpare() = true;
   asSpare() = this;  

   isComplete(boolean isTenth) = 
      (!isTenth && rolls.size==2) || rolls.size==3;
      
   scoreRolls() = 2;      
   bonusRolls() = 1;        
}


class Open extends Frame {
   isOpen() = true;
   asOpen() = this;

   isComplete(boolean isTenth) = rolls.size==2;
   
   scoreRolls() = 2;     
   bonusRolls() = 0;    
}


class Roll {
   int pins;

   int score() = pins;
   boolean isStrike() = pins == 10;
   boolean isSpare(Roll aRoll) = pins != 10 && pins + aRoll.score ==
10;
   boolean isOpen(Roll aRoll) = pins + aRoll.score < 10;
}
0
igouy (1009)
12/14/2003 9:10:23 PM
Isaac Gouy wrote:

> Let's do a naive full-on everything-is-an-object design!
>
> Much of the effort in implementation 'o' goes into constructing Strike
> and Spare and Open frame objects from the sequence of Roll objects.
>
> By the time we have refactored 'q' & 'x' to 'express the domain' are
> they as verbose as implemention 'o'?
>
> best wishes, Isaac
>
>
>    class ScoreKeeper {

Wouldn't changing the language affect the mapping between requirements and
code things?

(It's just a question - I can't tell if you did change the language or not.)

--
  Phlip




0
phlip_cpp (3852)
12/14/2003 9:34:28 PM
"Phlip" <phlip_cpp@yahoo.com> wrote in message news:<EH4Db.36602$EC1.23078@newssvr32.news.prodigy.com>...
> Isaac Gouy wrote:
> 
> > Let's do a naive full-on everything-is-an-object design!
> >
> > Much of the effort in implementation 'o' goes into constructing Strike
> > and Spare and Open frame objects from the sequence of Roll objects.
> >
> > By the time we have refactored 'q' & 'x' to 'express the domain' are
> > they as verbose as implemention 'o'?
> >

> Wouldn't changing the language affect the mapping between requirements and
> code things?

(Assuming we are talking about changing the programming language.)

Good question - which I'll evade answering, as we didn't change
language.

Nice http://nice.sourceforge.net/index.html is an OO language, which
allows package level definition of functions and variables sufficient
for straightforward procedural style programming.

Nice procedural style
http://groups.google.com/groups?q=g:thl4263204549d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0311241107.6c87f053%40posting.google.com

Nice simple OO style
http://groups.google.com/groups?q=g:thl1550167433d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0312121604.168c203f%40posting.google.com

(A couple of other languages have been used in this thread: some C#
for Ron Jeffries, and some C++)
0
igouy (1009)
12/15/2003 4:28:25 AM
Isaac Gouy wrote:

> Good question - which I'll evade answering, as we didn't change
> language.

Isaac, who the f*** evaded what?

I'm asking because I feel confrontational, not evasive, but I know better
than to Google for this one...

--
  Phlip


0
phlip_cpp (3852)
12/15/2003 7:20:32 AM
This version passes all of the tests from Ron's TDD article but it fails to
distinguish between a strike, and a miss followed by a spare, eg the game:

0/ 11 00 00 00 00 00 00 00 00

should score 13 (11 for the spare in the first frame + 2 for the open second
frame). The code below scores 14.

Isaac Gouy wrote:
> class ScoreKeeper {
>       int total = 0;
>       int m1 = 1;
>       int m2 = 1;
>       int knockedDown = 0;
>       boolean hasNewRack = true;
>       int racks = 0;
> 
>       void addScore(int pinsKnockedDown){
>          total += pinsKnockedDown * m1;
>          m1 = m2; m2 = 1;
>      
>          if (pinsKnockedDown == 10){
>             m1++; m2 = 2;
>             racks++;
>          }
>          else {
>             if (hasNewRack){
>                hasNewRack = false;
>                knockedDown = pinsKnockedDown;
>             }
>             else {
>                if (knockedDown + pinsKnockedDown == 10) m1++;
>                hasNewRack = true;
>                racks++;
>             }
>          }
> 
>          if (racks>=10) { // adjust bonus roll multipliers
>             if (m1>1) m1--;
>             m2 = 1;
>          }
>       }
> 
>       void score(){ totalScore(total); }
>    }

To make it work we need to change:

  if (pinsKnockedDown == 10)

to:

  if (hasNewRack && pinsKnockedDown == 10 )
0
12/15/2003 2:28:15 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fdb366d@news.totallyobjects.com>...
> Isaac Gouy wrote:
> > I was just looking for the result, rather than repeating them piecemeal.
> 
> Okay, here's a translation to C++. 

I understood more before the translation, mea culpa.

best wishes, Isaac
0
igouy (1009)
12/15/2003 4:01:11 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fd52d69@news.totallyobjects.com>...
 
> Still, it is not hard, as you say, to remove this duplication without losing
> the context. 

Like this:

   class ScoreKeeper {
      ArrayList<int> q = new ArrayList(3);
      int total = 0;
      int frames = 1;

      void addScore(int pinsKnockedDown){
         q.add(pinsKnockedDown);

         while (this.isScorable && frames<=10){
            if (this.isStrike){
               total += this.strikeScore + this.strikeBonusScore;
               this.fillStrikeScore();
               continue;  
            } 
            if (this.isSpare){
               total += this.spareScore + this.spareBonusScore;
               this.fillSpareScore(); 
               continue;   
            }   
            if (this.isOpen){
               total += this.openScore + this.openBonusScore;
               this.fillOpenScore();   
               continue;    
            }
         }
      }

      boolean isStrike() = q[0] == 10;
      int strikeScore() = q[0];
      int strikeBonusScore() = q[1] + q[2];

      boolean isSpare() = !this.isStrike && q[0] + q[1] == 10;
      int spareScore() = q[0] + q[1];
      int spareBonusScore() = q[2];

      boolean isOpen() = q[0] + q[1] < 10;
      int openScore() = q[0] + q[1];
      int openBonusScore() = 0;

      boolean isScorable() = 
         q.size==3 || (q.size==2 && this.isOpen);


       void fillStrikeScore(){
          this.fillScore(); 
       }

       void fillSpareScore(){
          q.removeAt(0); 
          this.fillScore(); 
       }

       void fillOpenScore(){
          q.removeAt(0); 
          this.fillScore(); 
       }

       void fillScore(){
          q.removeAt(0); 
          frames++;
          totalScore(total); 
       }
   }
0
igouy (1009)
12/15/2003 4:11:53 PM
Isaac Gouy wrote:
> Paul Sinnett <paul.sinnett@btinternet.com> wrote in message
> news:<3fd52d69@news.totallyobjects.com>...
>  
>> Still, it is not hard, as you say, to remove this duplication without
>> losing the context.
> 
> Like this:
<snip>

Okay. Now we can clearly see the duplication that was hidden in the earlier
version:

> while (this.isScorable && frames<=10){
>    if (this.isStrike){
>       total += this.strikeScore + this.strikeBonusScore;
>       this.fillStrikeScore();
>       continue;
>    }
>    if (this.isSpare){
>       total += this.spareScore + this.spareBonusScore;
>       this.fillSpareScore();
>       continue;
>    }
>    if (this.isOpen){
>       total += this.openScore + this.openBonusScore;
>       this.fillOpenScore();
>       continue;
>    }
> }

Can we remove it?

0
12/15/2003 5:52:19 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fddf547@news.totallyobjects.com>...

> Now we can clearly see the duplication that was hidden in the earlier
> version:
> Can we remove it?

(Part 1 of 2)
methods/functions are first class in Nice, so we can store them in
variables; and Nice has tuples so we can group together arbitrary
methods - (did we just re-implement objects?)

class SK {
   ArrayList<int> q = new ArrayList(3);
   int total = 0;
   int frames = 1;

   (SK->boolean, SK->int, SK->int, SK->void)[] frameScorers = [
         (isStrike, strikeScore, strikeBonusScore, nextStrikeScore),
         (isSpare, spareScore, spareBonusScore, nextSpareScore),
         (isOpen, openScore, openBonusScore, nextOpenScore)
      ];


   void addScore(int pinsKnockedDown){
      q.add(pinsKnockedDown);

      while (this.isScorable && frames<=10){
         for ((SK->boolean, SK->int, SK->int, SK->void) each:
frameScorers){

            (SK->boolean canScore, SK->int score, 
               SK->int bonus, SK->void next) = each;

            if (canScore(this)){
               total += score(this) + bonus(this);
               next(this);
               break;  
            } 
         }
         frames++;
         totalScore(total); 
      }
   }

   boolean isStrike() = q[0] == 10;
   int strikeScore() = q[0];
   int strikeBonusScore() = q[1] + q[2];

   boolean isSpare() = !this.isStrike && q[0] + q[1] == 10;
   int spareScore() = q[0] + q[1];
   int spareBonusScore() = q[2];

   boolean isOpen() = q[0] + q[1] < 10;
   int openScore() = q[0] + q[1];
   int openBonusScore() = 0;


   boolean isScorable() = 
      q.size==3 || (q.size==2 && this.isOpen);

   void nextStrikeScore(){
      q.removeAt(0); 
   }

   void nextSpareScore(){
      q.removeAt(0); 
      q.removeAt(0); 
   }      

   void nextOpenScore(){
      q.removeAt(0); 
      q.removeAt(0); 
   }   

}
0
igouy (1009)
12/15/2003 9:59:48 PM
> Now we can clearly see the duplication that was hidden in the earlier
> version:
> Can we remove it?

(Part 2 of 2) 
Oh! Let's just make some objects!

Do we think this is in-some-way 'better' than having 3 conditions in a loop?


class ScoreKeeper {
   ArrayList<int> q = new ArrayList(3);
   int total = 0;
   int frames = 1;
   Scorer[] frameScorers = [new Strike(), new Spare(), new Open()];

   void addScore(int pinsKnockedDown){
      q.add(pinsKnockedDown);
         
      while (frameScorers[0].isScorable(q) && frames<=10){
         for (Scorer each : frameScorers){
            if (each.canScore(q)){
               total += each.score(q) + each.bonus(q);
               each.next(q);                           
               break;  
            } 
         }
         frames++;  
         totalScore(total); 
      }
   }   
}


class Scorer {
   boolean canScore(ArrayList<int> q) = false;
   int score(ArrayList<int> q) = q[0] + q[1];
   int bonus(ArrayList<int> q) = 0;  
    
   void next(ArrayList<int> q){
      q.removeAt(0); 
      q.removeAt(0); 
   }   
   
   boolean isScorable(ArrayList<int> q) = 
      q.size==3 || (q.size==2 && q[0] + q[1] < 10);    
}

class Strike extends Scorer {
   canScore(ArrayList q) = q[0] == 10;
   score(ArrayList q) = q[0];
   bonus(ArrayList q) = q[1] + q[2];  
   
   next(ArrayList q){
      q.removeAt(0); 
   }   
}

class Spare extends Scorer {
   canScore(ArrayList q) = q[0] != 10 && q[0] + q[1] == 10;      
   bonus(ArrayList q) = q[2];   
}

class Open extends Scorer {
   canScore(ArrayList q) = q[0] + q[1] < 10;   
}
0
igouy (1009)
12/15/2003 10:04:01 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fddc573@news.totallyobjects.com>...

> This version passes all of the tests from Ron's TDD article but it fails to
> distinguish between a strike, and a miss followed by a spare

Thank you, for taking the trouble to look closely.

Maybe it's nothing more than luck that the same problem doesn't exist
in 'x', the variants of 'q', or the full-on OO implementations?

(Of course the other problem with 'm' is that allow the calculation is
incremental, it will not give the correct intermediate totals.)

best wishes, Isaac
0
igouy (1009)
12/16/2003 1:28:29 AM
Isaac Gouy wrote:
> Do we think this is in-some-way 'better' than having 3 conditions in a
> loop?

It has less duplicated logic. If duplicate logic is undesirable in the code,
it is better in that regard.

Look at the structure of this loop now:

>  while (frameScorers[0].isScorable(q) && frames<=10){
>     for (Scorer each : frameScorers){
>        if (each.canScore(q)){
>            ...
>            break;
>        }
>     }
>     frames++;
>     totalScore(total);
> }

This loop is still doing two things. It is finding which Scorer object can
score the q and it is doing some operations using that object. We are
testing q against a set of rules to see which one applies. Then we are
applying that rule to q to get a score for the frame. But these two
operations are currently spliced together within the loop.

Extracting the search is easy enough:

   while (frameScorers[0].isScorable(q) && frames<=10){
      Scorer s = findScorer();
      total += s.score(q) + s.bonus(q);
      s.next(q);                           
      frames++;
      totalScore(total);
   }

where:

   Scorer findScorer() {
      for (Scorer each: frameScorers){
         if (each.canScore(q)) return each;
      }
      assert(false);
   }

0
12/16/2003 2:16:33 AM
igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
(or about)  14 Dec 2003 20:28:25 -0800, :

>(A couple of other languages have been used in this thread: some C#
>for Ron Jeffries, and some C++)

Here's my favorite Java implementation:


public class BowlingGame {
  private int[] rolls = new int[21];
  private int currentRoll = 0;

  public void roll(int roll) {
    rolls[currentRoll++] = roll;
  }

  public int score() {
    int score = 0;
    int first = 0;
    for (int frame = 0; frame < 10; frame++) {
      if (strike(first)) {
        score += 10 + nextTwoBallsForStrike(first);
        first += 1;
      }
      else if (spare(first)) {
        score += 10 + nextBallForSpare(first);
        first += 2;
      } else {
        score += twoBallsInFrame(first);
        first += 2;
      }
    }
    return score;
  }

  private int twoBallsInFrame(int first) {
    return rolls[first] + rolls[first + 1];
  }
  private int nextBallForSpare(int first) {
    return rolls[first + 2];
  }
  private int nextTwoBallsForStrike(int first) {
    return rolls[first+1] + rolls[first+2];
  }
  private boolean spare(int first) {
    return rolls[first] + rolls[first + 1] == 10;
  }
  private boolean strike(int first) {
    return rolls[first] == 10;
  }
}



Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
12/16/2003 6:48:47 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fde6b72@news.totallyobjects.com>...

There's a library call and we can use an anonymous function to define
the test criteria.

   void addScore(int pinsKnockedDown){
      q.add(pinsKnockedDown);
         
      while (frameScorers[0].isScorable(q) && frames<=10){
         let s = frameScorers.search(Scorer each => each.canScore(q));
         if (s != null){
            total += s.score(q) + s.bonus(q);
            s.next(q);                          
            frames++;  
            totalScore(total); 
         }
      }
   }   

> It has less duplicated logic. If duplicate logic is undesirable in the code,
> it is better in that regard.

A single criteria would make it easy to judge the quality of code.
Even in this short discussion there seem to be other criteria - "I
prefer to see code that looks like the problem as understood by the
customer".

(Maybe if we squint the code might look somewhat like 'the problem as
understood' but if we open our eyes and look?)

best wishes, Isaac
0
igouy (1009)
12/16/2003 7:43:14 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fde6b72@news.totallyobjects.com>...

Was that C++ implementation significantly different to this Nice implementation?
0
igouy (1009)
12/16/2003 7:47:07 AM
Isaac Gouy wrote:
> Was that C++ implementation significantly different to this Nice
> implementation?

I'm not sure which examples you are comparing: we've looked at a lot.
0
12/16/2003 2:04:58 PM
Isaac Gouy:
> Paul Sinnett:
> 
> There's a library call and we can use an anonymous function to define
> the test criteria.
> 
>    void addScore(int pinsKnockedDown){
>       q.add(pinsKnockedDown);
>          
>       while (frameScorers[0].isScorable(q) && frames<=10){
>          let s = frameScorers.search(Scorer each => each.canScore(q));
>          if (s != null){
>             total += s.score(q) + s.bonus(q);
>             s.next(q);
>             frames++;
>             totalScore(total);
>          }
>       }
>    }

That's cool. But why?:

   if (s != null){
      ...
   }

The condition frameScorers[0].isScorable(q) prevents s from being null. That
suggests that they are doing some of the same job to me. If
Scorer::canScore took on the additional checks in SK::isScorable we could
simplify the loop:

   while (frames<=10){
      let s = frameScorers.search(Scorer each => each.canScore(q));
      if (s != null){
         total += s.score(q) + s.bonus(q);
         s.next(q);
         frames++;
         totalScore(total);
      }
      else{
         break;
      }
   }

or:

   while (frames<=10){
      let s = frameScorers.search(Scorer each => each.canScore(q));
      if (s == null) break;
      total += s.score(q) + s.bonus(q);
      s.next(q);
      frames++;
      totalScore(total);
   }

Looking again at SK::isScorable what do the numbers 2 and 3 refer to?

>   boolean isScorable(ArrayList<int> q) = 
>      q.size==3 || (q.size==2 && q[0] + q[1] < 10);

I recognise q[0]+q[1]<10, it's the old isOpen check. The q.size==3 side of
the conditional logic could include the opposite, then we'd have:

   boolean isScorable(ArrayList<int> q) =
      (q.size==3 && !this.isOpen) || (q.size==2 && this.isOpen);

and !isOpen could be written isMark where:

   boolean isMark() = this.isStrike || this.isSpare;

so we could expand it out to:

   boolean isScorable(ArrayList<int> q) =
      (q.size==3 && this.isStrike) ||
      (q.size==3 && this.isSpare)  ||
      (q.size==2 && this.isOpen);

and since each Scorer subclass has it's own canScore function we could split
these checks out to those:

class Strike extends Scorer {
   canScore(ArrayList q) = 
      q.size==3 && q[0] == 10;
   ...
}

class Spare extends Scorer {
   canScore(ArrayList q) = 
      q.size==3 && q[0] != 10 && q[0] + q[1] == 10;      
   ...
}

class Open extends Scorer {
   canScore(ArrayList q) = 
      q.size==2 && q[0] + q[1] < 10;   
   ...
}

0
12/16/2003 2:42:49 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<liattvcfgfop7mathnp6k49k61na6c0l8i@4ax.com>...
> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
> (or about)  14 Dec 2003 20:28:25 -0800, :
> 
> >(A couple of other languages have been used in this thread: some C#
> >for Ron Jeffries, and some C++)
> 
> Here's my favorite Java implementation:

Thanks, we've actually been working on a variation of the Bowling
Game; where instead of being responsible for providing the score when
asked, the responsibility is provide the score as-soon-as it can be
calculated - just passing the value along to 'some other software' in
a method or function call.

That requirement forced changes to some of the approaches I'd thought
of - how would it change your favourite implementation?

best wishes, Isaac
0
igouy (1009)
12/16/2003 4:51:56 PM
Isaac Gouy:
> Paul Sinnett:
>> It has less duplicated logic. If duplicate logic is undesirable in the
>> code, it is better in that regard.

> A single criteria would make it easy to judge the quality of code.

Agreed. The single criteria used in TDD (as far as I am aware) is
simplicity. Unfortunately, a consensus definition of what is simplest is
hard to find. In XPE:

1. The system (code and tests together) must communicate every-
   thing you want to communicate.
2. The system must contain no duplicate code. (1 and 2 together
   constitute the Once and Only Once rule).
3. The system should have the fewest possible classes.
4. The system should have the fewest possible methods.

> Even in this short discussion there seem to be other criteria - "I
> prefer to see code that looks like the problem as understood by the
> customer".

Hmm. I think this is Ron expressing an opinion. It's not something I've
heard before or seen published anywhere else as a criteria in TDD. I don't
know how to judge code using this criteria. Any thoughts on that Ron?

> (Maybe if we squint the code might look somewhat like 'the problem as
> understood' but if we open our eyes and look?)

The only way I can imagine we could test it would be to read the code back
to the customer to see if it made sense. I wouldn't expect the customer to
be able to read code but we could translate the algorithm back to English.
This code:

>  void addScore(int pinsKnockedDown){
>     q.add(pinsKnockedDown);
>     while (frames<=10){
>        let s = frameScorers.search(Scorer each => each.canScore(q));
>        if (s == null) break;
>        total += s.score(q) + s.bonus(q);
>        s.next(q);
>        frames++;
>        totalScore(total);
>     }
>  }

I would explain something like this:

When we get a message that some pins have been knocked down, first we add
that number of pins to our list 'q'. Then, if we've not yet counted 10
frames, we search for a scoring rule that can score the next frame from our
list 'q'. If a rule cannot be found we stop. Otherwise, using that rule on
our list 'q', we add the score for the pins in the frame and the score for
the pins in the bonus to our total. Then, also using the scoring rule, we
remove the pins for this frame from 'q'. Finally, we increase the number of
frames we have scored by one; and write out our total as the next frame's
score. While we have not yet scored all our frames we go back to search for
another scoring rule.

This might suggest some relatively cosmetic changes:

   void pinsDown(int pins){

      q.add(pins);

      do {
         let rule = scoringRules.search(Rule each => each.canScore(q));
         if (rule == null) break;
         total += rule.scoreFrame(q);
         writeScore(total);
         framesScored++;
         rule.removeFrame(q);
      }
      while (framesScored<10);
   }

0
Paul
12/16/2003 5:19:17 PM
"Paul Sinnett" <paul.sinnett@btinternet.com> wrote in message
news:3fdf3f0b@news.totallyobjects.com...
> Isaac Gouy:
> > Paul Sinnett:
> >> It has less duplicated logic. If duplicate logic is undesirable in the
> >> code, it is better in that regard.
>
> > A single criteria would make it easy to judge the quality of code.
>
> Agreed. The single criteria used in TDD (as far as I am aware) is
> simplicity. Unfortunately, a consensus definition of what is simplest is
> hard to find. In XPE:
>
> 1. The system (code and tests together) must communicate every-
>    thing you want to communicate.
> 2. The system must contain no duplicate code. (1 and 2 together
>    constitute the Once and Only Once rule).
> 3. The system should have the fewest possible classes.
> 4. The system should have the fewest possible methods.

I believe the current version condenses rules 3 and 4 into one,
and adds "must pass all the tests" as a rule. Phlip also adds a
rule 0: must use the "sane subset" of your language. I take this to
mean it should be readable by an average developer without having
to haul out the problem solving propeller beanie.

> > Even in this short discussion there seem to be other criteria - "I
> > prefer to see code that looks like the problem as understood by the
> > customer".
>
> Hmm. I think this is Ron expressing an opinion. It's not something I've
> heard before or seen published anywhere else as a criteria in TDD. I don't
> know how to judge code using this criteria. Any thoughts on that Ron?

I'm not sure exactly what Ron is getting at, but I would interpret
it as saying that the code should follow the shared domain model
(which may be more implicit than formally documented.)

I believe there is a substantial body of opinion in the OO community
that this is a desirable property.

John Roth
>


0
newsgroups10 (865)
12/16/2003 6:32:44 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fdf1a5f@news.totallyobjects.com>...

> That's cool. But why?:
>    if (s != null){
>       ...
>    }

In Nice, we distinguish between ordinary types (which cannot be null)
and option types (which can include null). Goodbye null pointer
exceptions ;-)

We let the compiler infer the type of the new variable 's' from the
result of the 'search' method.

The search method signature is:
   <Any T> ?T search (java.util.List<!T> list, !T->boolean test)

?T means the return value will be the same type as the list element
(T) or null. In this case that means the type of 's' will be ?Scorer
(Scorer or null).

The methods - score and bonus and next - are defined for Scorer
objects but not for null. The Nice compiler will trap that error at
compile time. We have to guarantee that 's' is not null before we can
use it with Scorer methods.

best wishes, Isaac
0
igouy (1009)
12/16/2003 8:14:43 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312152347.3743c9cf@posting.google.com>...
> Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fde6b72@news.totallyobjects.com>...
> 
> Was that C++ implementation significantly different to this Nice implementation?

Never mind - you seem to be incorporating elements of your previous
C++ implementation in recent posts.
0
igouy (1009)
12/16/2003 8:17:43 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fdf3f0b@news.totallyobjects.com>...

That's a topic of it's own, start a new discussion

> Agreed. The single criteria used in TDD (as far as I am aware) is
> simplicity. Unfortunately, a consensus definition of what is simplest is
> hard to find. In XPE:

-snip-
0
igouy
12/17/2003 12:43:23 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fdf1a5f@news.totallyobjects.com>...

> The condition frameScorers[0].isScorable(q) prevents s from being null. That
> suggests that they are doing some of the same job to me.

Expected you to notice that ;-)

Maybe this one is baked enough for now - what about refactoring the full-on OO?

class ScoreKeeper {
   ArrayList<int> q = new ArrayList(3);
   int total = 0;
   int frames = 1;
   Scorer[] frameScorers = [new Strike(), new Spare(), new Open()];

   void addScore(int pinsKnockedDown){
      q.add(pinsKnockedDown);

      while (frames<=10) {
         let s = frameScorers.search(Scorer each => each.canScore(q));
         if (s == null) break;

         total += s.score(q) + s.bonus(q);
         s.next(q); 
                                    
         frames++;  
         totalScore(total); 
      }
   }   
}


class Scorer {
   boolean canScore(ArrayList<int> q) = false;
   int score(ArrayList<int> q) = q[0] + q[1];
   int bonus(ArrayList<int> q) = 0;  
    
   void next(ArrayList<int> q){
      q.removeAt(0); 
      q.removeAt(0); 
   }   
   
   boolean isScorable(ArrayList<int> q) = 
      q.size==3 || (q.size==2 && q[0] + q[1] < 10);    
}

class Strike extends Scorer {
   canScore(ArrayList q) = q.size==3 && q[0] == 10;
   score(ArrayList q) = q[0];
   bonus(ArrayList q) = q[1] + q[2];  
   
   next(ArrayList q){
      q.removeAt(0); 
   }   
}

class Spare extends Scorer {
   canScore(ArrayList q) = 
      q.size==3 && q[0] != 10 && q[0] + q[1] == 10;    
  
   bonus(ArrayList q) = q[2];   
}

class Open extends Scorer {
   canScore(ArrayList q) = q.size==2 && q[0] + q[1] < 10;   
}
0
igouy (1009)
12/17/2003 1:49:44 AM
Uncle Bob (Robert C. Martin) wrote:
>   private int twoBallsInFrame(int first) {
>     return rolls[first] + rolls[first + 1];
>   }

and:
>   private int nextTwoBallsForStrike(int first) {
>     return rolls[first+1] + rolls[first+2];
>   }
>   private boolean spare(int first) {
>     return rolls[first] + rolls[first + 1] == 10;
>   }

how about:
  private boolean spare(int first) {
    return twoBallsInFrame(first) == 10;
  }

or even:
  private int twoBalls(int first) {
    return rolls[first] + rolls[first+1];
  }
  private int twoBallsInFrame(int first) {
    return twoBalls(first);
  }
  private int nextTwoBallsForStrike(int first) {
    return twoBalls(first+1);
  }
  private boolean spare(int first) {
    return twoBallsInFrame(first) == 10;
  }

0
12/17/2003 1:58:05 AM
Uncle Bob (Robert C. Martin) wrote:
>   public int score() {
>     int score = 0;
>     int first = 0;
>     for (int frame = 0; frame < 10; frame++) {
>       if (strike(first)) {
>         score += 10 + nextTwoBallsForStrike(first);
>         first += 1;
>       }
>       else if (spare(first)) {
>         score += 10 + nextBallForSpare(first);
>         first += 2;
>       } else {
>         score += twoBallsInFrame(first);
>         first += 2;
>       }
>     }
>     return score;
>   }
> 
>   private int twoBallsInFrame(int first) {
>     return rolls[first] + rolls[first + 1];
>   }
>   private int nextBallForSpare(int first) {
>     return rolls[first + 2];
>   }
>   private int nextTwoBallsForStrike(int first) {
>     return rolls[first+1] + rolls[first+2];
>   }
>   private boolean spare(int first) {
>     return rolls[first] + rolls[first + 1] == 10;
>   }
>   private boolean strike(int first) {
>     return rolls[first] == 10;
>   }

or:
   public int score() {
     int score = 0;
     int first = 0;
     for (int frame = 0; frame < 10; frame++) {
       if (strike(first)) {
         score += oneBallForStrikeAndTwoForBonus(first);
         first += 1;
       }
       else if (spare(first)) {
         score += twoBallsForSpareAndOneForBonus(first);
         first += 2;
       } else {
         score += twoBallsInFrame(first);
         first += 2;
       }
     }
     return score;
   }
 
   private int sumBalls(int first, int number) {
     int sum=0;
     for (int ball = 0; ball < number; ball++)
       sum+= rolls[first + ball];
     return sum;
   }
   private int twoBallsInFrame(int first) {
     return sumBalls(first, 2);
   }
   private int oneBallForStrikeAndTwoForBonus(int first) {
     return sumBalls(first, 3);
   }
   private int twoBallsForSpareAndOneForBonus(int first) {
     return sumBalls(first, 3);
   }
   private boolean spare(int first) {
     return twoBallsInFrame(first) == 10;
   }
   private boolean strike(int first) {
     return rolls[first] == 10;
   }



0
12/17/2003 2:23:04 AM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message
news:h3ohsvo0dkmddbtb4onbgs1odbo9uft2pa@4ax.com...
> I use this
> problem a *lot* when teaching classes; and the vast majority of BUFD
> solutions involve frames.  Specifically something like this:
>
>                    +-+ next
>                    | V
>     |Game|------>|Frame|---------->|Ball|
>              10     A          1..2   ^ 1
>                     |                 |
>                 |TenthFrame|----------+
>
> I posted this problem on the net a little over two years ago.  Back
> then there was an outcry against the solution *because* it did not
> contain a Frame class.

There is quite obviously only one stateful dimention  in the problem -
the frame/score progression, which imediately suggest to anyone with
any significant real world design experience that the minimal solution has
one class.

Paul C.


0
12/17/2003 10:57:40 AM
Paul Sinnett wrote:
or:
    public int score() {
      int strikeBonus = 2;
      int spareBonus = 1;
      int ballsForScore = 0;
      int ballsInFrame = 0;
      int score = 0;
      int first = 0;
      for (int frame = 0; frame < 10; frame++) {
        if (strike(first)) {
          ballsForScore = 1 + strikeBonus;
          ballsInFrame = 1;
        }
        else if (spare(first)) {
          ballsForScore = 2 + spareBonus;
          ballsInFrame = 2;
        } else {
          ballsForScore = 2;
          ballsInFrame = 2;
        }
        score += sumBalls(first, ballsForScore);
        first += ballsInFrame;
      }
      return score;
    }
  
    private int sumBalls(int first, int number) {
      int sum=0;
      for (int ball = 0; ball < number; ball++)
        sum+= rolls[first + ball];
      return sum;
    }
    private boolean spare(int first) {
      return sumBalls(first,2) == 10;
    }
    private boolean strike(int first) {
      return rolls[first] == 10;
    }

or even:
    public int score() {
      int bonusBalls = 0;
      int ballsInFrame = 0;
      int score = 0;
      int first = 0;
      for (int frame = 0; frame < 10; frame++) {
        if (strike(first)) {
          bonusBalls = 2;
          ballsInFrame = 1;
        }
        else if (spare(first)) {
          bonusBalls = 1;
          ballsInFrame = 2;
        } else {
          bonusBalls = 0;
          ballsInFrame = 2;
        }
        score += sumBalls(first, ballsInFrame+bonusBalls);
        first += ballsInFrame;
      }
      return score;
    }
  
    private int sumBalls(int first, int number) {
      int sum=0;
      for (int ball = 0; ball < number; ball++)
        sum+= rolls[first + ball];
      return sum;
    }
    private boolean spare(int first) {
      return sumBalls(first,2) == 10;
    }
    private boolean strike(int first) {
      return rolls[first] == 10;
    }


0
12/17/2003 12:50:35 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fdfbe83@news.totallyobjects.com>...

Too easy! 
This: http://groups.google.com/groups?q=g:thl544961689d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0312141310.9bc9145%40posting.google.com&rnum=118
0
igouy (1009)
12/17/2003 2:35:37 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<liattvcfgfop7mathnp6k49k61na6c0l8i@4ax.com>...
> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
> (or about)  14 Dec 2003 20:28:25 -0800, :
> 
> >(A couple of other languages have been used in this thread: some C#
> >for Ron Jeffries, and some C++)
> 
> Here's my favorite Java implementation:

If we ask for the score of rolls 3,3, 3 it reports 9.
At that point we don't know what the score will be for frame 2.

What is the correct way to report the score mid-frame?
- the last known total, 6
- unknown 

best wishes, Isaac
0
igouy (1009)
12/17/2003 4:40:06 PM
Isaac Gouy:
> Paul Sinnett:
>> ... or the requirements are vague enough that we can say they are met by
>> both solutions.

> These are demonstrations of TDD.
> The unit tests represent the requirements (did I misunderstand?)

(Sorry, I must have missed this post earlier.) Yes, you misunderstand (or I
do :-). The tests in TDD are not the requirements. They are part of the TDD
process. As you work with TDD you are likely to change both the tests and
the code. The tests are not all written up front but bit by bit. And you
don't think up a new test until you have code to pass all the existing
tests.

> If the code doesn't pass the same unit tests it doesn't meet the same
> requirements.

The unit tests (now called programmer tests I think) help to define our
design but they don't explicity express the requirements. In XP, for
example, the requirements are defined by the user stories and tested with
acceptance tests (now called customer tests I think.)

So it's not impossible for two examples to have different unit tests but
still fulfil the same, if slightly vague, requirements.
0
12/17/2003 6:02:02 PM
igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
(or about)  17 Dec 2003 08:40:06 -0800, :

>"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<liattvcfgfop7mathnp6k49k61na6c0l8i@4ax.com>...
>> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
>> (or about)  14 Dec 2003 20:28:25 -0800, :
>> 
>> >(A couple of other languages have been used in this thread: some C#
>> >for Ron Jeffries, and some C++)
>> 
>> Here's my favorite Java implementation:
>
>If we ask for the score of rolls 3,3, 3 it reports 9.
>At that point we don't know what the score will be for frame 2.
>
>What is the correct way to report the score mid-frame?
>- the last known total, 6
>- unknown 

Here's an example of a set of acceptance tests that describe a bit
more than you have asked for:

http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject

The program that makes those acceptance tests pass is running in situ.
Just hit the test button to see it work.  




Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
12/18/2003 3:11:11 AM
On 17 Dec 2003 06:35:37 -0800, igouy@yahoo.com (Isaac Gouy) wrote:

>Too easy! 

Isn't easier better?

-- 
Ronald E Jeffries
http://www.XProgramming.com
http://www.objectmentor.com
I'm giving the best advice I have. You get to decide whether it's true for you.
0
ronjeffries (434)
12/18/2003 4:34:47 AM
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<1jb2uv4iiip51cob9dr6p3evpv1tgql6cf@4ax.com>...
> On 17 Dec 2003 06:35:37 -0800, igouy@yahoo.com (Isaac Gouy) wrote:
> 
> >Too easy! 
> Isn't easier better?

Better for what purpose?

This solution is too similar to those that have already been
refactored. Refactoring the full-on OO solution should be more
interesting.
0
igouy (1009)
12/18/2003 2:46:09 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<jh62uv4p661ra23chibk19for564oknveb@4ax.com>...
> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
> (or about)  17 Dec 2003 08:40:06 -0800, :
> 
> >"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<liattvcfgfop7mathnp6k49k61na6c0l8i@4ax.com>...
> >> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
> >> (or about)  14 Dec 2003 20:28:25 -0800, :
> >> 
> >> >(A couple of other languages have been used in this thread: some C#
> >> >for Ron Jeffries, and some C++)
> >> 
> >> Here's my favorite Java implementation:
> >
> >If we ask for the score of rolls 3,3, 3 it reports 9.
> >At that point we don't know what the score will be for frame 2.
> >
> >What is the correct way to report the score mid-frame?
> >- the last known total, 6
> >- unknown 
> 
> Here's an example of a set of acceptance tests that describe a bit
> more than you have asked for:

Actually a bit less ;-)

Those acceptance tests show the final result of filling out a bowling
scorecard. They say nothing about *when* the frame score should be
filled in.

In a bowling competition, are the frame scores calculated at the end
of the game, or are they calculated as-soon-as possible during the
game?

It isn't clear to me what the rules refer to: "The count for the two
deliveries in the frame shall be recorded immediately."

ABC/WIBC/YABA Playing Rules Book 
Chapter II page 6 "Game - How Scored"
https://www.bowlingmembership.com/PDF/03-04PlayingRules_workingcopy.pdf

best wishes, Isaac
0
igouy (1009)
12/18/2003 3:09:50 PM
Isaac Gouy wrote:
>    boolean isStrike() = isStrike(rolls[0]);
> 
>    boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);
> 
>    boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);

You don't appear to have posted code for isStrike(Roll), isSpare(Roll,Roll),
or isOpen(Roll,Roll).
0
12/18/2003 9:04:06 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe216bc@news.totallyobjects.com>...

> >    boolean isStrike() = isStrike(rolls[0]);
> > 
> >    boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);
> > 
> >    boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);
> 
> You don't appear to have posted code for isStrike(Roll), isSpare(Roll,Roll),
> or isOpen(Roll,Roll).

Those methods are in class Roll.

rolls[0].isSpare(rolls[1]) and isSpare(rolls[0],rolls[1]) are equivalent forms
0
igouy (1009)
12/19/2003 2:19:33 AM
Or like:

   class ScoreKeeper {
      ArrayList<Roll> rolls = new ArrayList(21);
      ArrayList<Frame> frames = new ArrayList(10);
      ?Frame pending = null;
      boolean isTenthFrame = false;
      

      void addScore(int pinsKnockedDown){
         let r = new Roll(pins: pinsKnockedDown);
         rolls.add(r);      
         this.fillInFrame(r);
         this.fillInScores;
      }
         
  
      void fillInFrame(Roll aRoll){      
         var pendingFrame = pending;
 
         if (pendingFrame == null){
            if (aRoll.isStrike && !isTenthFrame)
               this.addFrame( newStrike(aRoll) );
            else 
               pending = newFrame(aRoll);
         }
         else {
            pendingFrame.addRoll(aRoll);
            pendingFrame = pending = pendingFrame.asSpecificFrame;

            if (pendingFrame.isComplete(isTenthFrame)) 
               this.addFrame(pendingFrame);
         }    
      }


      void addFrame(Frame aFrame){
         frames.add(aFrame);             
         pending = null;
         isTenthFrame = frames.size >= 9;
      }


      void fillInScores(){
         var total = 0;

         for (eachFrame : frames){
            if (eachFrame.hasFrameScore){
               total = eachFrame.frameScore;
            }
            else {                            
               var i = rolls.indexOf(eachFrame.lastRoll);  
                     
               if (i + eachFrame.bonusRolls < rolls.size){ 

                  let bonusRolls = rolls.slice(
                     i + 1, i + eachFrame.bonusRolls);

                  let bonusScore = bonusRolls.foldLeft(
                     (int sum, Roll each) => 
                        { return each.score + sum; }, 0);
                   
                  total += eachFrame.score + bonusScore;  
                               
                  eachFrame.frameScore = total;    
                  totalScore(total);
               }                                         
            }
         }  
              
      }
      
   }


Frame newFrame(Roll aRoll) = (new Frame()).addRoll(aRoll);  
     
Strike newStrike(Roll aRoll) = (new Strike()).addRoll(aRoll);   


class Frame {
   ArrayList<Roll> rolls = new ArrayList(3);
   int frameScore = -1;

   alike addRoll(Roll aRoll){
      rolls.add(aRoll);
      return this;
   }

   Frame asSpecificFrame(){
      if (this.isStrike) return this.asStrike;
      else if (this.isSpare) return this.asSpare;
      else return this.asOpen;
   }

   boolean isComplete(boolean isTenth) = 
      (!isTenth && rolls.size == this.scoreRolls) || 
       (isTenth && rolls.size == this.scoreRolls + this.bonusRolls);


   boolean isStrike() = isStrike(rolls[0]);

   boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);

   boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);

   Strike asStrike() = new Strike(rolls: rolls);

   Spare asSpare() = new Spare(rolls: rolls);

   Open asOpen() = new Open(rolls: rolls);

   
   boolean hasFrameScore() = frameScore > -1;

   Roll lastRoll() = rolls[this.scoreRolls - 1]; 
   
   int scoreRolls() = 0; 
   int bonusRolls() = 0;
   int score() = 0;
}


let int MaxPins = 10;

class Strike extends Frame {
   isStrike() = true;
   asStrike() = this;
   scoreRolls() = 1;      
   bonusRolls() = 2;
   score() = MaxPins;
}


class Spare extends Frame {
   isSpare() = true;
   asSpare() = this;  
   scoreRolls() = 2;      
   bonusRolls() = 1;    
   score() = MaxPins;    
}


class Open extends Frame {
   isOpen() = true;
   asOpen() = this;
   scoreRolls() = 2;     
   bonusRolls() = 0;    

   score() = rolls.foldLeft(
      (int sum, Roll each) => { return each.score + sum; }, 0);
}


class Roll {
   int pins;

   int score() = pins;

   boolean isStrike() = pins == MaxPins;

   boolean isSpare(Roll aRoll) = pins != MaxPins 
      && pins + aRoll.score == MaxPins;

   boolean isOpen(Roll aRoll) = pins + aRoll.score < MaxPins;
}
0
igouy (1009)
12/19/2003 8:37:18 AM
Isaac Gouy:
> Paul Sinnett:

>>>    boolean isStrike() = isStrike(rolls[0]);
>>> 
>>>    boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);
>>> 
>>>    boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);

>> You don't appear to have posted code for isStrike(Roll),
>> isSpare(Roll,Roll), or isOpen(Roll,Roll).

> Those methods are in class Roll.
> 
> rolls[0].isSpare(rolls[1]) and isSpare(rolls[0],rolls[1]) are equivalent
> forms

I see. Interesting. So if you have both forms defined which gets precedence?
0
12/19/2003 12:24:22 PM
Isaac Gouy wrote:
>       void fillInFrame(Roll aRoll){
>          var pendingFrame = pending;
>  
>          if (pendingFrame == null){
>             if (aRoll.isStrike && !isTenthFrame)
>                this.addFrame( newStrike(aRoll) );
>             else
>                pending = newFrame(aRoll);
>          }
>          else {
>             pendingFrame.addRoll(aRoll);
>             pendingFrame = pending = pendingFrame.asSpecificFrame;
> 
>             if (pendingFrame.isComplete(isTenthFrame))
>                this.addFrame(pendingFrame);
>          }
>       }

Is pendingFrame serving a purpose here? I'm not sure of the rules for this
language but can we do without it:?

   void fillInFrame(Roll aRoll){
      if (pending == null){
         if (aRoll.isStrike && !isTenthFrame)
            this.addFrame( newStrike(aRoll) );
         else
            pending = newFrame(aRoll);
      }
      else {
         pending.addRoll(aRoll);
         pending = pending.asSpecificFrame;
 
         if (pending.isComplete(isTenthFrame))
            this.addFrame(pending);
      }
   }

Also there appears to be some hidden duplication in the logic:

   if (pending == null){
      if (aRoll.isStrike && !isTenthFrame)
         this.addFrame( newStrike(aRoll) );
      else
         pending = newFrame(aRoll);
   }

Could also be written:

   if (pending == null){
      pending = newFrame(aRoll);
      
      if (aRoll.isStrike && !isTenthFrame) {
         pending = pending.asSpecificFrame;
      
         if (pending.isComplete(isTenthFrame))
            this.addFrame(pending);
      }
   }

That makes the duplication obvious but we can't just pull it out of the
conditionals because the current state of the pending frame is important.
If we duplicate the conditional we can extract the code block:

   void fillInFrame(Roll aRoll){
      boolean isNewFrame = pending == null;
      
      if (isNewFrame)
         pending = newFrame(aRoll);
      else
         pending.addRoll(aRoll);

      if ((isNewFrame && aRoll.isStrike) || !isNewFrame)
         pending = pending.asSpecificFrame;

      if (pending.isComplete(isTenthFrame))
         this.addFrame(pending);
   }

This highlights a problem with aRoll::isStrike. It only works as long as the
roll we are testing is the first roll of a new frame. This is true of all
the Roll::isXXX functions. The responsibility more properly belongs to the
Frame object I think. However, now we have separated the creation of the
frame we have a frame object we can ask:

      if (pending.isStrike || !isNewFrame)
         pending = pending.asSpecificFrame;

This is better but it's still making decisions on behalf of the Frame object
that the Frame object is in a better position to handle itself. We can pass
this responsibility to the Frame object by modifying the asSpecificFrame
function slightly from:

>   Frame asSpecificFrame(){
>      if (this.isStrike) return this.asStrike;
>      else if (this.isSpare) return this.asSpare;
>      else return this.asOpen;
>   }

to:

   Frame asSpecificFrame(){
      if (this.isStrike) return this.asStrike;
      else if (this.isSpare) return this.asSpare;
      else if (this.isOpen) return this.asOpen;
      else return this;
   }

Then we can remove the conditional on the call in fillInFrame. We also no
longer need the local variable isNewFrame:

   void fillInFrame(Roll aRoll){
      
      if (pending==null)
         pending = newFrame(aRoll);
      else
         pending.addRoll(aRoll);

      pending = pending.asSpecificFrame;
      
      if (pending.isComplete(isTenthFrame))
         this.addFrame(pending);
   }

0
12/19/2003 3:35:52 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe2ee71@news.totallyobjects.com>...

> > rolls[0].isSpare(rolls[1]) and isSpare(rolls[0],rolls[1]) are equivalent
> > forms
> 
> I see. Interesting. So if you have both forms defined which gets precedence?

Sorry, I don't understand - this is just different syntax for the same thing. 

Maybe assume that 
   rolls[0].isSpare(rolls[1]) 
is always pre-processed to 
   isSpare(rolls[0],rolls[1]) 

best wishes, Isaac
0
igouy (1009)
12/19/2003 7:47:44 PM
igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
(or about)  18 Dec 2003 07:09:50 -0800, :

>"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<jh62uv4p661ra23chibk19for564oknveb@4ax.com>...
>> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
>> (or about)  17 Dec 2003 08:40:06 -0800, :
>> 
>> >"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<liattvcfgfop7mathnp6k49k61na6c0l8i@4ax.com>...
>> >> igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
>> >> (or about)  14 Dec 2003 20:28:25 -0800, :
>> >> 
>> >> >(A couple of other languages have been used in this thread: some C#
>> >> >for Ron Jeffries, and some C++)
>> >> 
>> >> Here's my favorite Java implementation:
>> >
>> >If we ask for the score of rolls 3,3, 3 it reports 9.
>> >At that point we don't know what the score will be for frame 2.
>> >
>> >What is the correct way to report the score mid-frame?
>> >- the last known total, 6
>> >- unknown 
>> 
>> Here's an example of a set of acceptance tests that describe a bit
>> more than you have asked for:
>
>Actually a bit less ;-)

Sorry, I should have been more explicit.  

Look at the specs/tests in
http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming

These show many of the signals that you would need to control an
automated bowling scorekeeper, including detecting when a frame is
scoreable.


Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
12/19/2003 8:03:49 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe31b53@news.totallyobjects.com>...

> Is pendingFrame serving a purpose here? 

The instance field 'pending' has an option type ?Frame (Frame or
null); we can't use ?Frame values where Frame (not null) values are
expected.

We have these options:
- make runtime assertions - notNull(pending).asSpecificFrame
- make the compiler check the values cannot be null (only method
variables)
- make runtime assertions & make the compiler check the values

      void fillInFrame(Roll aRoll){
         Frame pendingFrame;
        
         if (pending==null)
            pendingFrame = newFrame(aRoll);
         else {
            pendingFrame = notNull(pending);
            pendingFrame.addRoll(aRoll);
         }

         pendingFrame = pending = pendingFrame.asSpecificFrame;     
 
         if (pendingFrame.isComplete(isTenthFrame))
            this.addFrame(pendingFrame);
      }

Assuming:

   Frame asSpecificFrame(){
      if (this.isStrike) return this.asStrike;
      else if (this.isSpare) return this.asSpare;
      else if (this.isOpen) return this.asOpen;      
      else return this;
   }

And we removed: newStrike()

And lets simplify: Open score()

   score() = rolls[0].score + rolls[1].score;


best wishes, Isaac
0
igouy (1009)
12/19/2003 8:52:14 PM
> Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>...
 
> >   When I demonstrate Test-Driven Development using the Bowling Game
> >   example

We only seem to have 2 different algorithms for the final score of a
bowling game, and just one algorithm for frame scores. We might call
them 'Multiplier' and 'Score plus Bonus'.

All but one of the implementations have been slight variations on
'Score plus Bonus'. The differences between them have been things like
'do we keep hold of more than the 2-or-3 pin counts needed to
calculate a frame score', 'do we distinguish when a frame score cannot
be calculated'.


Multiplier

M1 To calculate the total score for a game, we need to know the
running total score; and the contribution this roll makes to the total
score.

M2 To calculate the total score for a game, we need to know if we've
reached the tenth frame.

http://groups.google.com/groups?q=g:thl2372571634d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0312110826.616f31cc%40posting.google.com


Score plus Bonus

S1 To calculate a frame score, we need to know the previous frame
score; and the 2 to 3 rolls that followed the previous frame.

(Those 2 to 3 rolls may be from this frame, the next frame, or the
following frame).

S2.1 To calculate the running total score for each frame, we need to
know when each frame has been completed.

S2.2 To calculate the total score for a game, we need to know if we've
reached the tenth frame.


best wishes, Isaac
0
igouy (1009)
12/19/2003 10:16:15 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<m7m6uvsi7g0pqrqh90o9lujvmu2mcunof5@4ax.com>...

> Sorry, I should have been more explicit.  
> Look at the specs/tests in
http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming

> These show many of the signals that you would need to control an
> automated bowling scorekeeper, including detecting when a frame is
> scoreable.

Sorry to be churlish, there seems to be a mistake, frames 5 & 6 aren't
scored:

http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming.TestSpareAndStrikeTiming

Presumably the 'favourite Java implementation' doesn't meet these
acceptance tests? It would be more interesting to see the
implementation that does!

best wishes, Isaac
0
igouy (1009)
12/20/2003 12:37:53 AM
Isaac Gouy wrote:
> And lets simplify: Open score()
> 
>    score() = rolls[0].score + rolls[1].score;

Also, do we really need a Roll class? By removing the call to Roll::isStrike
from the fillInFrame function the only calls to Roll::isStrike,
Roll::isSpare, and Roll::isOpen are from Frame::isStrike, Frame::isSpare,
and Frame::isOpen.

We could quite easily embed the logic in the Frame methods and not need the
Roll methods:

   boolean isStrike() = 
      rolls[0].score == MaxPins;

   boolean isSpare() = 
      rolls.size>=2 && 
      rolls[0] != MaxPins &&
      rolls[0].score + rolls[1].score == MaxPins;

   boolean isOpen() = 
      rolls.size==2 && 
      rolls[0].score + rolls[1].score < MaxPins;

which would leave the Roll class a pure data class.

class Roll {
   int pins;
   int score() = pins;
}

Would it be possible to do away with Roll and just use 'int'? My concern is
the call:

var i = rolls.indexOf(eachFrame.lastRoll);

Would this still work with 'int' instead of a Roll object?
0
12/20/2003 11:08:00 AM
"Paul Sinnett" <paul.sinnett@btinternet.com> wrote in

> Also, do we really need a Roll class? By removing the call to
Roll::isStrike
> from the fillInFrame function the only calls to Roll::isStrike,
> Roll::isSpare, and Roll::isOpen are from Frame::isStrike, Frame::isSpare,
> and Frame::isOpen.
>
> We could quite easily embed the logic in the Frame methods and not need
the
> Roll methods:
>
>    boolean isStrike() =
>       rolls[0].score == MaxPins;

Or just update the Frame digit with an "X".

> ...[code elided]
> which would leave the Roll class a pure data class.
>
> class Roll {
>    int pins;
>    int score() = pins;
> }

My solution a week or so ago sent 'tally ( ... );

a 'pin' struct with an array of pin: pins[10]

*** You need to know if pin[1] is up/down as it affects scoring ***

*No* one else has read the reqs well enuff to stay on that point.

*** Plus, there's  another factor being overlooked that affects scoring.
I'll hold on to it for now, and show in my code to come in a day or 2.

Elliott


0
universe2 (613)
12/20/2003 11:24:32 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe42e0d@news.totallyobjects.com>...

> Also, do we really need a Roll class? 

Need? Of course not! This is a caricature of OO ;-)

> We could quite easily embed the logic in the Frame methods and not need the
> Roll methods...

Let's keep the Roll class and see how far we can go; and then get rid of it.
 

> Would this still work with 'int' instead of a Roll object?

We'd do something different - keep an currentRoll index.

best wishes, Isaac
0
igouy (1009)
12/20/2003 3:43:41 PM
igouy@yahoo.com (Isaac Gouy) might (or might not) have written this on
(or about)  19 Dec 2003 16:37:53 -0800, :

>"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<m7m6uvsi7g0pqrqh90o9lujvmu2mcunof5@4ax.com>...
>
>> Sorry, I should have been more explicit.  
>> Look at the specs/tests in
>http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming
>
>> These show many of the signals that you would need to control an
>> automated bowling scorekeeper, including detecting when a frame is
>> scoreable.
>
>Sorry to be churlish, there seems to be a mistake, frames 5 & 6 aren't
>scored:
>
>http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming.TestSpareAndStrikeTiming

Thanks for pairing!  Yes, there was an error in the test case.  I've
corrected the test case, so see what you think now.  You'll note that
the test case fails, because the java program beneath it broken.  I've
fixed it on my local version, and it will be updated at fitnesse.org
at the next release of fitnesse.

>Presumably the 'favourite Java implementation' doesn't meet these
>acceptance tests? 

I presume you hit the test button and saw that the tests passed.  The
java implementation specified by the tests did, in fact, pass those
tests.  Now, of course, it does not.  

I wouldn't call this implementation my favorite.  I've only recently
begun to explore this particular aspect of the problem.  And I haven't
had a lot of time to put on it.  At the moment the scoring element is
just about the same as we've seen before.  The element that captures
all the timing signals is a finite state machine.  

>It would be more interesting to see the
>implementation that does!

The version below is still pretty ugly I think.  But you should see
the basic finite state machine growing there.  Eventually I think it
could be refactored to use SMC to generate the finite state machine.
That would clean it up a lot.  I also think there are some states that
should be superstates.  We'll see.  (BTW, this version *does* pass the
corrected acceptance tests.)

package eg.bowling;

public class BowlingGame implements Bowling {
  private static final int FIRST_BALL_IN_FRAME = 0;
  private static final int SECOND_BALL_IN_FRAME = 1;
  private static final int GAME_OVER = 2;
  private static final int BALL_AFTER_SPARE = 3;
  private static final int BALL_AFTER_TENTH_FRAME_SPARE = 4;
  private static final int BALL_AFTER_FIRST_STRIKE = 5;
  private static final int BALL_AFTER_SECOND_STRIKE = 6;
  private static final int SECOND_BALL_AFTER_STRIKE = 7;

  private int currentFrame = 1;
  private int currentBall = 1;
  private int scoreableFrame = 0;
  private boolean gameOver = false;

  private int state = FIRST_BALL_IN_FRAME;
  private final BowlingScorer bowlingScorer = new BowlingScorer();

  private void changeState() {
    switch (state) {
      case FIRST_BALL_IN_FRAME:
        firstBallInFrame();
        break;

      case SECOND_BALL_IN_FRAME:
        secondBallInFrame();
        break;

      case BALL_AFTER_SPARE:
        ballAfterSpare();
        break;

      case BALL_AFTER_TENTH_FRAME_SPARE:
        endGame();
        break;

      case BALL_AFTER_FIRST_STRIKE:
        ballAfterFirstStrike();
        break;

      case BALL_AFTER_SECOND_STRIKE:
        ballAfterSecondStrike();
        break;

      case SECOND_BALL_AFTER_STRIKE:
        secondBallAfterStrike();
        break;
    }

  }

  private void firstBallInFrame() {
    if (bowlingScorer.lastRollWasStrike()) {
      state = BALL_AFTER_FIRST_STRIKE;
      incrementFrame();
    } else {
      state = SECOND_BALL_IN_FRAME;
      currentBall = 2;
    }
  }

  private void secondBallInFrame() {
    if (bowlingScorer.lastRollWasSpare() && currentFrame == 10) {
      state = BALL_AFTER_TENTH_FRAME_SPARE;
      currentBall = 3;
    } else if (bowlingScorer.lastRollWasSpare()) {
      state = BALL_AFTER_SPARE;
      incrementFrame();
    } else if (currentFrame == 10) {
      endGame();
    } else {
      state = FIRST_BALL_IN_FRAME;
      scoreableFrame = currentFrame;
      incrementFrame();
    }
  }

  private void secondBallAfterStrike() {
    scoreableFrame++;
    secondBallInFrame();
  }

  private void ballAfterSpare() {
    if (bowlingScorer.lastRollWasStrike()) {
      state = BALL_AFTER_FIRST_STRIKE;
      scoreableFrame++;
      incrementFrame();
    } else {
      state = SECOND_BALL_IN_FRAME;
      currentBall = 2;
      scoreableFrame++;
    }
  }

  private void ballAfterFirstStrike() {
    if (bowlingScorer.lastRollWasStrike()) {
      state = BALL_AFTER_SECOND_STRIKE;
      incrementFrame();
    } else {
      state = SECOND_BALL_AFTER_STRIKE;
      currentBall = 2;
    }
  }

  private void ballAfterSecondStrike() {
    if (bowlingScorer.lastRollWasStrike() && currentFrame == 10 &&
currentBall == 3) {
      endGame();
    } else if (bowlingScorer.lastRollWasStrike()) {
      incrementFrame();
      scoreableFrame++;
    } else {
      state = SECOND_BALL_IN_FRAME;
      currentBall = 2;
      scoreableFrame++;
    }
  }

  private void endGame() {
    state = GAME_OVER;
    currentBall = 0;
    scoreableFrame = 10;
    gameOver = true;
  }

  private void incrementFrame() {
    if (currentFrame < 10) {
      currentFrame++;
      currentBall = 1;
    } else
      currentBall++;
  }

  public int currentFrame() {
    return currentFrame;
  }

  public int currentBall() {
    return currentBall;
  }

  public int scoreableFrame() {
    return scoreableFrame;
  }

  public boolean validGame() {
    return true;
  }

  public boolean gameOver() {
    return currentFrame == 10 && currentBall == 0;
  }

  public boolean isGameOver() {
    return gameOver;
  }

  public void roll(int pins) {
    bowlingScorer.roll(pins);
    changeState();
  }

  public int score(int frame) {
    return bowlingScorer.score(frame);
  }
}


Robert C. Martin    | "Uncle Bob"                   
Object Mentor Inc.  | unclebob @ objectmentor . com
501 N. Riverside Dr.| Tel: (800) 338-6716         
Suite 206           | Fax: (847) 775-8174           | www.objectmentor.com
                    |                               | www.XProgramming.com
Gurnee, IL,         | Training and Mentoring        | www.junit.org
60031               | OO, XP, Agile, C++, Java, C#  | http://fitnesse.org
0
12/20/2003 3:44:53 PM
Isaac Gouy wrote:
> Let's keep the Roll class and see how far we can go; and then get rid of
> it.

Okay. The following functions are redundant: Strike::isStrike,
Spare::isSpare, Open::isOpen, Strike::asStrike, Spare::asSpare,
Open::asOpen. They can be deleted without breaking the tests.

Also, as I see you have already spotted, Frame::isComplete,
Strike::isComplete, Spare::isComplete, and Open::isComplete can all be
written:

>   boolean isComplete(boolean isTenth) = 
>      (!isTenth && rolls.size == this.scoreRolls) || 
>       (isTenth && rolls.size == this.scoreRolls + this.bonusRolls);

In a similar way, Strike::score, Spare::score, and Open::score can all be
written:

   int score(){
      var score = 0;
      for (var i = 0; i < this.scoreRolls; i++) 
         score += rolls[i].score;
      return score;   
   }

as it was in your originally posted version. Removing this duplication
leaves the Strike, Spare, and Open classes looking like this:

   class Strike extends Frame {
      scoreRolls() = 1;      
      bonusRolls() = 2;
   }

   class Spare extends Frame {
      scoreRolls() = 2;      
      bonusRolls() = 1;    
   }

   class Open extends Frame {
      scoreRolls() = 2;     
      bonusRolls() = 0;    
   }

We could remove the need for these classes by making scoreRolls and
bonusRolls into variables and modifying the existing Frame class:

   Frame asStrike(){ 
      this.scoreRolls=1;
      this.bonusRolls=2;
      return this;
   }

   Frame asSpare(){ 
      this.scoreRolls=2;
      this.bonusRolls=1;
      return this;
   }

   Frame asOpen(){ 
      this.scoreRolls=2;
      this.bonusRolls=0;
      return this;
   }

In fact, without different instances to worry about we no longer need to
pass around the pointers, and we can embed the as-XXX methods into the
addRoll method and simplify ScoreKeeper::fillInFrame.

ScoreKeeper::fillInFrame:

   void fillInFrame(Roll aRoll){
     
      if (pending==null)
         pending = new Frame;

      Frame pendingFrame = notNull(pending);
      pendingFrame.addRoll(aRoll);

      if (pendingFrame.isComplete(isTenthFrame))
         this.addFrame(pendingFrame);
   }

Frame::addRoll:

   void addRoll(Roll aRoll){
      rolls.add(aRoll);
      if (this.isStrike){
         this.scoreRolls=1;
         this.bonusRolls=2;
      }
      if (this.isSpare){
         this.scoreRolls=2;
         this.bonusRolls=1;
      }
      if (this.isOpen){
         this.scoreRolls=2;
         this.bonusRolls=0;
      }
   }

delete newFrame, Frame::asSpecificFrame, Frame::asStrike, Frame::asOpen, and
Frame::asSpare.
0
12/20/2003 6:38:22 PM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<74r8uv02djdt0v5ehs6ac5b1iae1fmk5aj@4ax.com>...

>http://fitnesse.org/FitNesse.WritingAcceptanceTests.BowlingGameProject.SuiteTiming.TestSpareAndStrikeTiming
> 
> Thanks for pairing!  Yes, there was an error in the test case.  I've
> corrected the test case, so see what you think now.  

I think you're still missing scoring frame 6!

(should score 95)
0
igouy (1009)
12/20/2003 10:03:00 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe49798@news.totallyobjects.com>...

> > Let's keep the Roll class and see how far we can go; and then get rid of
> > it. 
> Okay. The following functions are redundant...

Fair enough maestro, what next fillInScores or class Roll?

(Gosh darn, we're all releaved there's still a Frame class!)
0
igouy (1009)
12/21/2003 8:31:30 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe49798@news.totallyobjects.com>...

addFrame wasn't doing anything, and simple inference from the assigned
value is good enough for pendingFrame's type:

      void fillInFrame(Roll aRoll){        
         if (pending==null)
            pending = new Frame();

         let pendingFrame = notNull(pending);
         pendingFrame.addRoll(aRoll);
   
         if (pendingFrame.isComplete(isTenthFrame)) {
            frames.add(pendingFrame);             
            pending = null;
            isTenthFrame = frames.size >= 9;
         }
      }
0
igouy (1009)
12/21/2003 8:36:42 AM
Isaac Gouy wrote:
> Fair enough maestro, what next fillInScores or class Roll?
> 
> (Gosh darn, we're all releaved there's still a Frame class!)

I think procrastinating on the Roll class was a good idea because it was
used in so many places. Because we've reduced the total amount of code
we've also reduced the amount of work we'll need to do to remove Roll if we
want to later.

In fillInScores we're repeatedly recalculating the value 'total'. If we made
'total' a member variable we wouldn't have to recalculate it on every call:

   int total = 0;

   void fillInScores(){

      for (eachFrame : frames){
         if (!eachFrame.hasFrameScore){
            var i = rolls.indexOf(eachFrame.lastRoll);  
                     
            if (i + eachFrame.bonusRolls < rolls.size){ 

               let bonusRolls = rolls.slice(
                  i + 1, i + eachFrame.bonusRolls);

               let bonusScore = bonusRolls.foldLeft(
                  (int sum, Roll each) => 
                     { return each.score + sum; }, 0);
                   
               total += eachFrame.score + bonusScore;  
                               
               eachFrame.frameScore = total;    
               totalScore(total);
            }                                         
         }
      }  
   }

The only remaining reason for keeping the array of frames is to test for the
tenth frame. We can do that with another member variable. Also if we
removed frames from the list once they had been scored we wouldn't need to
test for hasFrameScore.  Does the eachFrame loop handle changes to the
container within the loop? That is, would this work?

   for (eachFrame : frames)
      if (eachFrame.hasFrameScore)
         frames.remove(eachFrame);

We can certainly do it by managing the loop variables ourselves:

   int framesScored=0;

   void fillInScores(){

      for (int frameIndex=0; frameIndex< frames.size;){
         Frame eachFrame = frames[frameIndex];
         var i = rolls.indexOf(eachFrame.lastRoll);  
                     
         if (i + eachFrame.bonusRolls < rolls.size){ 

            let bonusRolls = rolls.slice(
               i + 1, i + eachFrame.bonusRolls);

            let bonusScore = bonusRolls.foldLeft(
               (int sum, Roll each) => 
                  { return each.score + sum; }, 0);
                   
            total += eachFrame.score + bonusScore;  
                               
            totalScore(total);
            frames.remove(eachFrame);
            framesScored++;
            frameIndex=0;
         }
         else
            frameIndex++;
      }  
   }

   void fillInFrame(Roll aRoll){        
      if (pending==null)
         pending = new Frame();

      let pendingFrame = notNull(pending);
      pendingFrame.addRoll(aRoll);
   
      if (pendingFrame.isComplete(isTenthFrame)) {
         frames.add(pendingFrame);             
         pending = null;
         isTenthFrame = frames.size + framesScored >= 9;
      }
   }

So that gets rid of Frame::hasFrameScore, Frame::frameScore, and the special
value of -1.

Now it occurs to me that 'frames' only contains frames that are pending
bonuses and frames that can be scored straight away. If we didn't add
frames that could be scored straight away then we could calculate the
bonuses as we go without doing all the reverse lookup stuff.

To make that happen we need to extract the scoring block from the middle of
the loop:

   boolean scoreFrame(Frame aFrame){
      var i = rolls.indexOf(aFrame.lastRoll);  
                     
      if (i + aFrame.bonusRolls < rolls.size){ 

         let bonusRolls = rolls.slice(
            i + 1, i + aFrame.bonusRolls);

         let bonusScore = bonusRolls.foldLeft(
            (int sum, Roll each) => 
               { return each.score + sum; }, 0);
                   
         total += aFrame.score + bonusScore;  
                             
         totalScore(total);
         framesScored++;
         return true;
      }
      else
         return false;
   }

   void fillInScores(){

      for (int frameIndex=0; frameIndex< frames.size;){
         Frame eachFrame = frames[frameIndex];
         if (scoreFrame(eachFrame)){

            frames.remove(eachFrame);
            frameIndex=0;
         }
         else
            frameIndex++;
      }
   }

Now we can call scoreFrame before adding a pending frame to the list as long
as we change the order that fillInScores and fillInFrame are called:

   void addScore(int pinsKnockedDown){
      let r = new Roll(pins: pinsKnockedDown);
      rolls.add(r);      
      this.fillInScores;
      this.fillInFrame(r);
   }

   void fillInFrame(Roll aRoll){        
      if (pending==null)
         pending = new Frame();

      let pendingFrame = notNull(pending);
      pendingFrame.addRoll(aRoll);
   
      if (pendingFrame.isComplete(isTenthFrame)) {
         if (!scoreFrame(pendingFrame))
            frames.add(pendingFrame);             
         pending = null;
         isTenthFrame = frames.size + framesScored >= 9;
      }
   }

So to simplify counting the bonus we can now add the bonus rolls to the
Frame objects and get the frame to work out the score.

In ScoreKeeper:

   void addScore(int pinsKnockedDown){
      let r = new Roll(pins: pinsKnockedDown);

      for (eachFrame: frames)
         eachFrame.addRoll(r);
      
      this.fillInScores();
      this.fillInFrame(r);
   }

   boolean scoreFrame(Frame aFrame){

      if (aFrame.canScore){

         total += aFrame.score;  
                             
         totalScore(total);
         framesScored++;
         return true;
      }
      else
         return false;
   }

and in Frame:

   boolean canScore() = 
      rolls.size>0 && rolls.size==scoreRolls+bonusRolls;

   int score() = rolls.foldLeft(
      (int sum, Roll each) => { return each.score + sum; }, 0);

Then we can delete the rolls container from ScoreKeeper.

0
12/21/2003 1:18:19 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe59e19@news.totallyobjects.com>...

> So to simplify counting the bonus we can now add the bonus rolls to the
> Frame objects and get the frame to work out the score.

This is an interesting approach, we're keeping a list of pending
frames (frames we don't yet have enough information to score), and
providing each new roll to all of those pending frames. Each frame
knows how many rolls it uses as scoring rolls (from the instance
variable) and how many it uses as bonus rolls.

The rolls collection each frame instance holds is just sufficient to
calculate the frame score for that frame - the scoring is local to
each frame instance.

Well done! I'll take a closer look at this.

best wishes, Isaac
0
igouy (1009)
12/21/2003 8:12:38 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe59e19@news.totallyobjects.com>...

> Does the eachFrame loop handle changes to the
> container within the loop? That is, would this work?
> 
>    for (eachFrame : frames)
>       if (eachFrame.hasFrameScore)
>          frames.remove(eachFrame);

No that would fail. Still there's no reason to start messing with loop
indexes, yet. The ordinary approach would be iterate over a copy:

      for (each : frames.clone)
         if (this.hasScored(each))
            frames.remove(each);

And in Nice there's a library function, that'll do the loop index
stuff and just let use specify a test function:

      frames.retain(
         Frame each => { return !this.hasScored(each); }
      );

> Now we can call scoreFrame before adding a pending frame to the list as long
> as we change the order that fillInScores and fillInFrame are called:

Think there might be some mistake here - works fine with the original
order, first fillInFrame then fillInScores.

best wishes, Isaac
0
igouy (1009)
12/22/2003 12:57:25 AM
So now we have this (the !hasScored name and side-effect seems a bit odd):

class ScoreKeeper {
   ArrayList<Frame> frames = new ArrayList(3);
   ?Frame pending = null;
   boolean isTenthFrame = false;
   int total = 0;
   int framesScored = 0;
      
   void addScore(int pinsKnockedDown){
      let r = new Roll(pins: pinsKnockedDown);
      for (eachFrame: frames)
         eachFrame.addRoll(r);
      this.fillInFrame(r);     
      this.fillInScores;
   }

   void fillInFrame(Roll aRoll){        
      if (pending==null)
         pending = new Frame();

      let pendingFrame = notNull(pending);
      pendingFrame.addRoll(aRoll);
   
      if (pendingFrame.isComplete(isTenthFrame)) {
         frames.add(pendingFrame);             
         pending = null;
         isTenthFrame = frames.size + framesScored >= 9;
      }
   }

   void fillInScores(){
      frames.retain(
         Frame each => { return !this.hasScored(each); }
      );
   }

   boolean hasScored(Frame aFrame){
      let scoreable = aFrame.isScoreable;
      if (scoreable){
         total += aFrame.score;  
         framesScored++;                             
         totalScore(total);
      }
      return scoreable;
   }
   
}
  
      
class Frame {
   ArrayList<Roll> rolls = new ArrayList(3);
   int scoreRolls = 0;
   int bonusRolls = 0;

   void addRoll(Roll aRoll){
      rolls.add(aRoll);

      if (this.isStrike){
         scoreRolls = 1;
         bonusRolls = 2;
      }

      if (this.isSpare){
         scoreRolls = 2;
         bonusRolls = 1;
      }

      if (this.isOpen){
         scoreRolls = 2;
         bonusRolls = 0;
      }
   }

   boolean isComplete(boolean isTenth) = 
      (!isTenth && rolls.size == scoreRolls) || 
       (isTenth && rolls.size == scoreRolls + bonusRolls);

   boolean isStrike() = isStrike(rolls[0]);

   boolean isSpare() = rolls.size>=2 && isSpare(rolls[0],rolls[1]);

   boolean isOpen() = rolls.size==2 && isOpen(rolls[0],rolls[1]);

   boolean isScoreable() = 
      rolls.size>0 && (rolls.size == scoreRolls + bonusRolls);

   int score() = rolls.foldLeft(
      (int sum, Roll each) => { return each.score + sum; }, 0);

}


let int MaxPins = 10;

class Roll {
   int pins;

   int score() = pins;

   boolean isStrike() = pins == MaxPins;

   boolean isSpare(Roll aRoll) = pins != MaxPins 
      && pins + aRoll.score == MaxPins;

   boolean isOpen(Roll aRoll) = pins + aRoll.score < MaxPins;
}
0
igouy (1009)
12/22/2003 1:01:42 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe59e19@news.totallyobjects.com>...

This is better:

   void fillInScores(){
      frames.remove(
         Frame each => { return this.hasScored(each); }
      );
   }
0
igouy (1009)
12/22/2003 2:01:57 AM
Isaac Gouy:
> Paul Sinnett:
>> Now we can call scoreFrame before adding a pending frame to the list as
>> long as we change the order that fillInScores and fillInFrame are called:

> Think there might be some mistake here - works fine with the original
> order, first fillInFrame then fillInScores.

I think the total score will be correct, but some frames will get scored in
the wrong order. Eg, a strike pending bonus scores in the frames array will
get scored after an open frame that follows it unless we reverse the call
order.
0
12/22/2003 9:35:44 AM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe6bb84@news.totallyobjects.com>...
> Isaac Gouy:
> > Paul Sinnett:
> >> Now we can call scoreFrame before adding a pending frame to the list as
> >> long as we change the order that fillInScores and fillInFrame are called:
>  
> > Think there might be some mistake here - works fine with the original
> > order, first fillInFrame then fillInScores.
> 
> I think the total score will be correct, but some frames will get scored in
> the wrong order. Eg, a strike pending bonus scores in the frames array will
> get scored after an open frame that follows it unless we reverse the call
> order.

Back to testing? ;-)

   void addScore(int pinsKnockedDown){
      let r = new Roll(pins: pinsKnockedDown);

      for (eachFrame: frames)
         eachFrame.addRoll(r);

      this.fillInFrame(r);     
      this.fillInScores;
   }

void main(String[] args){  
   let int[][] tests = [
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3, 3,3],
         [4,6, 3,5, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [4,6, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 10, 5,3],
         [0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 4,6, 5],
         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
         [10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10, 4,6, 10],
         [],
         [3],
         [3,3],
         [3,3, 3],
         [4,6],
         [4,6, 3],
         [4,6, 3,5],
         [10],
         [10, 5],
         [10, 5,3],
         [10, 5,3, 0],
         [10, 4,6],
         [10, 4,6, 10],
         [10, 4,6, 10, 4],
         [10, 4,6, 10, 4,6],
         [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
         [11],
         [0,10, 1,1],
         [1,4, 3,7, 5,2, 10,10, 5,5, 3,2, 1,0, 7,1, 5,5, 3],
         [5,5, 3,2, 7,3, 10,5, 5,10, 3,4, 5,4, 3,5, 5,5, 5]
      ];

   for(pinScores:tests) {
      let sk = new ScoreKeeper();
      for(each:pinScores) sk.addScore(each);
      print("\n");
   }
}

0 0 0 0 0 0 0 0 0 0
6 12 18 24 30 36 42 48 54 60
13 21 21 21 21 21 21 21 21 21
15 23 23 23 23 23 23 23 23 23
18 26 26 26 26 26 26 26 26 26
0 0 0 0 0 0 0 0 0 18
0 0 0 0 0 0 0 0 0 15
30 60 90 120 150 180 210 240 270 300
20 40 60 80 100 120 140 160 180 200


6
6

13
13 21


18 26
18 26
20
20 40
20 40
20 40 60
30 60 90 120 150 180 210 240 270 300

11 13
5 20 27 52 72 85 90 91 99 112
13 18 38 58 78 95 102 111 119 134
0
igouy (1009)
12/22/2003 5:36:32 PM
Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe6bb84@news.totallyobjects.com>...

> I think the total score will be correct, but some frames will get scored in
> the wrong order. Eg, a strike pending bonus scores in the frames array will
> get scored after an open frame that follows it unless we reverse the call
> order.

   void addScore(int pinsKnockedDown){
      let r = new Roll(pins: pinsKnockedDown);

      for (eachFrame: frames)
         eachFrame.addRoll(r);

      this.fillInScores;
      this.fillInFrame(r);     
   }


Nice\TenPin>java -jar tenpin.jar


13
15
18


30 60 90 120 150 180 210 240 270
20 40 60 80 100 120 140 160 180





13
13


18
18
20
20 40
20 40
20 40 60
30 60 90 120 150 180 210 240 270

11
15 40 60 73
13 33 53 73 90
0
igouy (1009)
12/22/2003 5:40:05 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312211212.2c5ece93@posting.google.com>...
> Paul Sinnett <paul.sinnett@btinternet.com> wrote in message news:<3fe59e19@news.totallyobjects.com>...

> > So to simplify counting the bonus we can now add the bonus rolls to the
> > Frame objects and get the frame to work out the score.
> 
> This is an interesting approach

Instead of a special pendingFrame we can work with the last frame in
the list; and all the Roll methods can be refactored into Frame.
(Let's also add some runtime assertions to check for valid data.)

let int MaxFrames = 10;
let int MaxPins = 10;

class ScoreKeeper {
   ArrayList<Frame> unscoredFrames = new ArrayList(3);
   int total = 0;
   int framesScored = 0;
   boolean noteEachTotal;
   

   addScore(int pinsKnockedDown){
      if (this.isNewFrame)
         unscoredFrames.add( new Frame() );

      for (each : unscoredFrames)
         each.addRoll(pinsKnockedDown);

      unscoredFrames.remove(
         Frame each => { return this.hasScored(each); }
      );
   }


   boolean isNewFrame(){
      let n = unscoredFrames.size;
      let isTenthFrame = framesScored+1 == MaxFrames;

      return 
         n == 0 || 
         unscoredFrames[n-1].isComplete(isTenthFrame);
   }


   boolean hasScored(Frame aFrame){
      let scoreable = aFrame.isScoreable;
      if (scoreable){
         total += aFrame.score;     
         framesScored++;           
               
         if (noteEachTotal || framesScored==MaxFrames) 
            totalScore(total); 
      }
      return scoreable;
   }


   void addScore(int pinsKnockedDown)
      requires
         pinsKnockedDown >= 0 && pinsKnockedDown <= MaxPins 
            : "pinsKnockedDown == " pinsKnockedDown.toString,

         this.framesScored < MaxFrames  
            : "framesScored == " this.framesScored.toString;
}
  
      
class Frame {
   ArrayList<int> rolls = new ArrayList(3);
   int scoreRolls = 0;
   int bonusRolls = 0;

   void addRoll(int aRoll){
      rolls.add(aRoll);

      if (this.isStrike){
         scoreRolls = 1;
         bonusRolls = 2;
      }

      if (this.isSpare){
         scoreRolls = 2;
         bonusRolls = 1;
      }

      if (this.isOpen){
         scoreRolls = 2;
         bonusRolls = 0;
      }
   }

   boolean isComplete(boolean isTenth) = 
      (!isTenth && rolls.size == scoreRolls) || 
       (isTenth && rolls.size == scoreRolls + bonusRolls);

   boolean isStrike() = rolls[0] == MaxPins;

   boolean isSpare() = rolls.size>=2 && 
      (!this.isStrike && rolls[0] + rolls[1] == MaxPins);

   boolean isOpen() = rolls.size==2 && rolls[0] + rolls[1] < MaxPins;

   boolean isScoreable() = 
      rolls.size>0 && (rolls.size == scoreRolls + bonusRolls);

   int score() = rolls.foldLeft(
      (int sum, int each) => { return each + sum; }, 0);
}
0
igouy (1009)
12/23/2003 8:12:52 AM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312211212.2c5ece93@posting.google.com>...

> This is an interesting approach, we're keeping a list of pending
> frames (frames we don't yet have enough information to score), and
> providing each new roll to all of those pending frames. Each frame
> knows how many rolls it uses as scoring rolls (from the instance
> variable) and how many it uses as bonus rolls.

Isaac,

Here is a variation on the (variation on the :-) ) theme.
The previous impl logic computed a total or per frame score on a throw
by throw basis.

The new impl logic (changes marked by *** ) does the above , but also
enables the component user to query when the final score for any given
frame is available (ie when all legal scoring adjustments have been made
for the frame) . Similarly when the final total is available (ie when the
score for frame 10 is available) .

The changes increase the 'normalised' LOC count by 10 lines.


Regards,
Steven Perryman

-------------------------------------------

public
class TPScorer
{

// Constructors/Destructors

public
TPScorer() {}


// pre: PinsHit IN [0,PINS]

public
void
attemptMade(short iPinsHit)
{
    ivThrows++ ;
    recordThrow(ivFrame,iPinsHit) ;

    if(ivFrame < FRAMES)
    {
             if(ivScore[ivFrame - 1] == PINS)
        {
           adjustScores(ivFrame, 3 - ivThrows) ;
           newFrame() ;
        }
        else if(ivThrows == 2) { newFrame() ; }
    }
}

private
void
newFrame()
{
    ivFrame++ ;
    ivThrows = 0 ;
}


public
short
frames() { return ( (short) (ivFrame + 1) ; }


// post: RESULT IN [0, frames() * 3 * PINS]

public
short
total()
{
    short oTotal = 0 ;

    for( short iter = 0 ; iter != ivFrame ; iter++ )
    {
        oTotal += ivScore[iter] ;
    }

    return oTotal ;
}


// ***
// pre: Frame IN [1, frames() ]

public
boolean
finalScoreAvailable(short iFrame)
{
    boolean oResult = false ;

    short f = (short) (iFrame - 1) ;

         if(f < ivFrame)
    {
        oResult =
        ! ( (ivAdjustFrame[0] == f) || (ivAdjustFrame[1] == f) ) ;
    }
    else if(f == 9)
    {
        oResult =
        (ivThrows == 3) || ( (ivThrows == 2) && (ivScore[f] < 10) ) ;
    }

    return oResult ;
}


// pre: Frame IN [1, frames() ]
// post: RESULT IN [0, 3 * PINS]

public
short
score(short iFrame) { return ivScore[iFrame - 1] ; }


// Indicate that the score for the given frame will be adjusted by the
// scores made by the given successive throws.

private
void
adjustScores(short iFrame,
             short iThrows)
{
    short iter = 0 ;

    if(ivAdjustThrows[iter] > 0) { iter++ ; }

    ivAdjustFrame[iter] = (short) (iFrame - 1) ;
    ivAdjustThrows[iter] = iThrows ;
}

private
void
recordThrow(short iFrame,
            short iPinsHit)
{
    // Update the score of the given frame
    ivScore[iFrame - 1] += iPinsHit ;

    // Adjust the scores of the previous denoted frames (if any)
    for( short iter = 0 ; iter != TPScorer.CanAdjust ; iter++ )
    {
        if(ivAdjustThrows[iter] > 0)
        {
            ivScore[ivAdjustFrame[iter] ] += iPinsHit ;
            ivAdjustThrows[iter]-- ;
            ivAdjustFrame[iter] *= ivAdjustThrows[iter] ; // ***
        }
    }
}


// Constants

// The maximum number of frames whose scores can be adjusted by any
// throw made.
private
static final
short CanAdjust = 2 ;


// Instance variables

private
short ivFrame = 1 ;

private
short ivThrows = 0 ;

private
short[] ivScore = new short[FRAMES] ;

private
short[] ivAdjustFrame = new short[TPScorer.CanAdjust] ;

private
short[] ivAdjustThrows = new short[TPScorer.CanAdjust] ;


}
0
ggroups5 (201)
12/23/2003 11:15:07 AM
"Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com> wrote in message news:<74r8uv02djdt0v5ehs6ac5b1iae1fmk5aj@4ax.com>...

> But you should see the basic finite state machine growing there.  
> (BTW, this version *does* pass the corrected acceptance tests.)

Correct code is more interesting ;-)
 
Does this FSM based design have any qualities that make it more
interesting than the designs we've already seen?
0
igouy (1009)
12/23/2003 8:15:17 PM
ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0312230315.4afec7f0@posting.google.com>...

Thanks, the change I've made to provide the running total score
doesn't seem quite correct - 'finalScoreAvailable(ivFrame)' seems to
return true on every roll?

> public
> void
> attemptMade(short iPinsHit)
> {
>     ivThrows++ ;
>     recordThrow(ivFrame,iPinsHit) ;
> 
>     if(ivFrame < FRAMES)
>     {
>              if(ivScore[ivFrame - 1] == PINS)
>         {
>            adjustScores(ivFrame, 3 - ivThrows) ;
>            newFrame() ;
>         }
>         else if(ivThrows == 2) { newFrame() ; }
>     }

      if( ivFrame <= FRAMES && finalScoreAvailable(ivFrame) )
         totalScore( total() ); 
> }
0
igouy (1009)
12/23/2003 11:05:42 PM
"Isaac Gouy" <igouy@yahoo.com> wrote in message

> "Uncle Bob (Robert C. Martin)" <u.n.c.l.e.b.o.b@objectmentor.com>
wrote in message

> > But you should see the basic finite state machine growing there.
> > (BTW, this version *does* pass the corrected acceptance tests.)

> Correct code is more interesting ;-)
>
> Does this FSM based design have any qualities that make it more
> interesting than the designs we've already seen?

Don't know if this is it, however frequently FSM's are the least complex
way to carry out, or facilitate analysis, and to manage the operation,
and behavior of a domain entity, or new abstraction for the domain in
the software.  Many use Petri nets and other traditional state modelling
paradigms within OO designs.  That is why Petri state modelling was
incorporated wholesale as a sub-area, into UML.

The Shlaer-Mellor (S-M) methodology has really advanced FSM analysis and
integrated such analysis with some aspects of the OO paradigm.  While
that is positive, S-M, or at least many of the S-M adherents, tend to
raise E/R data modelling over and above the OO paradigm in the analysis
and system designs.

Often state modelling is used to analyze and design software for
embedded controllers.  Perhaps you can see it's value in software for a
VCR/TV controller?  However, it's still important with things like that
to have users test the interface and in fact users should be the primary
determinant in what makes sense regarding the sequence of state changes.

Elliott
-- 
Once a fortnight, Audrey and her close companion were driven to
re-experience bathing in the pure sound of shattering panes of glass.


0
universe2 (613)
12/24/2003 12:29:54 AM
Isaac Gouy <igouy@yahoo.com> wrote in message
news:ce7ef1c8.0312231505.10076d03@posting.google.com...

>ggroups@bigfoot.com (S Perryman) wrote in message
news:<745b753c.0312230315.4afec7f0@posting.google.com>...

>Thanks, the change I've made to provide the running total score
>doesn't seem quite correct - 'finalScoreAvailable(ivFrame)' seems to
>return true on every roll?

That is what happens in a stupid rush to post ones thoughts. :-(
The correct logic is :

// Determine whether the final score for a given frame is available.
// pre: Frame IN [1, frames() ]

public
boolean
finalScoreAvailable(short iFrame)
{
    boolean oResult = false ;

    short f = (short) (iFrame - 1) ;

         if(iFrame < ivFrame)
    {
        oResult =
        ! ( (ivAdjustFrame[0] == f) || (ivAdjustFrame[1] == f) ) ;
    }
    else if(iFrame == FRAMES)
    {
        oResult =
        (ivThrows == 3) || ( (ivThrows == 2) && (ivScore[f] < PINS) ) ;
    }

    return oResult ;
}

For frames 1-9, the final score for the frame is available once the
frame has completed and subsequent score adjustments have been made.
The corresponding change to the recordThrow op ensures that the
ivAdjustFrame[] entries are set to 0 for any frame that has its adjustment
completed.

For frame 10, no adjustments can be made
Therefore the frame completes according to the (subject domain) rules
of tenpin bowling. The rules of interest to us are :

- The maximum number of throws for a frame has been made (in the case
  of frame 10, 3 throws) .
- Or the frame is 'open' (the first 2 throws did not hit all 10 pins) .


>>void
>>attemptMade(short iPinsHit)
>>{
>>    ivThrows++ ;
>>    recordThrow(ivFrame,iPinsHit) ;

>>    if(ivFrame < FRAMES)
>>    {
>>             if(ivScore[ivFrame - 1] == PINS)
>>        {
>>           adjustScores(ivFrame, 3 - ivThrows) ;
>>           newFrame() ;
>>        }
>>        else if(ivThrows == 2) { newFrame() ; }
>>    }

>     if( ivFrame <= FRAMES && finalScoreAvailable(ivFrame) )
>        totalScore( total() );

A TPScorer component user can do something like the following to
retrieve the final (completed) score :

TPScorer scorer = new TPScorer() ;

// Input some throw data etc.

if(scorer.finalScoreAvailable(FRAMES) )
{
    print( "Total score = " + scorer.total() ) ;
}


Regards,
Steven Perryman


0
S
12/27/2003 2:14:42 PM
"S Perryman" <q@q.com> wrote in message news:<bsk455$dnb1g$1@ID-75294.news.uni-berlin.de>...
-snip-
> A TPScorer component user can do something like the following to
> retrieve the final (completed) score :
> 
> TPScorer scorer = new TPScorer() ;
> 
> // Input some throw data etc.
> 
> if(scorer.finalScoreAvailable(FRAMES) )
> {
>     print( "Total score = " + scorer.total() ) ;
> }

Thanks. 
Seems like a "TPScorer user" would still need to keep track of which
frames had been scored - finalScoreAvailable(5) will remain true on
every subsequent roll.
0
igouy (1009)
12/29/2003 7:13:26 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0312291113.65b9e6d@posting.google.com>...
 
> Seems like a "TPScorer user" would still need to keep track of which
> frames had been scored - finalScoreAvailable(5) will remain true on
> every subsequent roll.

Ok. As the component customer, you appear to be unhappy with the
usability aspects of TPScorer. So we will change to be more amenable.
Below is a usage example, and the new TPScorer component.


The new component increased in (normalised) LOC by 8 lines.
There are also some new Design By Contract artifacts that assist the
customer in using the component in various ways.


Regards,
Steven Perryman

-------------------------------------------

void displayScores(TPScorer iScorer, short iFrames)
{
    short total = 0 ;

    for(short f = 0 ; f != iFrames ; f++ )
    {
        short frameScore += iScorer.score(f+1) ;
        total += frameScore ;
        println( "Frame " + (f+1) + " : score = " + frameScore) ;
    }

    println( "Total score = " + total) ;
}


TPScorer scorer = new TPScorer() ;
short availableFrames = 0 ;

while( !scorer.completed() )
{
    scorer.attemptMade( /* input data */ ) ;
    if(scorer.scoresAvailableFor() > availableFrames)
    {
        availableFrames++ ;
        displayScores(scorer,availableFrames) ;
    }
}

displayScores(scorer,FRAMES) ;

------------------------------------

public
class TPScorer
{

// Constructors/Destructors

public
TPScorer() {}


// pre: PinsHit IN [0,PINS]

public
void
attemptMade(short iPinsHit)
{
    ivThrows++ ;
    recordThrow(ivFrame,iPinsHit) ;

    if(ivFrame < FRAMES)
    {
             if(ivScore[ivFrame - 1] == PINS)
        {
           adjustScores(ivFrame, 3 - ivThrows) ;
           newFrame() ;
        }
        else if(ivThrows == 2) { newFrame() ; }
    }
}

private
void
newFrame()
{
    ivFrame++ ;
    ivThrows = 0 ;
}


public
short
frames() { return ( (short) (ivFrame + 1) ; }


// post: RESULT IN [0, frames() * 3 * PINS]

public
short
total()
{
    short oTotal = 0 ;

    for( short iter = 0 ; iter != ivFrame ; iter++ )
    {
        oTotal += ivScore[iter] ;
    }

    return oTotal ;
}


// *2*
// post : RESULT IMPLIES (frames() = FRAMES)

public
boolean
completed()
{
    return
    ( (ivFrame == FRAMES) &&
      ( (ivThrows == 3) || ( (ivThrows == 2) && (ivScore[9] < PINS) ) ) ;
}


// *2*
// Determine which frames (if any) currently have their final scores
// computed.

// post: RESULT IN [0, frames() ]
// post: (RESULT = 0) IMPLIES (frames() < 4)
// post: (frames() > 3) IMPLIES (RESULT >= frames() - 3)

public
short
scoresAvailableFor()
{
    boolean oResult = ivFrame ;

    if( !completed() )
    {
        short incomplete = ivAdjustThrows[0] + ivAdjustThrows[1] ;

        if( ! ( (ivAdjustThrows[0] == this.MaxAdjust) || 
                (ivAdjustThrows[1] == this.MaxAdjust) ) )
        {
            incomplete++ ;
        }

        oResult -= incomplete ;
    }

    return oResult ;
}


// pre: Frame IN [1, frames() ]
// post: RESULT IN [0, 3 * PINS]

public
short
score(short iFrame) { return ivScore[iFrame - 1] ; }


// Indicate that the score for the given frame will be adjusted by the
// scores made by the given successive throws.

private
void
adjustScores(short iFrame,
             short iThrows)
{
    short iter = 0 ;

    if(ivAdjustThrows[iter] > 0) { iter++ ; }

    ivAdjustFrame[iter] = (short) (iFrame - 1) ;
    ivAdjustThrows[iter] = iThrows ;
}

private
void
recordThrow(short iFrame,
            short iPinsHit)
{
    // Update the score of the given frame
    ivScore[iFrame - 1] += iPinsHit ;

    // Adjust the scores for previous frames (where necessary)
    for( short iter = 0 ; iter != this.MaxAdjust ; iter++ )
    {
        if(ivAdjustThrows[iter] > 0)
        {
            ivScore[ivAdjustFrame[iter] ] += iPinsHit ;
            ivAdjustThrows[iter]-- ;
            ivAdjustFrame[iter] *= ivAdjustThrows[iter] ;
        }
    }
}


// Constants

// *2*
// The maximum number of frames/throws that can be involved in a scoring
// adjustment.
private
static final
short MaxAdjust = 2 ;


// Instance variables

private
short ivFrame = 1 ;

private
short ivThrows = 0 ;

private
short[] ivScore = new short[FRAMES] ;

private
short[] ivAdjustFrame = new short[this.MaxAdjust] ;

private
short[] ivAdjustThrows = new short[this.MaxAdjust] ;


}
0
ggroups5 (201)
1/2/2004 11:21:00 AM
Let each Frame know where it is in the overall game:

let int MaxFrames = 10;
let int MaxPins = 10;

class ScoreKeeper {
   ArrayList<Frame> unscoredFrames = new ArrayList(3);
   int total = 0;
   int frames = 0;
   boolean noteEachTotal;
   

   addScore(int pinsKnockedDown){
      if (this.isNewFrame)
         unscoredFrames.add( new Frame(index: ++frames) );

      for (each : unscoredFrames)
         each.addRoll(pinsKnockedDown);

      unscoredFrames.remove(
         Frame each => { return this.hasScored(each); }
      );
   }


   boolean isNewFrame(){
      let n = unscoredFrames.size;

      return 
         n == 0 || 
         unscoredFrames[n-1].isComplete;
   }


   boolean hasScored(Frame aFrame){
      let scoreable = aFrame.isScoreable;
      if (scoreable){
         total += aFrame.score;               
               
         if (noteEachTotal||aFrame.index==MaxFrames) 
            totalScore(total); 
      }
      return scoreable;
   }


   void addScore(int pinsKnockedDown)
      requires
         pinsKnockedDown >= 0 && pinsKnockedDown <= MaxPins 
            : "pinsKnockedDown == " pinsKnockedDown.toString,

         this.frames <= MaxFrames  
            : "frames == " this.frames.toString;
}
  
      
class Frame {
   ArrayList<int> rolls = new ArrayList(3);
   int scoreRolls = 0;
   int bonusRolls = 0;
   int index;

   void addRoll(int aRoll){
      rolls.add(aRoll);

      if (this.isStrike){
         scoreRolls = 1;
         bonusRolls = 2;
      }

      if (this.isSpare){
         scoreRolls = 2;
         bonusRolls = 1;
      }

      if (this.isOpen){
         scoreRolls = 2;
         bonusRolls = 0;
      }
   }

   boolean isComplete() = 
      (index != MaxFrames && rolls.size == scoreRolls) || 
       (index == MaxFrames && rolls.size == scoreRolls + bonusRolls);

   boolean isStrike() = rolls[0] == MaxPins;

   boolean isSpare() = rolls.size>=2 && 
      (!this.isStrike && rolls[0] + rolls[1] == MaxPins);

   boolean isOpen() = rolls.size==2 && rolls[0] + rolls[1] < MaxPins;

   boolean isScoreable() = 
      rolls.size>0 && (rolls.size == scoreRolls + bonusRolls);

   int score() = rolls.foldLeft(
      (int sum, int each) => { return each + sum; }, 0);
}
0
igouy (1009)
1/2/2004 5:38:11 PM
Isaac Gouy:
> Paul Sinnett:
>> Isaac Gouy:
>>> Paul Sinnett:
>>>> Now we can call scoreFrame before adding a pending frame to the
>>>> list as long as we change the order that fillInScores and 
>>>> fillInFrame are called:

>>> Think there might be some mistake here - works fine with the
>>> original order, first fillInFrame then fillInScores.

>> I think the total score will be correct, but some frames will get
>> scored in the wrong order. Eg, a strike pending bonus scores in
>> the frames array will get scored after an open frame that follows
>> it unless we reverse the call order.

> Back to testing? ;-)

Wise choice. But it was as a result of testing that I made this
assertion. If we're getting different results I think our code samples
must have gotten out of sync. Specifically, your test:

>          [10, 5,3],

returns, correctly:

> 18 26

when you call fillInFrame first? In fact the results you posted when
calling fillInScores first fails to score the second frame at all in
this case. That is, it returns:

> 18

On the other hand, my code returns correctly:

18 26

if I call fillInScores first, but incorrectly:

8 26

if I call fillInFrame first, hence my assertion above. I traced this
and it happens because the call to scoreFrame inside fillInFrame
scores the second frame before the call to scoreFrame inside
fillInScores gets a chance to score the first. Perhaps you have made
other changes, that I have not, to account for this?
0
1/4/2004 1:36:51 AM
paul.sinnett@btinternet.com (Paul Sinnett) wrote in message news:<aa332c1d.0401031736.64453977@posting.google.com>...

> Specifically, your test:
> >          [10, 5,3],
> returns, correctly:
> > 18 26
> when you call fillInFrame first? 

That's correct. Maybe I mis-applied the changes you suggested, and
happened on a correct solution ;-)

> Perhaps you have made other changes, that I have not, to account for this?

   Not that I'm aware of. Maybe you can find a difference to this: 

http://groups.google.com/groups?q=g:thl2242977065d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0312211701.42c1b362%40posting.google.com

   meanwhile, pendingFrame was removed:
http://groups.google.com/groups?q=g:thl2622503333d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0312230012.15157d33%40posting.google.com

   and 'Frame' given an index id
http://groups.google.com/groups?q=g:thl2242977065d&dq=&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=ce7ef1c8.0401020938.5720281e%40posting.google.com&rnum=175

best wishes, Isaac
0
igouy (1009)
1/4/2004 6:28:40 PM
ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0401020321.5db0f91c@posting.google.com>...

Again, thank you.

> public
> short
> scoresAvailableFor()
> {
>     boolean oResult = ivFrame ;

There seems to be a typo? int oResult = ivFrame ;


> Ok. As the component customer, you appear to be unhappy with the
> usability aspects of TPScorer. So we will change to be more amenable.


The component customer is still unhappy :-(

> TPScorer scorer = new TPScorer() ;
> short availableFrames = 0 ;
> 
> while( !scorer.completed() )
> {
>     scorer.attemptMade( /* input data */ ) ;
>     if(scorer.scoresAvailableFor() > availableFrames)
>     {
>         availableFrames++ ;
>         displayScores(scorer,availableFrames) ;
>     }
> }

The customer want's the component to keep track of 'availableFrames'
and only have responsibility for passing the input data to TPScorer.

   TPScorer scorer = new TPScorer() ;
   for each pinscore in a game { 
      scorer.attemptMade( a pinscore ); }

and have TPScorer take responsibility for calling 'totalScore' as soon
as it's able to calculate a new frame score.

   void totalScore(int score){
      print(score); print(" ");
   }

best wishes, Isaac
0
igouy (1009)
1/8/2004 10:55:46 PM
igouy@yahoo.com (Isaac Gouy) wrote in message news:<ce7ef1c8.0401081455.47181a8c@posting.google.com>...

>ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0401020321.5db0f91c@posting.google.com>...

>>Again, thank you.

>> public
>> short
>> scoresAvailableFor()
>> {
>>     boolean oResult = ivFrame ;

>There seems to be a typo? int oResult = ivFrame ;

Thanks.


>The component customer is still unhappy :-(

> [...]

>The customer want's the component to keep track of 'availableFrames'
>and only have responsibility for passing the input data to TPScorer.

> TPScorer scorer = new TPScorer() ;
> for each pinscore in a game {
>    scorer.attemptMade( a pinscore ); }

>and have TPScorer take responsibility for calling 'totalScore' as soon
>as it's able to calculate a new frame score.

>void totalScore(int score){ print(score); print(" "); }

Ok. See below (changes marked as "***" ) .


Regards,
Steven Perryman

----------------------------

public
interface Attempt
{
    // pre: PinsHit IN [0,PINS]

    void
    attemptMade(short iPinsHit)
}

----------------------------

public
class IsaacsScorer
implements
    Attempt
{

public
IsaacsScorer() {}

public
void
attemptMade(short iPinsHit)
{
    ivScorer.attemptMade(iPinsHit) ;
    
    short frames = ivScorer.scoresAvailableFor() ;

    if(ivScorer.completed() || (ivFrames < frames) )
    {
        ivFrames = frames ;
        totalScore(ivScorer.total(ivFrames) ) ;
    }
}


// Instance variables

private
TPScorer ivScorer = new TPScorer() ;

private
short ivFrames = 0 ;

}

---------------------

// ***
public
class TPScorer
implements
    Attempt
{

// Constructors/Destructors

public
TPScorer() {}


// pre: PinsHit IN [0,PINS]

public
void
attemptMade(short iPinsHit)
{
    ivThrows++ ;
    recordThrow(ivFrame,iPinsHit) ;

    if(ivFrame < FRAMES)
    {
             if(ivScore[ivFrame - 1] == PINS)
        {
           adjustScores(ivFrame, 3 - ivThrows) ;
           newFrame() ;
        }
        else if(ivThrows == 2) { newFrame() ; }
    }
}

private
void
newFrame()
{
    ivFrame++ ;
    ivThrows = 0 ;
}


public
short
frames() { return ( (short) (ivFrame + 1) ; }


// pre: Frames IN [1, frames() ]
// post: RESULT IN [0, frames() * 3 * PINS]

// ***
public
short
total(short iFrames)
{
    short oTotal = 0 ;

    for( short iter = 0 ; iter != iFrames ; iter++ )
    {
        oTotal += ivScore[iter] ;
    }

    return oTotal ;
}


public
boolean
completed()
{
    return
    ( (ivFrame == FRAMES) &&
      ( (ivThrows == 3) || ( (ivThrows == 2) && (ivScore[9] < PINS) ) ) ;
}


// Determine which frames (if any) currently have their final scores
// computed.

// post: RESULT IN [0, frames() ]
// post: (RESULT = 0) IMPLIES (frames() < 4)
// post: (frames() > 3) IMPLIES (RESULT >= frames() - 3)

public
short
scoresAvailableFor()
{
    short oResult = ivFrame ;

    if( !completed() )
    {
        short incomplete = ivAdjustThrows[0] + ivAdjustThrows[1] ;

        if( ! ( (ivAdjustThrows[0] == this.MaxAdjust) || 
                (ivAdjustThrows[1] == this.MaxAdjust) ) )
        {
            incomplete++ ;
        }

        oResult -= incomplete ;
    }

    return oResult ;
}


// pre: Frame IN [1, frames() ]
// post: RESULT IN [0, 3 * PINS]

public
short
score(short iFrame) { return ivScore[iFrame - 1] ; }


// Indicate that the score for the given frame will be adjusted by the
// scores made by the given successive throws.

private
void
adjustScores(short iFrame,
             short iThrows)
{
    short iter = 0 ;

    if(ivAdjustThrows[iter] > 0) { iter++ ; }

    ivAdjustFrame[iter] = (short) (iFrame - 1) ;
    ivAdjustThrows[iter] = iThrows ;
}

private
void
recordThrow(short iFrame,
            short iPinsHit)
{
    // Update the score of the given frame
    ivScore[iFrame - 1] += iPinsHit ;

    // Adjust the scores for previous frames (where necessary)
    for( short iter = 0 ; iter != this.MaxAdjust ; iter++ )
    {
        if(ivAdjustThrows[iter] > 0)
        {
            ivScore[ivAdjustFrame[iter] ] += iPinsHit ;
            ivAdjustThrows[iter]-- ;
            ivAdjustFrame[iter] *= ivAdjustThrows[iter] ;
        }
    }
}


// Constants

// The maximum number of frames/throws that can be involved in a scoring
// adjustment.
private
static final
short MaxAdjust = 2 ;


// Instance variables

private
short ivFrame = 1 ;

private
short ivThrows = 0 ;

private
short[] ivScore = new short[FRAMES] ;

private
short[] ivAdjustFrame = new short[this.MaxAdjust] ;

private
short[] ivAdjustThrows = new short[this.MaxAdjust] ;


}
0
ggroups5 (201)
1/12/2004 11:19:38 AM
ggroups@bigfoot.com (S Perryman) wrote in message news:<745b753c.0401120319.6e0acebf@posting.google.com>...

> Ok. See below (changes marked as "***" ) .

The implementation doesn't seem to give the expected results, in a couple of cases:

Sequences beginning 10,5,3...
   [10, 5,3, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0],
   [10, 5,3],
   [10, 5,3, 0]
seem to skip the first score 18, and go straight to 26

   [5,5, 3,2, 7,3, 10,5, 5,10, 3,4, 5,4, 3,5, 5,5, 5]
gives the scores 
   13 18 38 58 78 102 111 119 134
missing out 95
   13 18 38 58 78 95 102 111 119 134

best wishes, Isaac
0
igouy (1009)
1/20/2004 3:56:27 PM
Reply:

Similar Artilces:

Test-Driven Development Sample
Adventures in C#: The Bowling Game Ron Jeffries 11/17/2003 When I demonstrate Test-Driven Development using the Bowling Game example, I begin by describing the problem and inviting the attendees to do a little up front design about what objcts we may need. Then I take a very simple approach that produces a rather simple single-class solution, with none of the complexity we anticipated. I've been wondering how to drive the development to cause the creation of some of the classes that are anticipated, supposing that we might have some actual need for them. Here's an ex...

Test-Driven Development Sample
Adventures in C#: The Bowling Game Ron Jeffries 11/17/2003 When I demonstrate Test-Driven Development using the Bowling Game example, I begin by describing the problem and inviting the attendees to do a little up front design about what objcts we may need. Then I take a very simple approach that produces a rather simple single-class solution, with none of the complexity we anticipated. I've been wondering how to drive the development to cause the creation of some of the classes that are anticipated, supposing that we might have some actual need for them. Here's an ex...

Test-Driven Development Sample
Adventures in C#: The Bowling Game Ron Jeffries 11/17/2003 When I demonstrate Test-Driven Development using the Bowling Game example, I begin by describing the problem and inviting the attendees to do a little up front design about what objcts we may need. Then I take a very simple approach that produces a rather simple single-class solution, with none of the complexity we anticipated. I've been wondering how to drive the development to cause the creation of some of the classes that are anticipated, supposing that we might have some actual need for them. Here's an ex...

TDD: Test-Driven Design or Test-Driven Development?
I have a feeling that for some people, TDD stands for test-driven design. In other words, one doesn't need to think about a problem ahead of time. Instead, one starts by writing just one test. This test produces a bit of code. Then, the next test is followed by another piece of code. Eventually the correct implementation emerges organically from this process. Up to this point, I think this approach is perhaps a bit naive. I would argue for test-driven development: One can still think about a problem, doodle, do research, have a discussion with customers, write a mathematical proof for t...

Test Driven Development Sample #2
Ron Jeffries <ronjeffries@REMOVEacm.org> wrote in message news:<l11irvgd06vp6aqups002oobumiun3u3u7@4ax.com>... > Adventures in C#: The Bowling Game > Ron Jeffries > 11/17/2003 I noticed there was a start at incremental scoring TDD version of the Bowling Game, but that seems to have come to naught. http://www.xprogramming.com/xpmag/acsBowlingProceduralFrameScore.htm Is it easier to re-design from scratch ;-) ...

Test-Driven Development for Embedded Software with C sample project
The Methods & Tools newsletter has released in its html archive section the article "Mocking the Embedded World: Test-Driven Development, Continuous Integration, and Design Patterns". Despite a prevalent industry perception to the contrary, the agile practices of Test-Driven Development and Continuous Integration can be successfully applied to embedded software. We present here a holistic set of practices, platform independent tools, and a new design pattern (Model Conductor Hardware - MCH) that together produce: good design from tests programmed first, logic decoupled from hardw...

Advice on Automated Unit Testing and Test Driven Development
Hi there, I am a consultant working for a software company trying to provide a recommendation on the future of Automated Unit Testing and Test Driven Development for J2EE/.Net/Web Services Development. I would appreciate any advice, data, information, people have on whether these practices work and what tools appear to be useful. - How do people measure the ROI of AUT or TDD? What are the hard and soft benefits? - What types of software companies get most benefit from AUT or TDD? I.e. mission critical? Short lifecycle? Long lifecycle? Highly volatile code? Etc? - Will AUT or TDD r...

Advice on Automated Unit Testing and Test Driven Development
Hi there, I am a consultant working for a software company trying to provide a recommendation on the future of Automated Unit Testing and Test Driven Development for J2EE/.Net/Web Services Development. I would appreciate any advice, data, information, people have on whether these practices work and what tools appear to be useful. - How do people measure the ROI of AUT or TDD? What are the hard and soft benefits? - What types of software companies get most benefit from AUT or TDD? I.e. mission critical? Short lifecycle? Long lifecycle? Highly volatile code? Etc? - Will AUT or TDD r...

Oracle test driven development and automated testing with FitNesse
Hi I wrote an extension for FitNesse, a popular test-automation and collaboration tool, which allows Oracle developers to write, manage and automate tests for database code. The first version supports testing stored procedures and functions, executing queries and statements on Oracle. Source code, examples and binaries can be downloaded from http://gojko.net/fitnesse/dbfit - and there is a PDF guide which explains how to quickly dive into Test driven development with DbFit/ FitNesse and Oracle on http://gojko.net/FitNesse/dbfit-oracle.pdf I would really appreciate your comments...

Test-driven Development
There are soime great free tutorials on Test-driven Development at http://www.objectmonkey.com, plus heaps of other free papers and resources. ...

Test-driven development
I've recently delved into the Ruby programming language, which has a thrivi= ng "ecology" going. One of the "traditions" there is test-driven development: http://en.wikipedia.org/wiki/Test-driven_development which was one aspect of Kent Beck's eXtreme Programming proposals. The Ruby community has a slogan "red/green/refactor" which is emblematic of= the theme of writing test cases first, before writing code that achieves t= hem. They fail (red), and then succeed (green), and subsequently you clean= up the code (refactor) with the previously passed t...

Test-driven development
I am interested in opinions on test-driven development, for an article I am writing. Do you use it (eg JUnit)? If so, what benefits have you seen? Are there any downsides, and if so what are they? Any comments, please post here or email me tim(at)itwriting.com. Many thanks, Tim Kent Beck slams troglodyte developers http://www.itwriting.com/blog/?postid=170 Hi, I have used both JUnit and NUnit and fornd them very helpful... They are good for unit testing the modules and u can check that nothing has broken by the new changes introduced in your code . Infact, I would like to add...

Test Driven Development
I've been soaking up Uncle Bob's book on Agile processes and am really enamored with TDD. However, managers and customers like estimations on when things are to be completed. Anybody have any references on using TDD for project estimation? Thanks, Brian Zix wrote: > I've been soaking up Uncle Bob's book on Agile processes and am really > enamored with TDD. However, managers and customers like estimations > on when things are to be completed. Anybody have any references on > using TDD for project estimation? The book /Test Driven Development/ might cover that...

Test driven development
Hi Sorry if this is a bit off topic but as unit testing is such a cornerstone of python development I thought a few of you may be able to share your knowledge/experiences. I like the concept of TDD but find it difficult to put into practice most of the time. I think this primarily because I tend to like top- down development and functional/object decomposition and TDD feels more like a bottom-up approach. So my question is when approaching a project that you want to employ test driven development on how and where do you start? And also if anyone uses top-down design with TDD I would be inte...

Web resources about - Test Driven Development Sample - comp.object

Agile Software Development - Better ways of developing software
- All things cross-platform, mobile, and mobile-web. Especially Qt/QML related - I love developing software, but also practice managing projects ...

United States Department of Housing and Urban Development - Wikipedia, the free encyclopedia
Robert C. Weaver Federal Building , 451 7th Street SW , Washington, D.C. 38°53′2.17″N 77°1′21.03″W  /  38.8839361°N 77.0225083°W  / 38.8839361; ...

Traditional toys beat gadgets in language development
Baby laptops and other talking, flashing toys that promise to improve&nbsp;children's language skills may do the exact opposite.

Zika virus: Indian biotech company claims to have vaccine in development, help needed for faster roll-out ...
An Indian biotech company claims to have developed a vaccine for the Zika virus.

Successful Software Development Project Failures
Software development projects have always been challenging. The most referenced source for metrics about project success is the Standish Group ...

Law Schools Are Not Exposing Students To Real World Business Development
If you're a law student, ask yourself if your law school is truly preparing you for the future.

Toyota buys Daihatsu for small-car development
Filed under: Earnings/Financials , Toyota , Daihatsu , Budget Having acquired a controlling interest in Daihatsu back in 1988, Toyota is now ...

Facebook irks devs by shutting Parse mobile development platform
Facebook is closing down Parse, its mobile development platform, just three years after acquiring it for $85 million. The shutdown comes as something ...

Talpa & Zhejiang Tangde Forge Joint Production Venture; Avanti & Sky Vision Ink 2-Year Development Deal ...
Expanding their relationship, John de Mol’s Talpa, now owned by ITV, has entered a joint venture with Middle Kingdom media company Zhejiang Tangde ...

Thanks To Google Fiber, This Is America's Best-Connected Public Housing Development
100 low-income households in Kansas City now have the best Internet access money can buy—for free. Starting today, every family in a Kansas ...

Resources last updated: 2/9/2016 6:15:10 PM