Reputation: 73
"Bulgarian Solitaire" is a mathematical curiosity. It is played with a deck of 45 (any triangular number will work) unmarked cards. Put them into randomly sized piles. Then, to play a round, remove a card from each pile and create a new pile with the removed cards. Repeating this step eventually yields the configuration 1 2 3 4 5 6 7 8 9
(for 45 cards), which is clearly a fixed point of the game and thus the end of the solitaire. I wanted to simulate this game in J.
After a couple of days thinking about it and some long-awaited insight into J gerunds, I came up with a solution, on which I would like some opinions. It starts with this verb:
bsol =: ((#~ ~:&0) , #)@:(-&1)^:(<_)
Given a vector of positive integers whose sum is triangular, this verb returns a rank 2 array showing the rounds of the solitaire that results. I also came up with this verb to generate an initial configuration, but I'm less happy with it:
t =: 45 & - @ (+/) NB. Would work with any triangular number
cards =: (]`(]@,>:@?&t@]))@.(0&<@t)^:_
Given a vector y
of positive integers, t
returns the defect from 45, i.e., the number 45 - +/ y
of cards not accounted for in the piles represented by the argument. Using t
, the verb cards
appends to such a vector y
an integer from >: i. t y
repeatedly until the defect is 0.
Expanding t
explicitly, I get
cards =: (]`(]@,>:@?&(45 & - @ (+/))@]))@.(0&<@(45 & - @ (+/)))^:_
I feel like this is not very brief, and maybe overly parenthesized. But it does work, and the complete solution now looks like this:
bsol @ cards @ >: @ ? 44 NB. Choose the first pile randomly from >: i. 44
Without the named verbs:
(((#~ ~:&0) , #)@:(-&1)^:(<_)) @: ((]`(]@,>:@?&(45 & - @ (+/))@]))@.(0&<@(45 & - @ (+/)))^:_)@>:@? 44
Not knowing much about J idiom, I have the same feeling about this: it's not very brief, certainly redundant (would it be better to use a local verb like t
here, since it's repeated, e.g.?), and probably overly parenthesized. What opportunities do I have to improve this program?
Upvotes: 3
Views: 286
Reputation: 9153
You can improve t
with
t =: 45 - +/
Using 46 - +/
will spare you some >:
.
You can replace cards
with a recursive definition:
cards =: }.`(($:@] , -) ?)@.(0&<)
where, now, cards n
produces an initial configuration with sum n
.
In bsol
you don't need -&1
if you remove (-.
) the zeroes and rearrange it like:
bsol =: (0 -.~ [: (, #) <:)^:(<_)
Upvotes: 3