Score a hand of Hearts

Hearts is a trick-taking card game for 4 players. Each trick is taken by the player who played the highest card of the leading suit. At the end of each hand, the players incur a penalty score depending on the penalty cards they have taken; the task is to determine the scores under Microsoft Hearts rules.

Input

Input is 4 lists (or delimited string, array, etc.) showing the penalty cards taken by each of the 4 players. The penalty cards are

2♥, 3♥, 4♥, 5♥, 6♥, 7♥, 8♥, 9♥, 10♥, J♥, Q♥, K♥, A♥, Q♠

which we shall represent as

2,  3,  4,   5,  6,  7,  8,  9,  10,  11, 12,  13,  1,  0

respectively.

Output

Output is the 4 penalty points incurred by the 4 players (list, string, array etc.). Scoring is as follows:

• Each heart (, represented by integers 1 to 13 inclusive) incurs 1 point
• The queen of spades (Q♠, represented by 0) incurs 13 points
• Exception: if a player has taken all of the penalty cards (called shooting the moon), then he incurs 0 points, while all other players incur 26 points.

Test cases

[2, 8, 7, 1], [3, 4], [], [9, 5, 6, 0, 10, 11, 12, 13]     -->  4,  2,  0, 20
[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [], [],    --> 25,  0,  0,  1
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0], [], [], [] -->  0, 26, 26, 26

Shortest code in bytes wins.

R, 85 77 74 bytes

function(x,z=sapply(x,function(x)sum(x>0)+any(x<1)*13))abs(z-any(z>25)*26)

Unnamed function that takes an R-list as input. Works by counting the number of elements >0 and adds 13 if any element within each vector is <1 (i.e. queen of spades) and store as z.

If any element in z is >25, return 26-z, else return z.

Try it on R-fiddle

• Would 26-z work?
– user58632
Nov 14 '16 at 9:07
• @lastresort Yes of course. /facepalm Nov 14 '16 at 9:08

C++14, 158 bytes

As unnamed Lambda:

[](auto c){typename decltype(c)::value_type r;int b=0;for(auto d:c){int q=0;for(auto i:d)q+=i?1:13;r.push_back(q);b+=q==26;}if(b)for(int&x:r)x=26-x;return r;}

Requires a vector<vector<int>> and returns vector<int>

Ungolfed:

[](auto c){
typename decltype(c)::value_type r;   //result vector
int b=0;                              //flag if one has all cards
for(auto d:c){                        //over all decks
int q=0;                             //count points
for(auto i:d) q+=i?1:13;             //+13 for queen, +1 else
b+=q==26;                            //possibly activate flag
}
if(b) for(int&x:r) x=26-x;            //if flag is set, mirror the results
return r;
}

Few testcases for you:

auto r = std::vector<std::vector<int>>{{2,8,7,1},{3,4},{},{9,5,6,0,10,11,12,13}};
auto s = std::vector<std::vector<int>>{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0},{},{},{}};
auto t = std::vector<std::vector<int>>{{},{2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0},{},{1}};

Python 2, 7572 71 bytes

i=[len(a)+12*(0in a)for a in input()]
print[[x,26-x][26in i]for x in i]

Takes input as [2, 8, 7, 1], [3, 4], [], [9, 5, 6, 0, 10, 11, 12, 13]

• Could you save 3 characters by using 12*[0in a] instead of [0,12][0in a]? Nov 14 '16 at 14:06
• @Costantino I think you mean 12*(0in a). Nov 14 '16 at 14:25
• print[[x,26-x][26in i]for x in i] is one byte shorter. Nov 14 '16 at 16:37

CJam, 22 20 bytes

Thanks to jimmy23013 for saving 2 bytes.

{{XD?}f%1fb_26&1bf^}

An unnamed block (function), which takes a list of 4 lists as input and returns the list of scores.

Try it online!

Explanation

{      e# For each card...
XD?  e#   Choose 1 if it's positive and 13 if it's zero.
}f%
1fb    e# Sum each hand.
_26&   e# Get the set intersection of the scores with 26. This gives 
e# if someone shot the moon, and [] otherwise.
1b     e# Treat as base-1 digits, which gives 26 if someone shot the moon
e# and zero otherwise.
f^     e# XOR each result with this number. This swaps zeros and 26s when
e# someone shot the moon and does nothing otherwise.
• _26&1b. -2 bytes. Nov 14 '16 at 13:24
• @jimmy23013 Ahhhh, 1b... I was trying to find a short way to turn  into 26 and [] into 0 but somehow that didn't occur to me. Thank you :) Nov 14 '16 at 13:25

PHP, 113 bytes

function h($a){foreach($a as&$b)$b=count($b)+12*in_array(0,$b);if(max($a)>25)foreach($a as&$n)$n=26-$n;return$a;}

function takes an array of arrays, returns an array of values.

Marvel the other array mapping in PHP: loops with referenced items. Waaay shorter than array_map.

f x|all(<26)x=x|0<1=map(26-)x
f.map(sum.map((13^).(0^)))

