Score a hand of Hearts

22

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.

for Monica

Posted 2016-11-14T07:23:24.727

Reputation: 1 172

Answers

3

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.

Martin Ender

Posted 2016-11-14T07:23:24.727

Reputation: 184 808

_26&1b. -2 bytes. – jimmy23013 – 2016-11-14T13:24:41.537

@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 :) – Martin Ender – 2016-11-14T13:25:19.030

8

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

Billywob

Posted 2016-11-14T07:23:24.727

Reputation: 3 363

1Would 26-z work? – for Monica – 2016-11-14T09:07:01.310

@lastresort Yes of course. /facepalm – Billywob – 2016-11-14T09:08:28.177

4

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}};

Karl Napf

Posted 2016-11-14T07:23:24.727

Reputation: 4 131

4

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]

TFeld

Posted 2016-11-14T07:23:24.727

Reputation: 19 246

Could you save 3 characters by using 12*[0in a] instead of [0,12][0in a]? – Costantino – 2016-11-14T14:06:47.720

@Costantino I think you mean 12*(0in a). – mbomb007 – 2016-11-14T14:25:28.850

print[[x,26-x][26in i]for x in i] is one byte shorter. – mathmandan – 2016-11-14T16:37:26.420

3

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]

Angs

Posted 2016-11-14T07:23:24.727

Reputation: 4 825

I think you can write f as f n=13^0^n. – xnor – 2016-11-14T10:01:42.910

@xnor I think you're right. Saves 3 bytes. – Angs – 2016-11-14T10:25:17.157

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. – Zgarb – 2016-11-14T12:54:33.440

@Zgarb right you are, I'd say that's another 3 bytes. – Angs – 2016-11-14T13:12:29.597

3

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.

Titus

Posted 2016-11-14T07:23:24.727

Reputation: 13 814

2

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!

Osable

Posted 2016-11-14T07:23:24.727

Reputation: 1 321

2

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

Lmis

Posted 2016-11-14T07:23:24.727

Reputation: 421

c=>b*26^c saves 2 bytes. – Neil – 2016-11-15T13:13:10.623

2

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]

Jimmy Johnson

Posted 2016-11-14T07:23:24.727

Reputation: 71

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]. – Mego – 2016-11-14T18:39:03.483

1

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.

Lee W

Posted 2016-11-14T07:23:24.727

Reputation: 521

1

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!

DLosc

Posted 2016-11-14T07:23:24.727

Reputation: 21 213

0

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

corvus_192

Posted 2016-11-14T07:23:24.727

Reputation: 1 889