Ok, so it looks like Roger's moo guesses
the number sequence you have chosen, and
you are responsible for (a) declaring the range
of possibilities, and (b) telling it how many "bulls"
and "cows" it has gotten with its guesses.

Here is an example session where I am thinking
of the sequence 1 2 3.

   moo 5 5 5
3 3 0   c
30 choices left
4 1 3   bc
4 choices left
2 4 3   bc
one choice left; it must be 1 2 3
1 2 3

Here's a simpler session, which I will use to illustrate
the code.  I am thinking of the sequence 0 1:
   moo 2 2
0 0   b
2 choices left
1 0   cc
one choice left; it must be 0 1
0 1

I supplied the initial command line (moo 5 5 5), and
I responded with the sequences of 'b's and 'c's.  The
numbers to the left of those sequences were its guesses.

Anyways, univ 5 5 5 computes all the possible things
I could be thinking of.  The program will be eliminating
entries from this list based on my responses.

   univ 2 2
0 0
0 1
1 0
1 1

These are equivalent:
   (#: i.@(*/))2 2
   2 2 (#: i.@(*/)) 2 2
   2 2 #: i. */ 2 2
   2 2 #: i. 4
   2 2 #: 0 1 2 3

I will use this last line to represent the various universe
states as this routine executes.

moo1 is the heart of this program.  It takes as its argument some
universe of choices, and if there are multiple possibilities it picks
one at random and prompts the user with that choice and eliminates
choices from the universe which conflict with that answer, and
displays a line describing this result universe.

Since moo1^:_ repeats this process until the result stops
changing, this process repeats until there are less than
two choices.

Here's the definition of moo1:
   ([state)@([EMAIL PROTECTED] prune ])^:(1&<@#)

That ^:(1&<@#) means that moo1 only does something
when there's more than one choice.

The routine guess just picks something at random.  The
definition of guess is [EMAIL PROTECTED] { ] and for the first run through,
this is equivalent to 0 { ]

The definition of score is
   ; prompt@(,&'   ')@":

This prompts the user with its argument and returns
two boxes.  The left box contains its argument and
the right box contains the user's response.  So if
I run
   score 2 2 #:0
I am prompted with
0 0

And if I respond with b
the result of score would be
   0 0;,'b'
+---+-+
|0 0|b|
+---+-+

Next, prune takes this (as its left argument) and the
current universe of valid results (as its right argument)
and returns the refined universe of valid results
   (0 0;,'b') prune 2 2#:0 1 2 3
0 1
1 0

Finally, the verb state describes this new universe, based
on how many entries it contains (0, 1 or many), and its
use in the hook ([state) means that the result of state is
ignored and its argument is returned as its final result.

   state (0 0;,'b') prune 2 2#:0 1 2 3
2 choices left
2 choices left
   ([state)(0 0;,'b') prune 2 2#:0 1 2 3
2 choices left
0 1
1 0

That first line after each line is displayed, and subsequent
lines are the corresponding result.

The definition of prune is
   ((bull *. bc) # ])`(''"_)@.solved

If the users' answer indicates that the puzzle has been
solved, prune returns a blank.  Otherwise, it eliminates
impossible answers from the universe.

The definition of solved is
   (('b' $~ [EMAIL PROTECTED]) -: ])&>/@[

If the user has provided a list of 'b's the same length as
the length of the guess then the problem is solved,
otherwise it's not.

   (0 1;'bb') solved _
1
   (0 1;'bc') solved _
0

These are equivalent:
   (0 1;'bc') solved _
   (('b' $~ [EMAIL PROTECTED]) -: ])&>/ 0 1;'bc'
   (('b' $~ [EMAIL PROTECTED]) -: ])&>/ ((<0 1),<'bc')
   (<0 1) (('b' $~ [EMAIL PROTECTED]) -: ])&> <'bc'
   0 1 (('b' $~ [EMAIL PROTECTED]) -: ]) 'bc'
   ('b' $~ $0 1) -: 'bc'
   ('b' $~ 2) -: 'bc'
   (2 $ 'b') -: 'bc'
   'bb' -: 'bc'
0

The rest of the definition of prune selects lines
from the universe that match the "bull" criterion and
the "bc" criterion.  In the first step of my simple
example, these are the same:
   (0 0;,'b') bull 2 2#:0 1 2 3
0 1 1 0
   (0 0;,'b') bc 2 2#:0 1 2 3
0 1 1 0

The "bull" criterion only concerns itself with the
appearance of the letter 'b' in my typed answer.

The "bc" criterion only concerns itself with the
number of letters in my typed answer.

Here's the definition of "bull"
   +/@('b'&=)@(>@{:@[) = >@[EMAIL PROTECTED] +/@(="1) ]
This has the structure
   verb1 = verb2 +/@(="1) ]

The result of verb1 is the number of 'b's I typed.
The result of verb2 is the current guess.
The result of ] is the previous universe of valid choices.

   (0 0;,'b') +/@('b'&=)@(>@{:@[) 2 2 #:0 1 2 3
1
   (0 0;,'b') >@[EMAIL PROTECTED] 2 2 #:0 1 2 3
0 0

These are equivalent
   (0 0;,'b') bull 2 2 #: 0 1 2 3
   1 = 0 0 +/@(="1)  2 2 #:0 1 2 3
   1 = 2 1 1 0
0 1 1 0

Hopefully, it's clear that +/@(="1) counts
how many exact matches there are between
the current guess and each item in the universe
of valid choices?

Here's the definition of bc
   #@(>@{:@[) = ] ([ +/@:([ <. -~) [EMAIL PROTECTED] {. ])&(#/.~)"1 ] ,"1 
>@[EMAIL PROTECTED]

This one is a bit more complicated, since it needs to ignore
the order of the guess and valid choices.  The top level structure
of bc is
   verb1 = ] verbA ] ,"1 verb2

The result of verb1 is the number of characters I typed.
The result of verb2 is the current guess.
The result of ] is the previous universe of valid choices.

The result of verbA is the number of any position matches
between the universe of valid choices and the current guess.
By verbA, I mean:  ([ +/@:([ <. -~) [EMAIL PROTECTED] {. ])&(#/.~)"1

The result of (] ,"1 verb2) has one row for each previous
valid choice.  These rows consist of that choice followed
by the guess.  In other words, these are equivalent:
   (0 0;,'b') (] ,"1 >@[EMAIL PROTECTED]) 2 2 #:0 1 2 3
   (2 2 #:0 1 2 3),"1(0 0)
0 0 0 0
0 1 0 0
1 0 0 0
1 1 0 0

These rows will be the right argument for verbA.  The left
argument will be the previous universe of valid choices.

Anyways, verbA has the structure f&g"1 where
f is ([ +/@:([ <. -~) [EMAIL PROTECTED] {. ]) and g is #/.~

We can look at each of these pairs of arguments to f
by replacing f with ;
   (2 2 #:0 1 2 3) ;&(#/.~)"1 (2 2 #:0 1 2 3),"1(0 0)
+---+---+
|2  |4  |
+---+---+
|1 1|3 1|
+---+---+
|1 1|1 3|
+---+---+
|2  |2 2|
+---+---+

In essence, g (or #/.~) has broken items in our arguments
down into groups and has counted the number of members
in each.  Since all groups have the same prefix, we can
meaningfully compare entries on the right with entries on
the left (after we have discarded any extra entries on the
right).  In other words, these entries will be in the same
order.

The definition of f was
   [ +/@:([ <. -~) [EMAIL PROTECTED] {. ]

This has the structure [ h [EMAIL PROTECTED] {. ]

And the verb ([EMAIL PROTECTED] {. ]) says "take only the elements from
the right argument which have corresponding elements in
the left argument".  In other words: we are discarding any
surplus.

Thus, the core of f is +/@:([ <. -~)

In other words: the right argument to f includes the
counts on the left, so we first need to subtract those.
Once we have done so, for each element we choose
the number on the left side of f or this difference -- that's
how many times the guessed value can relevantly
appear in the choice.  In other words, these are equivalent
   1 1 +/@:([ <. -~) 1 3
   +/ 1 1 ([ <. -~) 1 3
   +/ 1 1 <. 1 1 -~ 1 3
   +/ 1 1 <. 1 3 - 1 1
   +/ 1 1 <. 0 2
   +/ 0 1
1

The guess was 0 0 and the choice was 1 0,
and the row with both was 1 0 0 0.  In this
choice we have two values, each of which
appears once (thus: 1 1).  In the row with both,
we have a 1 and we have three 0s (thus: 1 3).
When we subtract out the choice this means that
our guess had 0 of that first value, and 2 of that
second value (thus: 0 2).  But since the choice
only had one of each, that means that when we
are counting bulls and cows we would expect me
to have typed one character if that guess matched
that choice.  (And, as an aside, that one character
would have to be a zero, but we do not need to
record that detail.)

In other words, f first trims the argument pairs
so that their lengths match:
+---+---+
|2  |4  |
+---+---+
|1 1|3 1|
+---+---+
|1 1|1 3|
+---+---+
|2  |2  |
+---+---+

And then, f subtracts the values on the left (choices)
from the values on the right (which was choices and
guess combined) to get values for just the guess

+---+---+
|2  |2  |
+---+---+
|1 1|2 0|
+---+---+
|1 1|0 2|
+---+---+
|2  |0  |
+---+---+

And, then f clips the guess counts so they do not exceed
the choice counts

+---+
|2  |
+---+
|1 0|
+---+
|0 1|
+---+
|0  |
+---+

And, finally, f adds these counts
2 1 1 0

Since my typed answer had one character,
   1 = 2 1 1 0
0 1 1 0

All of this means that for my simple universe of four
choices, with the random guess 0 0 that my answer 'b'
means that the valid remaining choices must each have
both a zero and a one.

I have skimmed over some elements of this routine and gone
over others in perhaps excessive detail.  If you feel there is some
subject I should cover in more detail, or from a different angle,
or whatever, let us know.

Thanks,

-- 
Raul
----------------------------------------------------------------------
For information about J forums see http://www.jsoftware.com/forums.htm

Reply via email to