Mario Kart Scoring w/ Ties

I ran into this problem while working on another challenge I'm making for this site. In that challenge I utilize "Mario Kart 8 Scoring". The amount of points the player in kth place gets is represented by this 1-indexed array: [15,12,10,9,8,7,6,5,4,3,2,1]. So 1st place gets 15 points, 2nd place gets 12 points, etc.

It's easy enough to assign points like this, however the tricky part comes with how I handle ties. What I do is give each tying player the average of the points given for each tying place. For example, if only 1st and 2nd tied, then both players get (15+12)/2 = 13.5 points. (Note: You're allowed to round to the nearest int, so 13 or 14 are both also acceptable.) Then 3rd - 12th place get the normal amount of points for their position.

Challenge

Given 12 non-negative integer scores that are decreasingly sorted, output the number of points each player gets. You can also take the points list [15,12,10,9,...] as input. Note that the number of points each player gets does not depend on the actual values of the scores, but how they compare to the other scores.

Test Cases

• [21,21,15,14,12,9,6,5,4,3,2,1] => [14,14,10,9,8,7,6,5,4,3,2,1]
• [20,15,15,15,10,9,8,7,6,5,4,3] => [15,10,10,10,8,7,6,5,4,3,2,1]
• explanation: (12+10+9)/3 = 10.3333
• [1,1,1,1,1,1,1,1,1,1,1,1] => [7,7,7,7,7,7,7,7,7,7,7,7]
• explanation: (15+12+10+9+8+7+6+5+4+3+2+1)/12 = 6.8333
• [20,20,20,20,10,10,10,9,8,7,6,5] => [12,12,12,12,7,7,7,5,4,3,2,1]
• explanation: (15+12+10+9)/4 = 11.5, (8+7+6)/3 = 7
• [100,99,98,95,95,95,94,93,93,92,91,91] => [15,12,10,8,8,8,6,5,5,3,2,2]
• explanation: (9+8+7)/3 = 8, (5+4)/2 = 4.5, (2+1)/2 = 1.5

JavaScript (ES6), 57 bytes

Takes input in currying syntax (p)(s), where p is the list of points and s is the list of scores.

p=>s=>s.map(v=>s.reduce((t,x,i)=>x-v?t:t+p[n++,i],n=0)/n)

Test cases

let f =

p=>s=>s.map(v=>s.reduce((t,x,i)=>x-v?t:t+p[n++,i],n=0)/n)

points = [15,12,10,9,8,7,6,5,4,3,2,1];

console.log(JSON.stringify(f(points)([21,21,15,14,12,9,6,5,4,3,2,1])))
console.log(JSON.stringify(f(points)([20,15,15,15,10,9,8,7,6,5,4,3])))
console.log(JSON.stringify(f(points)([1,1,1,1,1,1,1,1,1,1,1,1])))
console.log(JSON.stringify(f(points)([20,20,20,20,10,10,10,9,8,7,6,5])))
console.log(JSON.stringify(f(points)([100,99,98,95,95,95,94,93,93,92,91,91])))

R, 3 bytes

Apparently R has a built-in for this. Takes a list of points and scores as input.

ave

Try it online!

Example:

p=c(15,12,10,9,8,7,6,5,4,3,2,1)

> ave(p,c(20,15,15,15,10,9,8,7,6,5,4,3))
 15.00000 10.33333 10.33333 10.33333  8.00000  7.00000  6.00000  5.00000  4.00000  3.00000  2.00000  1.00000
> ave(p,c(1,1,1,1,1,1,1,1,1,1,1,1))
 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333 6.833333
• Right tool for the job! Aug 23 '17 at 19:55
• This should be 3 bytes (just ave) otherwise it's just a snippet (which isn't allowed). Fortunately, this saves you 5 bytes. Sep 10 '17 at 19:31
• @caird thanks, you're absolutely right.
– BLT
Sep 11 '17 at 14:17

Perl 5, 109 +1 (-a) = 110 bytes

