22
\$\begingroup\$

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], [], [], [1]   --> 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.

\$\endgroup\$

12 Answers 12

3
\$\begingroup\$

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 [26]
       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.
\$\endgroup\$
  • \$\begingroup\$ _26&1b. -2 bytes. \$\endgroup\$ – jimmy23013 Nov 14 '16 at 13:24
  • \$\begingroup\$ @jimmy23013 Ahhhh, 1b... I was trying to find a short way to turn [26] into 26 and [] into 0 but somehow that didn't occur to me. Thank you :) \$\endgroup\$ – Martin Ender Nov 14 '16 at 13:25
8
\$\begingroup\$

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

\$\endgroup\$
  • 1
    \$\begingroup\$ Would 26-z work? \$\endgroup\$ – u54112 Nov 14 '16 at 9:07
  • \$\begingroup\$ @lastresort Yes of course. /facepalm \$\endgroup\$ – Billywob Nov 14 '16 at 9:08
4
\$\begingroup\$

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
  r.push_back(q);                      //add to result
  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}};
\$\endgroup\$
4
\$\begingroup\$

Python 2, 75 72 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]

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

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.

\$\endgroup\$
3
\$\begingroup\$

Haskell, 62 59 56 bytes

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], [], [], [1]]
[25,0,0,1]
\$\endgroup\$
  • \$\begingroup\$ I think you can write f as f n=13^0^n. \$\endgroup\$ – xnor Nov 14 '16 at 10:01
  • \$\begingroup\$ @xnor I think you're right. Saves 3 bytes. \$\endgroup\$ – Angs Nov 14 '16 at 10:25
  • \$\begingroup\$ 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. \$\endgroup\$ – Zgarb Nov 14 '16 at 12:54
  • \$\begingroup\$ @Zgarb right you are, I'd say that's another 3 bytes. \$\endgroup\$ – Angs Nov 14 '16 at 13:12
2
\$\begingroup\$

05AB1E, 26 22 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!

\$\endgroup\$
2
\$\begingroup\$

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]
\$\endgroup\$
  • \$\begingroup\$ 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]. \$\endgroup\$ – Mego Nov 14 '16 at 18:39
2
\$\begingroup\$

JavaScript (ES6), 82 80 77 72 70 69 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], [], [], [1] ]));
console.log(f([[0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], [], [1], [] ]));
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
\$\endgroup\$
  • \$\begingroup\$ c=>b*26^c saves 2 bytes. \$\endgroup\$ – Neil Nov 15 '16 at 13:13
1
\$\begingroup\$

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!

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

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

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.