Scoring Chinese Hearts

6

Chinese Hearts, also known as Gong Zhu (拱猪, Chase the Pig), is a variance of the Hearts game. It is a 4 player card game with the aim to earn the highest amount of points. You may read more about it on Wikipedia. This time, we want implement a program (full or function) to calculate ones score.

Scoring

The player who plays the highest-value card of the suit that is led wins all cards in this round. And after the game, the score is calculated by these cards:

  • ♥2, ♥3, ♥4 = 0 points for each;
  • ♥5, ♥6, ♥7, ♥8, ♥9, ♥10 = -10 points for each;
  • ♥J = -20 points;
  • ♥Q = -30 points;
  • ♥K = -40 points;
  • ♥A = -50 points;
  • ♠Q = -100 points; ♠Q known as the Pig (猪)
  • ♦J = +100 points; ♦J known as the Goat / Sheep (羊)
  • ♣10 = +50 points along, and x2 with other cards; ♣10 known as the Double / Transformer (变压器)

Special rules:

  • If a player collects only ♣10, it is worth +50 points. But if a player collects ♣10 together with any other non-zero point cards, it doubles the other cards' points.
  • If a player collects all hearts ♥2 ~ ♥A (全红), ♥2 ~ ♥A count as +200 points instead of -200 points.
  • If a player collects all hearts plus ♠Q and ♦J, ♠Q counts as +100 points instead of -100 points.

Scoring rules may vary from place to place. Note that this challenge does not include "sell" (卖/明牌) which would make it more complex.

Input / Output

Input would be a string (or a list of characters).

  • 23456789TJQKA for ♥2 ~ ♥A
  • P for ♠Q
  • G for ♦J
  • D for ♣10

All other cards are ignored from input. You won't get characters out of this list. We use one deck of cards (52 cards) in this challenge. So, there won't be duplicate cards in the input.

Output as an integer, the score of these cards.

Examples and Testcases

Input            Output
234               0
56789T           -60
T                -10
J                -20
Q                -30
K                -40
A                -50
TJQK             -100
P                -100
G                 100
TJQKP            -200
PG                0
2468TQKG          0
D                 50
234D              50
GD                200
PGD               0
TJQKGD            0
23456789TJQK     -150
3456789TJQKA     -200
23456789TJQKA     200
23456789TJQKAP    100
23456789TJQKAG    300
23456789TJQKAPG   400
2356789TJQKAPD   -600
23456789TJQKAPGD  800
DP2A45968TJK7QG3  800
DP24             -200
G2P6D4           -20

Rule


Note: Some details of scoring rules may be differ from game to game. Some rules allow all hearts plus ♠Q count as +300 points, regardless whether ♦J is collected or not. Some rules apply ♥2, ♥3, ♥4 to ♣10, and collecting ♥2, ♥3, ♥4 together with ♣10 is 0 points instead of +50 points. However, your answer have to follow the rule described in this post.

tsh

Posted 2018-10-07T04:41:23.163

Reputation: 13 072

Answers

1

JavaScript (ES6), 160 156 146 143 135 bytes

Saved 10 bytes thanks to @tsh

Takes input as an array of characters.

a=>(a.map(c=>(i='GPDTJQKA'.search(c))>2|c?t-=h--*c?c>4:i-2:m|=1<<i,m=t=0,h=13),t+=!h*40+(m&2)*(m&!h?5:-5)+m%2*10,t+=m&4?t|m&3?t:5:0)*10

Try it online!

Commented