Usage:

> f.map(sum.map((13^).(0^))) $[[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [], [], ] [25,0,0,1] • I think you can write f as f n=13^0^n. – xnor Nov 14 '16 at 10:01 • @xnor I think you're right. Saves 3 bytes. – Angs Nov 14 '16 at 10:25 • I think defining f x|all(<26)x=x|0<1=map(26-)x and using it in place of the lambda function saves some bytes. Nov 14 '16 at 12:54 • @Zgarb right you are, I'd say that's another 3 bytes. – Angs Nov 14 '16 at 13:12 05AB1E, 2622 21 bytes Trailing whitespace must be removed from input so that it is interpreted as an array. The ending was inspired of the other answers when using (26-x) when a player gathered all penalty cards. vy0å12*yg+})D26©åi(®+ v For each array y Push array on the stack 0å Generate a boolean array indicating whether the queen of spades is at the same index in the original array 12* Multiply by 12 the value of the queen of spades yg+ Add the length of the array; the queen of spades gets her last point from this part } End for ) Push an array of all evaluated scores D26©å 1 if there is a 26, 0 otherwise i If there is a 26 (®+ Mirror the array: for each element yield 26-element Implicit end if Implicitly print the score array Try it online! It still looks pretty golfable, with duplicated constants and conditional statements. Former version, 26 bytes (One byte for each point in maximal penalty value) I decided to keep it as its length fits best this challenge in my opinion :) . vyD0å12*sg+})D26©QDOi_®*ë\ Try it online! Python 3, 101 bytes def s(a):r=[sum([(1,13)[c==0]for c in h])for h in a];s=(r,[(26,0)[s==26]for s in r]);return s[26in r] Full code: def score(hands): result = [sum([(1, 13)[card == 0] for card in hand]) for hand in hands] results = (result, [(26, 0)[score == 26] for score in result]) return results[26 in result] • 12*(c<1)+1 is 2 bytes shorter than (1,13)[c==0]. 26*(s>25) is 3 bytes shorter than (26,0)[s==26]. – user45941 Nov 14 '16 at 18:39 JavaScript (ES6), 828077727069 67 bytes Saved 2 bytes thanks to @Neil f = s=>s.map(c=>c.map(t=>r+=t?1:13,r=0)|(b|=r>25,r),b=0).map(c=>b*26^c) console.log(f.toString().length) console.log(f([[2, 8, 7, 1], [3, 4], [], [9, 5, 6, 0, 10, 11, 12, 13]])); console.log(f([[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [], [],  ])); console.log(f([[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [], , [] ])); console.log(f([[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0], [], [], []])); console.log(f([[],[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0], [], []])); Breakdown s=>s.map( // for each hand c=>c.map( // for each card t=>r+=t?1:13, // add value of card r=0)|( b=b|r>25,r // set flag if any hand scores 26 points ), b=0) .map(c=>b? // for every card if a hand scored 26 c?0:26 // set every 0 hand to 26 and the 26 hand to 0 :c) // otherwise do nothing • c=>b*26^c saves 2 bytes. – Neil Nov 15 '16 at 13:13 Pip, 28 bytes 27 bytes of code, +1 for -p flag. Y{$+1+12*!*a}M Va26Ny?26-yy

Takes input on the command-line as a string representing a nested list, like "[[2 8 7 1] [3 4] [] [9 5 6 0 10 11 12 13]]" (quotes not needed on TIO). Try it online!

Ruby, 59 bytes

->a{a.map{|h|a.max.size>13?h.min||26:h.size+12*h.count(0)}}

Or, alternatively,

->a{a.map{|h|a.count([])>2?h.min||26:h.size+12*h.count(0)}}

If only one hand has any cards, we want the empty hands to get a value of 26, and the hand with cards to get a value of 0. I do this by calling min on the hands - this returns nil for an empty array, and then I || it into 26. In other cases, I count the number of cards in a hand and then add 12 to the Queen of Spades.

Scala, 93 bytes

a=>{val% =a.map(_.map{case 0=>13;case _=>1}sum)
if(%toSet 26)%map{case 0=>26;case _=>0}else%}

Usage:

val f:(Seq[Seq[Int]]=>Seq[Int])=...
f(Seq(Seq(2, 8, 7, 1), Seq(3, 4), Seq(), Seq(9, 5, 6, 0, 10, 11, 12, 13)))

Explanation:

a=>{           //define an anonymou function with a parameter a
val% =         //define % as...
a.map(         //map each element of a...
_.map{         //to each of the card
case 0=>13     //replaced with its value
case _=>1
}
sum            //and the sum of the values
)
if(            //if
%toSet 26      //one player has all cards
)
%map{          //return % with...
case 0=>26     //each 0 replaced with 26
case _=>0      //and everything else (aka the 26) replaced 0
}
else           //else
%              //return %
}

I could use %toSet 26 instead of % contains 26 because Set's apply method is contains and not get-at-index like Seq's