Posts in this category

Mon, 04 May 2009

Blackjack and Perl 6

Permanent link

When you play Blackjack, you need to collect cards so that the sum of their values gets as close as possible to 21, but not above.

The tricky thing is that an Ace can count both as 1 or 11, whatever suits better for you. So how do we find the best value of a dealt set of cards?

Well, somhow we have to sum over all combinations. For example if you have an ace and a three, you could represent them as 3 and [1, 11] in your program. What you want as a result is the array [4, 14].

The cross meta-operator lets us apply a + pairwise on all possible combinations of a list (shown on Rakudo's REPL):

> say (1, 11 X+ 3).perl
[4, 14]

In principle some of the values could have been greater than 21 (if you had two aces), so let's filter out only those that are at most as large as 21, and find the maximum:

> say [max] (1, 11 X+ 3).grep({$_ <= 21})

max is an operator that gives us the larger of two values, and the reduction operator [...] applies this pair wise to all list items.

Now that works, but you can have more than two cards on your hand, so we need to generalize it a bit..

In a perfect Perl 6 world we could simply write [max] ([X+] @cards).grep: {$_ <= 21};, but that requires a quite complicated thing named slice context, which Rakudo doesn't implement yet.

So we have to work around the non-working [X+] by doing the reduction manually:

> my @cards = [1, 11], 4, [1, 11];
  say [max] @cards.reduce({ @^a X+ @^b }).grep: { $_ <= 21 }

(Note: splitted on two lines for readability, but should really be on one line.)

The reduce method does (roughly) the same as the equally named sub in Perl 5's List::Util module: it calls the block with two arguments, where the first one is the previous return value from the block (or the first array item on the first call), and the second is the next list item. The difference is that in Perl 5 the arguments are stored in the special variables $a and $b (about which doesn't complain), whereas in Perl 6 they are passed as ordinary arguments to the block. The ^ twigil (secondary sigil) specifies that the value of that variable should be taken from the parameter list, in lexicographic order of all such variables in the block.

Since X+ operates on lists and not on array references, the arguments needs to be derefences. In Perl 5 you'd write that as @{...}, in Perl 6 you can simply bind to a @-sigiled variable - with the slight difference that the derefencing on a number is not an error, but simply returns a list of that number.

(you can write the same thing a bit simpler with junctions, but that doesn't demonstrate the meta operators, and is discouraged for other reasons).

You see that you can still write scary code with Perl 6, and I hope you will play around with it a bit, join us on #perl6 and have the appropriate amount of fun!

[/perl-6] Permanent link