by Arne Sommer

# Stepping Winner with Raku

[63] Published 6. March 2020.

This is my response to the Perl Weekly Challenge #52.

## Challenge #52.1: Stepping Numbers

 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: stepping-loop ```subset 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 stepping-loop 100 199 101 121 123 \$ raku stepping-loop 100 299 101 121 123 210 212 232 234 \$ raku stepping-loop 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.

File: stepping-gather ```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 -> \$first-digit # [4] { for \$first-digit -1, \$first-digit + 1 -> \$second-digit # [5] { next unless -1 < \$second-digit < 10; # [6] for \$second-digit -1, \$second-digit + 1 -> \$third-digit # [7] { next unless -1 < \$third-digit < 10; # [8] my \$current = "\$first-digit\$second-digit\$third-digit".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 stepping-gather 100 199 101, 121, 123 \$ raku stepping-gather 100 299 101, 121, 123, 210, 212, 232, 234 \$ raku stepping-gather 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.

## Challenge #52.2: Lucky Winner

 Suppose there are following coins arranged on a table in a line in random order. ``` £1, 50p, 1p, 10p, 5p, 20p, £2, 2p ``` 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's-turn = Bool.pick; # [2] say ": The { \$player's-turn ?? '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's-turn # [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's-turn = ! \$player's-turn # [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.