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\$
0

13 Answers 13

7
\$\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\$
2
  • 1
    \$\begingroup\$ Would 26-z work? \$\endgroup\$
    – user58632
    Nov 14, 2016 at 9:07
  • \$\begingroup\$ @lastresort Yes of course. /facepalm \$\endgroup\$
    – Billywob
    Nov 14, 2016 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\$
3
  • \$\begingroup\$ Could you save 3 characters by using 12*[0in a] instead of [0,12][0in a]? \$\endgroup\$
    – Costantino
    Nov 14, 2016 at 14:06
  • \$\begingroup\$ @Costantino I think you mean 12*(0in a). \$\endgroup\$
    – mbomb007
    Nov 14, 2016 at 14:25
  • \$\begingroup\$ print[[x,26-x][26in i]for x in i] is one byte shorter. \$\endgroup\$
    – mathmandan
    Nov 14, 2016 at 16:37
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\$
2
  • \$\begingroup\$ _26&1b. -2 bytes. \$\endgroup\$
    – jimmy23013
    Nov 14, 2016 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\$ Nov 14, 2016 at 13:25
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\$
4
  • \$\begingroup\$ I think you can write f as f n=13^0^n. \$\endgroup\$
    – xnor
    Nov 14, 2016 at 10:01
  • \$\begingroup\$ @xnor I think you're right. Saves 3 bytes. \$\endgroup\$
    – Angs
    Nov 14, 2016 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, 2016 at 12:54
  • \$\begingroup\$ @Zgarb right you are, I'd say that's another 3 bytes. \$\endgroup\$
    – Angs
    Nov 14, 2016 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\$
1
  • \$\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\$
    – user45941
    Nov 14, 2016 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\$
2
  • \$\begingroup\$ c=>b*26^c saves 2 bytes. \$\endgroup\$
    – Neil
    Nov 15, 2016 at 13:13
  • \$\begingroup\$ s=>s.map(b=c=>c.map(t=>r+=t?1:13,r=0)|(b|=r>25,r)).map(c=>b*26^c)? \$\endgroup\$
    – l4m2
    Feb 15, 2022 at 8:11
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\$
1
\$\begingroup\$

Vyxal, 18 bytes

ƛ0c12*nL+;:26c[26꘍

Try it online or run all the test cases.

How?

ƛ0c12*nL+;:26c[26꘍
ƛ                   # For each inner list in the (implicit) input:
 0c                 # Does it contain a zero (the queen of spades)?
   12*              # Multiply this by 12 (aka if it contains a zero, then 12, otherwise 0)
      nL+           # Add the length of the inner list to this
         ;          # Close map lambda
          :         # Duplicate
           26c      # Does it contain 26? (aka someone shot the moon)
              [26꘍  # Then bitwise XOR each with 26 (swapping 26 and 0)
\$\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 and acknowledge that you have read and understand our privacy policy and code of conduct.