Perl Weekly Challenge 214.
My solutions (task 1 and task 2 ) to the The Weekly Challenge - 214.
Task 1: Rank Score
Submitted by: Mohammad S Anwar
You are given a list of scores (>=1).
Write a script to rank each score in descending order. First three will get medals i.e. G (Gold), S (Silver) and B (Bronze). Rest will just get the ranking number.
Using the standard model of giving equal scores equal rank, then advancing that number of ranks.
Example 1
Input: @scores = (1,2,4,3,5)
Output: (5,4,S,B,G)
Score 1 is the 5th rank.
Score 2 is the 4th rank.
Score 4 is the 2nd rank i.e. Silver (S).
Score 3 is the 3rd rank i.e. Bronze (B).
Score 5 is the 1st rank i.e. Gold (G).
Example 2
Input: @scores = (8,5,6,7,4)
Output: (G,4,B,S,5)
Score 8 is the 1st rank i.e. Gold (G).
Score 4 is the 4th rank.
Score 6 is the 3rd rank i.e. Bronze (B).
Score 7 is the 2nd rank i.e. Silver (S).
Score 4 is the 5th rank.
Example 3
Input: @list = (3,5,4,2)
Output: (B,G,S,4)
Example 4
Input: @scores = (2,5,2,1,7,5,1)
Output: (4,S,4,6,G,S,6)
I read the list of scores from @ARGV
, order them from largest to
smallest and assign each score a rank, taken from an array of possible
ranks, ordered from first place (Gold) to last (@ARGV
). I only
assign a rank if the score has not been seen before. Finally, I
map scores to ranks and print the result. This fits a oneliner.
Examples:
perl -E '
@a=@ARGV; @r=(qw(G S B), 4..@ARGV); map {$c=$r[$i++]; $s{$_}//=$c} sort{$b<=>$a}@a; say join " ", @a, "->", @s{@a}
' 1 2 4 3 5
perl -E '
@a=@ARGV; @r=(qw(G S B), 4..@ARGV); map {$c=$r[$i++]; $s{$_}//=$c} sort{$b<=>$a}@a; say join " ", @a, "->", @s{@a}
' 8 5 6 7 4
perl -E '
@a=@ARGV; @r=(qw(G S B), 4..@ARGV); map {$c=$r[$i++]; $s{$_}//=$c} sort{$b<=>$a}@a; say join " ", @a, "->", @s{@a}
' 3 5 4 2
perl -E '
@a=@ARGV; @r=(qw(G S B), 4..@ARGV); map {$c=$r[$i++]; $s{$_}//=$c} sort{$b<=>$a}@a; say join " ", @a, "->", @s{@a}
' 2 5 2 1 7 5 1
Results:
1 2 4 3 5 -> 5 4 S B G
8 5 6 7 4 -> G 4 B S 5
3 5 4 2 -> B G S 4
2 5 2 1 7 5 1 -> 4 S 4 6 G S 6
The full code is similar:
1 # Perl weekly challenge 214
2 # Task 1: Rank Score
3 #
4 # See https://wlmb.github.io/2023/04/24/PWC214/#task-1-rank-score
5 use v5.36;
6 die <<~"FIN" unless @ARGV;
7 Usage: $0 S1 [S2..]
8 to rank the scores S1 S2...
9 FIN
10 my @scores=@ARGV;
11 my @ranks=(qw(Gold Silver Bronce), 4..@scores); # G S B 4 5 6...
12 my $counter;
13 my %score_to_rank;
14 map {my $current=$ranks[$counter++]; $score_to_rank{$_}//=$current}
15 sort {$b<=>$a} @scores;
16 say join " ", @scores, "->", @score_to_rank{@scores}
Example:
./ch-1.pl 1 2 4 3 5
./ch-1.pl 8 5 6 7 4
./ch-1.pl 3 5 4 2
./ch-1.pl 2 5 2 1 7 5 1
Results:
1 2 4 3 5 -> 5 4 Silver Bronce Gold
8 5 6 7 4 -> Gold 4 Bronce Silver 5
3 5 4 2 -> Bronce Gold Silver 4
2 5 2 1 7 5 1 -> 4 Silver 4 6 Gold Silver 6
Task 2: Collect Points
Submitted by: Mohammad S Anwar
You are given a list of numbers.
You will perform a series of removal operations. For each operation, you remove from
the list N (one or more) equal and consecutive numbers, and add to your score N × N.
Determine the maximum possible score.
Example 1:
Input: @numbers = (2,4,3,3,3,4,5,4,2)
Output: 23
We see three 3's next to each other so let us remove that first and collect 3 x 3 points.
So now the list is (2,4,4,5,4,2).
Let us now remove 5 so that all 4's can be next to each other and collect 1 x 1 point.
So now the list is (2,4,4,4,2).
Time to remove three 4's and collect 3 x 3 points.
Now the list is (2,2).
Finally remove both 2's and collect 2 x 2 points.
So the total points collected is 9 + 1 + 9 + 4 => 23.
Example 2:
Input: @numbers = (1,2,2,2,2,1)
Output: 20
Remove four 2's first and collect 4 x 4 points.
Now the list is (1,1).
Finally remove the two 1's and collect 2 x 2 points.
So the total points collected is 16 + 4 => 20.
Example 3:
Input: @numbers = (1)
Output: 1
Example 4:
Input: @numbers = (2,2,2,1,1,2,2,2)
Output: 40
Remove two 1's = 2 x 2 points.
Now the list is (2,2,2,2,2,2).
Then reomove six 2's = 6 x 6 points.
The brute force solution consists of building groups of repeated entries and removing them in all possible orders, joining neighbor groups if possible, using a recursive routine. I tried some heuristic solutions but they didn’t quite work. I first present a compact 4.5 liner solution and then expand it into a more reasonable code.
Example 1:
perl -E '@l=@ARGV;@c=(shift @l, 1);for(@l){push(@n, [@c]), @c=($_, 0) unless $c[0] eq $_; $c[1]++;}
push @n, [@c];say "@ARGV -> ", [sort {$b <=> $a} map {p($_, \@n)} 0..@n-1]->[0]; sub p($w, $r){
my @r=@$r; my $p = $r[$w][1]**2; $r[$w-1]=[$r[$w-1][0],$r[$w-1][1]+$r[$w+1][1]], splice @r, $w+1,1
if 0<$w<@r-1 && $r[$w-1][0] eq $r[$w+1][0]; splice @r, $w, 1; @p= sort {$b <=> $a} map {p($_, \@r)}
0..@r-1; $p += shift @p if @p; $p;}
' 2 4 3 3 3 4 5 4 2
Results:
2 4 3 3 3 4 5 4 2 -> 23
Example 2:
perl -E '@l=@ARGV;@c=(shift @l, 1);for(@l){push(@n, [@c]), @c=($_, 0) unless $c[0] eq $_; $c[1]++;}
push @n, [@c];say "@ARGV -> ", [sort {$b <=> $a} map {p($_, \@n)} 0..@n-1]->[0]; sub p($w, $r){
my @r=@$r; my $p = $r[$w][1]**2; $r[$w-1]=[$r[$w-1][0],$r[$w-1][1]+$r[$w+1][1]], splice @r, $w+1,1
if 0<$w<@r-1 && $r[$w-1][0] eq $r[$w+1][0]; splice @r, $w, 1; @p= sort {$b <=> $a} map {p($_, \@r)}
0..@r-1; $p += shift @p if @p; $p;}
' 1 2 2 2 2 1
Results:
1 2 2 2 2 1 -> 20
Example 3:
perl -E '@l=@ARGV;@c=(shift @l, 1);for(@l){push(@n, [@c]), @c=($_, 0) unless $c[0] eq $_; $c[1]++;}
push @n, [@c];say "@ARGV -> ", [sort {$b <=> $a} map {p($_, \@n)} 0..@n-1]->[0]; sub p($w, $r){
my @r=@$r; my $p = $r[$w][1]**2; $r[$w-1]=[$r[$w-1][0],$r[$w-1][1]+$r[$w+1][1]], splice @r, $w+1,1
if 0<$w<@r-1 && $r[$w-1][0] eq $r[$w+1][0]; splice @r, $w, 1; @p= sort {$b <=> $a} map {p($_, \@r)}
0..@r-1; $p += shift @p if @p; $p;}
' 1
Results:
1 -> 1
Example 4:
perl -E '@l=@ARGV;@c=(shift @l, 1);for(@l){push(@n, [@c]), @c=($_, 0) unless $c[0] eq $_; $c[1]++;}
push @n, [@c];say "@ARGV -> ", [sort {$b <=> $a} map {p($_, \@n)} 0..@n-1]->[0]; sub p($w, $r){
my @r=@$r; my $p = $r[$w][1]**2; $r[$w-1]=[$r[$w-1][0],$r[$w-1][1]+$r[$w+1][1]], splice @r, $w+1,1
if 0<$w<@r-1 && $r[$w-1][0] eq $r[$w+1][0]; splice @r, $w, 1; @p= sort {$b <=> $a} map {p($_, \@r)}
0..@r-1; $p += shift @p if @p; $p;}
' 2 2 2 1 1 2 2 2
Results:
2 2 2 1 1 2 2 2 -> 40
The full code, with some comments, is:
1 # Perl weekly challenge 214
2 # Task 2: Collect Points
3 #
4 # See https://wlmb.github.io/2023/04/24/PWC214/#task-2-collect-points
5 use v5.36;
6 die <<~"FIN" unless @ARGV;
7 Usage: $0 N1 [N2...]
8 to collect all points from the list N1 N2...
9 FIN
10 # Build list of nodes, one for each group
11 my @list=@ARGV;
12 my $first=shift @list;
13 my @current=($first, 1); # nodes are of the form [value, count]
14 my @nodes;
15 for(@list){
16 push(@nodes, [@current]), @current=($_, 0) unless $current[0] eq $_; # New node if value changes
17 $current[1]++;
18 }
19 push @nodes, [@current]; # array of pairs [value, count]
20 # remove all groups with different starting group and choose largest score
21 my @points = sort {$b <=> $a} map {remove($_, \@nodes)} 0..@nodes-1;
22 my $points=$points[0]; # largest score
23 say "@ARGV -> $points";
24
25 # Get max points after removing all $remaining nodes starting from $which
26 sub remove($which, $remaining){
27 my @copy=@$remaining;
28 my $points = $copy[$which][1]**2;
29 # join neighbor groups if possible
30 $copy[$which-1]=[$copy[$which-1][0],$copy[$which-1][1]+$copy[$which+1][1]],
31 splice @copy, $which+1,1
32 if 0<$which<@copy-1 && $copy[$which-1][0] eq $copy[$which+1][0];
33 splice @copy, $which, 1;
34 my @points= sort {$b <=> $a} map {remove($_, \@copy)} 0..@copy-1;
35 $points += $points[0] if @points;
36 return $points;
37 }
38
Examples:
./ch-2.pl 2 4 3 3 3 4 5 4 2
./ch-2.pl 1 2 2 2 2 1
./ch-2.pl 1
./ch-2.pl 2 2 2 1 1 2 2 2
Results:
2 4 3 3 3 4 5 4 2 -> 23
1 2 2 2 2 1 -> 20
1 -> 1
2 2 2 1 1 2 2 2 -> 40