DEV Community

Myoungjin Jeon
Myoungjin Jeon

Posted on • Updated on

Weekly Challenge #080 Task #2 :: Raku

Task #2 Count Candies

Submitted by: Mohammad S Anwar

You are given rankings of @N candidates.

Write a script to find out the total candies needed for all
candidates. You are asked to follow the rules below:
a) You must given at least one candy to each candidate.
b) Candidate with higher ranking get more candies than their mediate neighbors on either side.

Example 1:

Input: @N = (1, 2, 2)

Explanation:

Applying rule #a, each candidate will get one candy. 
So total candies needed so far 3.
 Now applying rule #b,  the first candidate do not get any more
candy as its rank is lower than it's neighbours.
  The second candidate gets one more candy as it's ranking is
higher than it's neighbour.
  Finally the third candidate do not get any extra candy
as it's ranking is not higher than neighbour.
  Therefore total candies required is 4.

Output: 4

Example 2:

Input: @N = (1, 4, 3, 2)
Applying rule #a, each candidate will get one candy. So total candies needed so far 4.
  Now applying rule #b, the first candidate do not get any more candy as its rank is lower than it's neighbours.
The second candidate gets two more candies as it's ranking is
higher than it's both neighbour. The third candidate gets
one more candy as it's ranking is higher than it's neighbour.
  Finally the fourth candidate do not get any extra candy
as it's ranking is not higher than neighbour.
  Therefore total candies required is 7.

Output: 7

At first, if you don't the look at the explanation, it is easy to think they are seating in a circular group. I hope it would be more interesting if they are seating along with each other and looking at the candies in front of them. :-]

So I took another journey with Raku
If we are getting only one candy and get hungry
the solution might look like ...

sub candies( *@nums where @nums.all ~~ UInt ) {
    @nums.elems.say;
}
> candies(1, 4, 3, 2);
4

but we are human.. getting more, if we have higher ranking.
to look at the neighbour's ranking, I used rotor again.
but each first and last element has only one neighbour.
It will be probably handy if we add android neighbour next to them.
I used "Inf" in this case, so the android will never give you an extra candy.

sub MAIN( *@nums where @nums.all ~~ UInt ) {
    (Inf, |@nums, Inf).
    rotor(3=>-1).
    say;
}
raku test.raku 1 4 3 2 # example #2
((Inf 1 4) (1 4 3) (4 3 2) (3 2 Inf))

so we can now see how we could can one or two more candies when compared to neighbours (even if we are at one hundred higher position to them so people get lazy)

sub MAIN( *@nums where @nums.all ~~ UInt ) {
    (Inf,|@nums,Inf).
    rotor(3=>-2).
    kv. # takes each elements as key => value
    map( -> $index, $grp {
              my $c = 1; # rule "a"
              if $grp[0] < $grp[1] {
                  ++$c;
              }
              if $grp[2] < $grp[1] {
                  ++$c;
              }
              "{$index} gets => $c as {$grp}"
           } ).
    join("\n").
    say;
}
0 gets => 1 as Inf 1 4
1 gets => 3 as 1 4 3
2 gets => 2 as 4 3 2
3 gets => 1 as 3 2 Inf

I believe that it is always good habit to check the given data and result is matched what I intend to get.
and we now we can add them all into one summation form.

but if we think it is a correct method. why not make it shorter?
so I tried to get a little compact(golfed) code.

and final form is ...

sub MAIN{(|@_,|((|@_,|@_.reverse).rotor(2=>-1).map({[-] $_}).grep(*>0))).elems.say}

as I mentioned before. I'm not a type of golf programmer.
and there is - surprisingly - always... a shorter way to do the task.

Latest comments (2)

Collapse
 
jeongoon profile image
Myoungjin Jeon • Edited

This is probably final solution which I'll upload in PWC :-)
after solving task #2 in haskell

sub MAIN{(|@_,|(|@_.rotor(2=>-1).grep({[!=] $_}))).elems.say}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jeongoon profile image
Myoungjin Jeon • Edited

Oh.. I found shorter one.

sub MAIN{(|@_,|((|@_,|@_.reverse).rotor(2=>-1).grep({[<] $_}))).elems.say}