@p=(1..10,12,15);while(@F){$/=$,=0;do{$,++;$/+=pop@p}while($w=shift@F)==$F;push@r,(int.5+$//$,)x$,}say"@r" Try it online! Includes 17 bytes to hardcode the point values. MATL, 12 10 bytes 2 bytes off thanks to @geokavel! 7#uti2XQw) Inputs are a column vector (; as separator) of integer scores and a column vector with the points. The output contains the results separated by newlines. Explanation % Implicitly take first input. % STACK: [21;21;15;14;12;9;6;5;4;3;2;1] 7#u % Unique consecutive integer labels % STACK: [1;1;2;3;4;5;6;7;8;9;10;11] t % Duplicate % STACK: [1;1;2;3;4;5;6;7;8;9;10;11], [1;1;2;3;4;5;6;7;8;9;10;11] i % Take second input % STACK: [1;1;2;3;4;5;6;7;8;9;10;11], [1;1;2;3;4;5;6;7;8;9;10;11], [15;12;10;9;8;7;6;5;4;3;2;1] 2XQ % Average second argument as grouped by the first % STACK: [1;1;2;3;4;5;6;7;8;9;10;11], [13.5;10;9;8;7;6;5;4;3;2;1] w % Swap % STACK: [[13.5;10;9;8;7;6;5;4;3;2;1], [1;1;2;3;4;5;6;7;8;9;10;11] ) % Reference indexing % STACK: [13.5;10;9;8;7;6;5;4;3;2;1] % Implicitly display • Nics solution! I think you can save some bytes by not rounding to nearest int (it's not required). Aug 18 '17 at 13:31 • @geokavel Oh, you are right! I misread the challenge as requering rounding. Thanks! Aug 18 '17 at 16:36 05AB1E, 12 bytes γ€g£vygFyÅAˆ Try it online! Explanation γ # group the scores into chunks of consecutive equal elements €g # get the length of each chunk £ # split the points list into chunks of these sizes v # for each chunk y in the points list ygF # len(y) times do: yÅA # get the arithmetic mean of y ˆ # add to global list # implicitly output global list C# (.NET Core), 154 bytes x=>s=>{for(int i=0;i<12;){int b=0,j=i,a=0,c=0;for(;j<12&&x[i]==x[j];j++,b++){a+=s[j];}a=(int)Math.Round(a/(b+.0));for(;c<b;c++){x[i+c]=a;}i+=b;}return x;} Try it online! C# (.NET Core) + using Linq, 170 + 23 bytes x=>s=>x.GroupBy(z=>z).Select(y=>Enumerable.Repeat(Math.Round(s.Skip(Array.IndexOf(x,y.Key)).Take(y.Count()).Average()),y.Count())).Aggregate((a,b)=>a.Concat(b)).ToArray() Try it online! J, 15 bytes [:;<@(##+/%#)/. Try it online! Takes the list of scores (1 2 ... 12 15) as a right-hand argument and the values to score as a left-hand argument. If this isn't a logical input, add 1 byte for a ~-passive to invert the order in which the inputs are taken. There might be a few things to golf, which include • My usage of boxing • The cap at the end Explanation I'll split this into a couple functions. avg_and_dupe =. # # +/ % # score =. [: ; <@avg_and_dupe/. • avg_and_dupe takes the average of a list and duplicates it as many times as the list's length • score scores an input (left argument) given a list of scores (right argument). avg_and_dupe # # +/ % # # Length # Copy as many times as the left argument +/ % # Average +/ Sum % Divided by # Length This works so nicely because it's treated as two forks. If you're still scratching your head (I know I was at first), ask and I can provide a more in-depth explanation for why this works as it does. score [: ; <@avg_and_dupe/. /. Key: using the values given, partition the scores <@avg_and_dupe For each partition: avg_and_dupe Average and duplicate < Then box ; Raze the boxes into a single list If it's still confusing, I can also add an explanation for /.-key, but I think the wiki page explains it pretty well. • Note that OP added You can also take the points list [15,12,10,9,...] as input. if that saves you any bytes Aug 18 '17 at 1:01 Python 2, 66 bytes -8 bytes thanks to Leaky Nun. lambda s,p:[sum(p[s.index(i):][:s.count(i)])/s.count(i)for i in s] Try it online! Jelly, 11 bytes ṁ⁴Œg¤Æmṁ$€F

Try it online!

-3 bytes thanks to fireflame for noticing new Jelly features :D

• Yeah, it's probably too long looking at how short the solutions on the related challenge are. Aug 18 '17 at 0:46
• @geokavel the annoying thing is that the code to generate the list is longer than the J solution on that one ;_; Aug 18 '17 at 0:49
• I forgot to put that you can take the points list as input too. I'm going to add that. Aug 18 '17 at 0:50
• 11 bytes. Uses the new arithmetic mean monad instead of S÷L and mold instead of xL, which allows \$ instead of two µ. Aug 18 '17 at 2:53
• Sep 10 '17 at 19:25

Python 3, 67 bytes

lambda s,p:[sum(v for j,v in zip(s,p)if j==i)/s.count(i)for i in s]

Try it online!

Python 2, 108 70 bytes

lambda s,p:[1.*sum(v for j,v in zip(s,p)if j==i)/s.count(i)for i in s]

Try it online!

Python 3, 72 bytes

lambda s,p:[sum(p[s.index(i):12-s[::-1].index(i)])/s.count(i)for i in s]

Try it online!

Proton, 62 bytes

(s,p)=>[sum(p[s.index(i)to][to s.count(i)])/s.count(i)for i:s]

Try it online!

Proton, 63 bytes

(s,p)=>map(i=>sum(p[s.index(i)to][to s.count(i)])/s.count(i),s)

Try it online!

• ^^ I'm just gonna start with Proton next time lol. Aug 18 '17 at 19:52

Dyalog APL, 14 bytes

∊{(⊂≢⍴+/÷≢)⍵}⌸

Takes the list of scores as left argument and points list as right argument. Add 2 bytes for wrapping it in () if called directly and not as a named function.

{...}⌸ group right argument by key in left argument and apply function in braces to each group (key operator).

⊂≢⍴+/÷≢ is a fork where:

+/÷≢ is average points for group (sum divided by tally)

≢⍴ tally reshape (replicate the average to match number of items in group)

boxes the result (this is to counteract the mixing of the result that the key operator applies)

is enlist and flattens the result of the key operator (which is a nested vector of vectors) into a simple list.

TryAPL online

g(q:r)t(x:z)|x>head z=(replicate(l(q:t))(sum(q:t)divl(q:t))):g r[]z|1<2=g