a => (                     // a[] = input array
  a.map(c =>               // for each card c in a[]:
    (i =                   //   convert c into an index i (-1 to 7)
      'GPDTJQKA'.search(c) //   in the string 'GPDTJQKA'
    ) > 2                  //   if c is a heart symbol (TJQKA)
    | c ?                  //   or c is numeric (2-9):
      t -=                 //     update t (total number of points / 10):
        h--                //       decrement h (number of hearts)
        * c ?              //       if c is numeric:
          c > 4            //         decrement t if c is greater than 4
        :                  //       else:
          i - 2            //         subtract 1 (for Ten) to 5 (for Ace) from t
    :                      //   else:
      m |= 1 << i,         //     update the bitmask m of special cards (G=1, P=2, D=4)
    m = t = 0, h = 13      //   start with m = 0, t = 0 and h = 13
  ),                       // end of map()
  t +=                     // update t (pass #1):
    !h * 40 +              //   add 40 to t if we have all hearts (-(-200) + 200)
    (m & 2) * (            //   if we have the Pig:
      m & !h ?             //     if we have all hearts and the Goat:
        5                  //       add 10 to t (the Pig is worth +100)
      :                    //     else:
        -5                 //       subtract 10 from t (the Pig is worth -100)
    ) +                    //
    m % 2 * 10,            //   add 10 to t if we have the Goat (the Goat is worth +100)
  t +=                     // update t (pass #2):
    m & 4 ?                //   if we have the Double:
      t | m & 3 ?          //     if t is non-zero or we have either the Pig or the Goat:
        t                  //       double t by adding it to itself
      :                    //     else:
        5                  //       add 5 to t (the Double alone is worth +50)
    :                      //   else:
      0                    //     leave t unchanged
) * 10                     // multiply the final result by 10

Arnauld

Posted 2018-10-07T04:41:23.163

Reputation: 111 334

146 bytes – tsh – 2018-10-07T23:32:22.433

1

Charcoal, 107 bytes

≔”A↘⊟T“{»aS¡I⁷”ζ≔×χΣΦ”)⊟≡GO∧”№θ§ζκηF¬⌊E…⮌ζ¹³№θι≦±ηF№θG≧⁺¹⁰⁰η¿⌊Eζ№θι≧⁺¹⁰⁰ηF№θP≧⁻¹⁰⁰ηF№θD¿Φ…ζ¹²№θκ≧ײη≧⁺⁵⁰ηIη

Try it online! Link is to verbose version of code. Explanation:

≔”A↘⊟T“{»aS¡I⁷”ζ

Save the string PGAKQJT98765432 for later. In particular, this string ends with the 13 Hearts and starts with the 12 scoring cards.

≔×χΣΦ”)⊟≡GO∧”№θ§ζκη

Loop over the string 005432111111. Keep the digits corresponding to the cards from the saved string that appear in the input, then take the sum and multiply by 10.

F¬⌊E…⮌ζ¹³№θι≦±η

If at least one of the Hearts does not appear in the input then negate the total.

F№θG≧⁺¹⁰⁰η

Add 100 for each Goat.

¿⌊Eζ№θι≧⁺¹⁰⁰η

If the Goat and all of the Hearts are present then add 100 for the Pig,

F№θP≧⁻¹⁰⁰η

otherwise subtract 100 for each Pig.

F№θD¿Φ…ζ¹²№θκ≧ײη≧⁺⁵⁰η

For each Transformer, if any of the scoring cards a present then double the total otherwise add 50.

Iη

Output the total.

Neil

Posted 2018-10-07T04:41:23.163

Reputation: 95 035

Don't know if it helps your byte count but there can only be one "pig" and one "transformer" in the input. – Shaggy – 2018-10-07T16:53:46.563

@Shaggy Charcoal has implicit else so using if costs a byte if you don't need it, unless it happens to be at the end of a block. (Of course, you can only switch to for if your value is known to be 0 or 1.) – Neil – 2018-10-07T20:12:49.953

1

Python 2, 183 180 bytes

H=set(input())
a=len(H-set('PGD'))>12
s=sum('TJQKA'.find(c)+1or'4'<c<':'for c in H)*(1|-a)-10*[0,1,-1,2*a][('G'in H)+2*('P'in H)]
print-10*[s,[-5,2*s][any(H-set('D234'))]]['D'in H]

Try it online!

TFeld

Posted 2018-10-07T04:41:23.163

Reputation: 19 246