This is my response to the Perl Weekly Challenge #52.
Write a script to accept two numbers between 100 and 999. It should then print all
Stepping Numbers between them.
A number is called a stepping number if the adjacent digits have a difference of 1. For example, 456 is a stepping number but 129 is not. 
Brute force should work, as the number of numbers (so to speak) isn't that large:
File: steppingloopsubset SteppingLimit of Int where 100 <= * <= 999; # [1]
unit sub MAIN (SteppingLimit $from, # [2]
SteppingLimit $to where $to > $from, # [3]
:$verbose); # [4]
say ": Candidates: { ($from.Int .. $to.Int).list }" if $verbose; # [5]
for $from .. $to > $number # [6]
{
say $number if is_stepping($number); # [7]
}
sub is_stepping (SteppingLimit $number) # [8]
{
my @digits = $number.comb; # [9]
my $current = @digits.shift; # [10]
while (@digits) # [11]
{
return False unless @digits[0] == any($current +1, $current 1); # [12]
$current = @digits.shift; # [13]
}
return True; # [14]
}
[1]
The legal input values are between 100 and 999, both included.
I have used a custom type (with subset
) to enforce that. Note the
way we can check both boundaries at once, as done here. The *
represents the current value in the where
check.
[2] Enforcing legal values on the from variable,
[3] and the to variable. Note the extra check to enforce that to is higher than from.
[4] Verbose mode, as I am fond of that. Note my private rule about a leading colon on the verbose output to make them stand out.
[5] Just to show that we actually get a full list.
[6] For each number,
[7] • print it if it as a stepping number.
[8] Check the given number.
[9] Split the number into a list of the three individual digits.
[10] Get the first digit.
[11] As long as there are more digits (initially there are 2).
[12] Return «False» if the next digit is not one higher or one
lower than the current one. Note the any
junction that makes it
possible to do multiple comparisons at once.
[13] The digit is legal (as [12] didn't return). Get the next digit, ready for the next iteration (in [11]).
[14] If we get here, we have checked all the digits, and the number is a stepping number.
Running it:
$ raku steppingloop 100 199
101
121
123
$ raku steppingloop 100 299
101
121
123
210
212
232
234
$ raku steppingloop 100 999
101
121
123
210
212
232
234
321
323
343
345
432
434
454
456
543
545
565
567
654
656
676
678
765
767
787
789
876
878
898
987
989
That looks ok. It would probably look nicer if we printed the values on a single line. So I'll do that. In the next program.
Brute force works out quite nicely as the numbers (the difference between the lower and upper
limits) are small. But it doesn't scale well. We can fix that by only generating the
actual stepping numbers. And gather
/take
is a good choice here.
subset SteppingLimit of Int where 100 <= * <= 999;
unit sub MAIN (SteppingLimit $from,
SteppingLimit $to where $to > $from,
:$verbose);
say ": Candidates: { ($from.Int .. $to.Int).list }" if $verbose;
my $a = $from.substr(0,1).Int; # [1]
my $b = $to.substr(0,1).Int; # [2]
my $stepping := gather # [3]
{
for $a .. $b > $firstdigit # [4]
{
for $firstdigit 1, $firstdigit + 1 > $seconddigit # [5]
{
next unless 1 < $seconddigit < 10; # [6]
for $seconddigit 1, $seconddigit + 1 > $thirddigit # [7]
{
next unless 1 < $thirddigit < 10; # [8]
my $current = "$firstdigit$seconddigit$thirddigit".Int; # [9]
take $current if $from <= $current <= $to; # [10]
}
}
}
}
say $stepping.join(", "); # [11]
[1] Get the first digit of the from number,
[2] and the same for the to number.
[3] Set it all up with gather
/take
.
[4] A loop for the first digit, iterating over the legal first digits.
[5] A loop for the second digit, where we use the values below (1) and above (+1) the current first digit.
[6] Skip illegal digits (1 and 10), as they don't exist.
[7] A loop for the third digit, where we use the values below (1) and above (+1) the current second digit.
[8] As [6], for the third digit.
[9] We have a stepping number, glue the digits together.
[10] Return the number if it is within the from and to limits. This check is necessary, as e.g. a start value of «199» would give the very first result at «123» (as we only take the first digit into account in the loops).
[11] Nicer output this time, on a single line.
Running it:
$ raku steppinggather 100 199
101, 121, 123
$ raku steppinggather 100 299
101, 121, 123, 210, 212, 232, 234
$ raku steppinggather 100 999
101, 121, 123, 210, 212, 232, 234, 321, 323, 343, 345, 432, 434, 454, 456, \
543, 545, 565, 567, 654, 656, 676, 678, 765, 767, 787, 789, 876, 878, 898, \
987, 989
Note that this algorithm requires
the same length (number of digits) in the from and to values.
That is enforced in this program by the subset
, so we are good.
Suppose there are following coins arranged on a table in a line in random order.
Suppose you are playing against the computer. Player can only pick one coin at a time
from either ends. Find out the lucky winner, who has the larger amounts in total?

I have chosen to write a «coin» class, where we get the coin label (e.g. «£2») when we stringify the object, and the decimal value (e.g. «200») when we use it in a numeric context.
A hash, where we map the strings and the values, would work just as well. But a class is cooler.
File: lucky (partial)unit sub MAIN (:$verbose); # [1]
my $player'sturn = Bool.pick; # [2]
say ": The { $player'sturn ?? 'Player' !! 'Machine' } starts." if $verbose
class coin # [3]
{
has $.value; # [3a]
has $.label; # [3b]
method Str { self.label } # [3c]
method Numeric { self.value } # [3d]
method Real { self.value } # [3e]
}
my @coins; # [4]
@coins.push: coin.new(label => "1p", value => 1); # [4a]
@coins.push: coin.new(label => "2p", value => 2);
@coins.push: coin.new(label => "5p", value => 5);
@coins.push: coin.new(label => "10p", value => 10);
@coins.push: coin.new(label => "20p", value => 20);
@coins.push: coin.new(label => "50p", value => 50);
@coins.push: coin.new(label => "£1", value => 100);
@coins.push: coin.new(label => "£2", value => 200);
@coins.=pick(*); # [5]
say ": Coins (val): { @coins>>.Numeric.join(", ") }" if $verbose; # [6]
say ": Coins (txt): { @coins>>.Str.join(", ") }" if $verbose; # [6]
my $player = 0; # [7]
my $computer = 0; # [7]
[1] Verbose mode, yet again.
[2] The challenge doesn't say if the player or the computer should start
first, so we let the program decide. (True, False).pick
would also
work, but is more typing. Note the '
in the variable name.
That is legal in Raku. This variable is used throughout the program to keep
track of whose turn it is.
[3] The coin has a value (in pence) [3a] and a label (a string) [3b]. The
Str
method is used to stringify the object, and Numeric
is used when we add the coins (in [7]). Real
is used when we
numerically compare two coins (in [14]).
[4] An array of coins, from left to right. Add the coins objects [4a].
[5]
Reorder the coins (pick(*)
picks them all, in
random order), and assign them back to the original array (with .=
).
[6] Note the hyper operator call >>.
that applies
the method (Numeric
and Str
) on each element in the
array. The next method call (.join
) is done on the whole array,
as usual.
[7] The initial amount for the player and computer.
Note the missing check for a matching value and label in the «coin» class. I have played nice here, and specified values that are equal. But don't write code on the assumption that a programmer will not screw up. They do. (I know, as I am one.)
File: lucky (the rest)while @coins # [8]
{
say "Available Coins: { @coins>>.Str.join(", ") }."; # [9]
if $player'sturn # [10]
{
my $choice = @coins.elems > 1 # [11]
?? prompt "Do you want the Left (L) or the Right (R): "
!! "L";
my $coin = $choice.substr(0,1) eq "L" # [12]
?? @coins.shift
!! @coins.pop;
say "You choose $coin"; # [12a]
$player += $coin; # [12b]
}
else # The computer has a go # [13]
{
my $coin = @coins[0] > @coins[*1] # [14]
?? @coins.shift
!! @coins.pop;
say "The Computer choose $coin"; # [14a]
$computer += $coin; # [14b]
}
say "Player: $player p"; # [15]
say "Computer: $computer p";
say "";
$player'sturn = ! $player'sturn # [16]
}
if $player > $computer { say "Player won"; } # [17]
elsif $player < $computer { say "Computer won"; }
else { say "Nobody won"; }
[8] As long as there are more coins.
[9] Show the list of available coins.
[10] If it is the Player's turn,
[11] • Ask about the left or right coin, if more than 1. If only one, take the left without asking.
[12] • If the user has chosen the left, take the left (with shift
).
If not, take the right (with pop
). Add the value [12b].
[13] The computer's turn.
[14] • Choose the coin with the highest value. (This is not the best strategy, as it doesn't look ahead, but it is the easiest one to implement that has a chance of success.) Show which coin was chosen [14a], as the user wouldn't know otherwise.
[15] Show the score.
[16] The other player's turn, ready for the next iteration.
[17] When there are no more coins (see [8]), show who won, if any.
(The else
part is actually redundant with the chosen coins, as it is
impossible to get a tie. The player that gets hold of the «£2» coin will be the
winner, regardless of the other coins. (As both players get 4 coins each, and
the sum of the four most valuable coins, excluding the «£2» coin, is «£1.75».)
Running it:
$ raku lucky
Available Coins: 10p, 1p, 5p, £1, 50p, 2p, £2, 20p.
The Computer choose 20p
Player: 0 p
Computer: 20 p
Available Coins: 10p, 1p, 5p, £1, 50p, 2p, £2.
Do you want the Left (L) or the Right (R): R
You choose £2
Player: 200 p
Computer: 20 p
Available Coins: 10p, 1p, 5p, £1, 50p, 2p.
The Computer choose 10p
Player: 200 p
Computer: 30 p
Available Coins: 1p, 5p, £1, 50p, 2p.
Do you want the Left (L) or the Right (R): R
You choose 2p
Player: 202 p
Computer: 30 p
Available Coins: 1p, 5p, £1, 50p.
The Computer choose 50p
Player: 202 p
Computer: 80 p
Available Coins: 1p, 5p, £1.
Do you want the Left (L) or the Right (R): R
You choose £1
Player: 302 p
Computer: 80 p
Available Coins: 1p, 5p.
The Computer choose 5p
Player: 302 p
Computer: 85 p
Available Coins: 1p.
You choose 1p
Player: 303 p
Computer: 85 p
Player won
And another one:
$ raku lucky
Available Coins: £1, 10p, 1p, 5p, 50p, £2, 2p, 20p.
The Computer choose £1
Player: 0 p
Computer: 100 p
Available Coins: 10p, 1p, 5p, 50p, £2, 2p, 20p.
Do you want the Left (L) or the Right (R): R
You choose 20p
Player: 20 p
Computer: 100 p
Available Coins: 10p, 1p, 5p, 50p, £2, 2p.
The Computer choose 10p
Player: 20 p
Computer: 110 p
Available Coins: 1p, 5p, 50p, £2, 2p.
Do you want the Left (L) or the Right (R): L
You choose 1p
Player: 21 p
Computer: 110 p
Available Coins: 5p, 50p, £2, 2p.
The Computer choose 5p
Player: 21 p
Computer: 115 p
Available Coins: 50p, £2, 2p.
Do you want the Left (L) or the Right (R): L
You choose 50p
Player: 71 p
Computer: 115 p
Available Coins: £2, 2p.
The Computer choose £2
Player: 71 p
Computer: 315 p
Available Coins: 2p.
You choose 2p
Player: 73 p
Computer: 315 p
Computer won
And that's it.