Perl Weekly Challenge: Week 104
Challenge 1:
FUSC Sequence
Write a script to generate first 50 members of
FUSC Sequence
. Please refer to OEIS for more information._The sequence defined as below:
fusc(0) = 0
fusc(1) = 1
for n > 1:
when n is even: fusc(n) = fusc(n / 2),
when n is odd: fusc(n) = fusc((n-1)/2) + fusc((n+1)/2)
This is a situation where Rakus multimethods are very useful. We can treat each case of the sequence as a separate function which makes the code much more expressive and easy to understand.
multi sub fusc(
Int $n where { $n == 0 }
) {
return 0;
}
multi sub fusc(
Int $n where { $n == 1 }
) {
return 1;
}
multi sub fusc(
Int $n where { ($n > 1) && ($n % 2 == 0); }
) {
return fusc($n div 2);
}
multi sub fusc(
Int $n where { ($n > 1) && ($n % 2 == 1); }
) {
return fusc(($n - 1) div 2) + fusc(($n + 1) div 2);
}
sub MAIN () {
for ^50 -> $n {
print fusc($n), ' ';
}
print "\n";
}
Not that the Perl version shown below is so bad but it's a little harder to read IMO.
sub fusc {
my ($n) = @_;
if ($n < 2) {
return $n;
}
if ($n % 2 == 0) {
return fusc($n / 2);
} else {
return fusc(($n - 1) / 2) + fusc(($n + 1) / 2);
}
}
for my $n (0 .. 49) {
print fusc($n), ' ';
}
print "\n";
Other than those minor inconveniences, the Perl version follows the same pattern as the Raku version.
Challenge 2:
NIM Game
Write a script to simulate the
NIM Game
.It is played between 2 players. For the purpose of this task, let assume you play against the machine.
There are 3 simple rules to follow:
a) You have 12 tokens
b) Each player can pick 1, 2 or 3 tokens at a time
c) The player who picks the last token wins the game
With only three rules, NIM is a very easy game to program. This time I'll start with the Perl version.
my $tokens = 12;
Rule a is implemented in 1 line.
while ($tokens) {
Now we have to alternate between the player and computer until all the tokens have been picked.
This is done with a while
loop.
say "There are $tokens ", plural("token", $tokens), ".";
I start each turn by printing how many tokens are left. A pet peeve of mine is incorrect pluralization i.e. "1 tokens". So I have a little function that does the right thing which looks like this:
sub plural {
my ($word, $count) = @_;
return $word . ($count == 1 ? q{} : 's');
}
...back to the game:
my $playerChoice = 0;
while (1) {
print "How many tokens will you pick [1, 2 or 3]?";
my $answer = <>;
if ($answer =~ /\A \s* (1|2|3) \s* \z/msx) {
$playerChoice = $1;
last;
}
}
I prompt the player for how many tokens they want to pick. The only valid choices are 1, 2 or 3 so I have to validate their response to allow only those values.
$tokens -= $playerChoice;
if ($tokens < 1) {
say "You win!";
last;
}
If they made a valid move, that number of tokens are subtracted from the total. As per rule 3, If there are none left the player has won.
my $computerChoice = 4 - $playerChoice;
say "The computer picks $computerChoice ", plural("token", $computerChoice);
$tokens -= $computerChoice;
if ($tokens < 1) {
say "The computer wins.";
last;
}
Now for the computer. An interesting fact I learned when researching this game on the web is that player 2 can guarantee a win every time by picking 4 - the number of tokens picked by player 1. So my code does just that. If there are no tokens left at this point, the computer has won.
}
This is the Raku version.
sub plural(Str $word, Int $count) {
return $word ~ ($count == 1 ?? q{} !! 's');
}
sub MAIN () {
my $tokens = 12;
while ($tokens) {
say "There are $tokens ", plural("token", $tokens), ".";
my $playerChoice = 0;
loop {
my $answer = prompt("How many tokens will you pick [1, 2 or 3]?");
if $answer ~~ /^ \s* (1|2|3) \s* $/ {
$playerChoice = $0;
last;
}
}
The only features really worthy of note are the prompt()
function which takes some of the
grunt work out of displaying a message and getting a line of input. Also regexp capture variables
start from $0
not $1
as in Perl. That tripped me up for a minute.
$tokens -= $playerChoice;
if $tokens < 1 {
say "You win!";
last;
}
my $computerChoice = 4 - $playerChoice;
say "The computer picks $computerChoice ",
plural("token", $computerChoice);
$tokens -= $computerChoice;
if ($tokens < 1) {
say "The computer wins.";
last;
}
}
}