Evaluate a Skat-Hand

18

1

Introduction

Skat is a traditional German card game for 3 players. The deck consists of 32 cards: Ace, King, Queen, Jack, 10, 9, 8, 7 in all 4 suits (Clubs, Spades, Hearts, Diamonds).

In every round there one player plays solo while the other two play against him. At the start of a round each player is dealt 10 cards, the remaining 2 card are called the skat and are put facedown in the middle. The solo player is determined by a bidding phase. This is the part of the game that you will have to deal with in this challenge, more details on this below.

The player who wins the bidding phase becomes the solo player. He picks up the skat and then drops two cards (which may be the same, the other team does not know), picks the trump suit, and the round starts.

One round consists of ten tricks. The player who wins a trick leads the next one until all cards are played. I won't explain the rules here, but you should know that having a lot of trump cards is good. If you want to learn about the rules check the Wikipedia article which I linked at the start of this post, but it is not needed for this challenge.

The Challenge

You want to teach your two sons how to play skat. The rules are not that hard, so they quickly get into it. The only thing giving them a hard time is the bidding, specifically calculating the game value of their hand. So you decide to write a small program which outputs the maximum game value that they can bid given their current hand.

Calculating the game value

Every hand has a certain game value. It is determined by the amount of sequential Jacks you have and the suit which you want to pick as trump. Let's start with the first factor, the jacks!

The Jack Factor

Jacks are always trump cards, and they beat every other trump card. The order of strength between the four Jacks is:

  1. Jack of Clubs (highest)
  2. Jack of Spades
  3. Jack of Hearts
  4. Jack of Diamonds (lowest)

In the further explanation I will refer to them with the numbers I assigned to them here.

You remember that there is some kind of factor that you get from the Jacks in your hand which is parts of the game value? Great! Here is how you get it:

This Jack factor is the number of top Jacks (see order above) in sequence, plus 1. So if you have all 4 Jacks it is 4 + 1 = 5. If you have only the first 2 Jacks, it is 2 + 1 = 3.

Alternatively, to make things a bit more complicated, the Jack Factor can also be the number of top Jacks in sequence that you are missing, plus 1. So if you are missing the first one, it is 1 + 1 = 2. If you are missing he first 3, it is 3 + 1 = 4. Here some examples, using the numbering above:

[1, 4] -> 1 + 1 = 2
[1, 2, 4] -> 2 + 1 = 3
[2, 3, 4] -> 1 + 1 = 2
[1, 2, 3, 4] -> 4 + 1 = 5
[] -> 4 + 1 = 5

That was the first factor. Here is how you get the 2nd one:

The Trump Suit Factor

This one is a lot simpler. The 2nd factor is determined by the trump suit that the solo player picks using the following mapping:

Clubs    -> 12
Spades   -> 11
Hearts   -> 10
Diamonds ->  9

That was easy, wasn't it?

The Game Value

The game value is the product of the two factors. Pretty easy you think? Wrong! While the Jack-Factor is fixed, the suit-factor is not. The suit you end up picking as trump depends on the amount of trumps and the value of your non-trump cards in your hand. It would be way too complicated to explain what a good hand looks like, so you will use the following algorithm:

The Which-Trump-do-I-Pick Algorithm

You don't have to participate in the bidding. If you decide that your hand is too bad to play solo, you can just pass. Your hand must match the following criteria to be playable:

  • Have at least 6 trump cards (cards of the trump suit you pick + the number of Jacks). If this is possible for more than one suit, pick the one which would result in more trump cards. If there is still a tie, pick the suit with the highest rating given above.

  • Out of the non-trump cards, have at least 1 Ace.

If your hand does not match both of this criteria, you will pass. If it does, you will output the calculated game value and the chosen trump suit.

Short note: Of course this is a very simplified algorithm. There goes way too much strategy and experience into judging a hand than we could ever cover in a challenge like this.

Input

Every card has an unique identifier. The first part is the suit (Clubs, Spades, Hearts, Diamonds), the second part is the value which is given by this mapping:

Ace -> A
King -> K
Queen -> Q
Jack -> J
10 -> 0
9 -> 9
8 -> 8
7 -> 7

Both parts combined form one card. The value comes first, then comes the suit. You may take the cards in any format as you want.

Output

If the hand is playable, output the game value and the picked trump suit (order doesn't matter). If it's not, output "pass".

Rules

  • As mentioned you can take the input in the most convenient format for you. Examples see below in the test cases.
  • Input may be provided by command line arguments, user input, or function arguments.
  • Output may be provided as return value or may just be printed on the screen.
  • The cards in the input may not be ordered in any way. Your program has to be able to deal with any random card order.
  • Lowest byte-count wins!

Testcases

Input in the test cases will be a list of 2-char Strings.

1. ["JC", "JS", "JD", "AC", "KC", "9C", "AS", "7H", "QD", "8D"] -> 36 Clubs
2. ["JD", "AS", "0S", "KS", "QS", "9S", "8S", "AD", "8C", "9C"] -> 44 Spades
3. ["JH", "JD", "0S", "KS", "9C", "8C", "QH", "KH", "AD", "9D"] -> pass
4. ["JD", "AS", "KS", "QS", "0S", "9S", "8D", "7D", "0C", "QH"] -> pass

Explanation:

  1. Two Jacks in a row with Clubs as trump. So the game value is 3 x 12 = 36
  2. Three Jacks in a row missing with Spades as trump. So the game value is 4 x 11 = 44
  3. Only a maximum 4 of trump cards is possible, so you will pass.
  4. Six trump cards with Spades but no non-trump ace, so you will pass.

If some rules are unclear, go ahead and comment. I have grown up with this game, so it hard for me to judge if I described everything in enough detail.

And now... Happy Coding!

edit: As pointed out to me in the comments (thanks to isaacg), there is a rule which counts the following top trumps after the 4 Jacks into the "Jack-factor" so it could go up to 11. To keep this challenge simple and to not confuse people, the rules I proposed originally will stay as they are. So the maximum factor stays at 5.

Denker

Posted 2016-01-18T20:22:59.387

Reputation: 6 639

6Welcome to Programming Puzzles & Code Golf—excellent first challenge! :) – Doorknob – 2016-01-18T20:27:42.703

1

Should the number of straight jacks/missing jacks also include the top suit trumps in sequence? That's what wikipedia says here

– isaacg – 2016-01-18T21:54:04.053

@isaacg I have to admit that I didn't know about this rule until now. Thanks for pointing that out. I did some research and you are indeed right. In my family we do not play with this rule and I haven't met anyone who plays with it either. It does not have that high relevance tho, because when you have such a hand, you will most of the times play Grand which is counted different anyway. So for this challenge we will just stay with the rules I proposed. I will edit my post so it is clear to everyone. – Denker – 2016-01-18T22:16:16.443

1@DenkerAffe, I played Skat for many years in a club in Germany, and trust me, the rule is important, and there are cases where it is extremely relevant (and yes, it is unknown in most non-serious players). Especially with the missing side - imagine you have trump K, D, 9, 8, 7, and three A and two 10 in the other colors. Your Grand dies for sure, but you can play 'ohne 6' (collect some contra), and beat them, assuming you have an idea how the B are sitting from the bidding. And you can bid till the sun comes up with that card. – Aganju – 2016-01-19T02:53:27.193

@Aganju I already assumed that this rule is not know to most hobby-players. Thanks for the confirmation. I don't doubt that it is important but from my experience hands like this are pretty rare, so the rule does not come into play that often. – Denker – 2016-01-19T06:06:20.920

Answers

1

Python 2, example implementation

Since there are no submissions yet, I wrote down an example implementation in Python. The input format is the same as in the testcases in the challenge.

Maybe that motivates you guys to get going, it's not that hard :)

def gameValue(hand):
    jacks = ""
    suits = {"C" : 0, "S" : 0, "H" : 0, "D" : 0}
    # Loop through the hand, find all jacks and count the cards of each suit
    for card in hand:
        jacks += card[1] if "J" in card else ""
        suits[card[1]] += 1 if card[0] != "J" else 0

    # Map the Jacks to numbers while 1 is the highest (Clubs) then sort them ascending
    jacks =  sorted(map(lambda j: {"C" : 1, "S" : 2, "H" : 3, "D" : 4}[j], list(jacks)))

    # Sort the suits by amount. Highest amount and value is first after that
    suits = sorted(suits.items(), key = lambda suit: suit[1], reverse = True)
    trumpSuit = suits[0][0];
    # Amount of trumps is jack-count plus trumpsuit-count
    trumpCount = len(jacks) + suits[0][1];

    # Check for at least one ace that is no trump
    hasAce  = len(filter(lambda c: c[0] == "A" and c[1] != trumpSuit, hand)) >= 1

    # If the hand  is playable, calculate jack-factor and output the result, otherwise pass
    if trumpCount >= 6 and hasAce:
        # If there no jacks the factor is 5. If there are, find the first gap
        if len(jacks) > 0:
            lastJack = 0
            for jack in jacks:
                if jack - lastJack >= 2:
                    break
                lastJack = jack

            jackFactor = jacks[0] if lastJack == 0 else lastJack + 1
        else:
            jackFactor = 5

        trumpFactor = {"C" : 12, "S" : 11, "H" : 10, "D" : 9}[suits[0][0]]
        print str(trumpFactor * jackFactor) + " " + {12 : "Clubs", 11 : "Spades", 10 : "Hearts", 9 : "Diamonds"}[trumpFactor]
    else:
        print "pass"

Denker

Posted 2016-01-18T20:22:59.387

Reputation: 6 639

0

Java, 256 bytes

h->{int i,j=1,m=0,t,n=0,a[]=new int[8];for(var c:h){t=c[1]-48;if(c[0]==74){j+=1<<t;n++;}else{m+=i=c[0]==65?1:0;a[--t+4]+=i;a[t]++;}}for(i=t=0;i<4;i++)t=a[i]<a[t]?t:i;return a[t]+n<6|m-a[t+4]<1?"p":(t+++9)*(5-(int)(Math.log(j>7?~j&7:j)/Math.log(2)))+" "+t;}

Takes input as an array of character arrays in the format A4, where 4 is Clubs, 3 is Spades, 2 is Hearts and 1 is Diamonds. Output is 36 4 for a bid of 36 with trump suit Clubs, p for passing.

Try it online here.

Ungolfed version:

h -> { // lambda taking a char[][] as argument and returning a String
    int i,                // used as a loop variable and as a temporary variable
        j = 1,            // variable storing the jacks present in the hand in its four last-to-least significant bits
        m = 0,            // number of aces in the hand
        t,                // used as a temporary variable at first, later stores the trump suit
        n = 0,            // number of jacks in the hand
        a[] = new int[8]; // in the lower 4 indices, stores the number of non-jack cards present in the hand for each suit; in the higher 4 indices, stores the number of aces present in the hand for each suit (0 or 1)

    for(var c : h) {   // loop over all the cards in the hand
        t = c[1] - 48; // determine the suit of the current card; 48 is the ASCII code for '0'
        if(c[0] == 74) { // if it's a jack; 74 is the ASCII code for 'J'
            j += 1 << t; // set the corresponding bit
            n++;         // and increment the total number of jacks
        } else {                             // if it's not a jack
            m += (i = (c[0] == 65 ? 1 : 0)); // increment the total number of aces if it's an ace (65 is the ASCII code for 'A')
            a[ --t + 4] += i;                // increment the number of aces for this suit if it's an ace
            a[t]++;                          // increment the number of non-jack cards for this suit
        }
    }

    for(i = t = 0; i < 4; i++)     // loop over the suits ...
        t = (a[i] < a[t]) ? t : i; // ... and find the one with the most cards, giving priority to higher-valued suits in case of a tie

    return (a[t] + n < 6) |                                             // if there are less than 6 trump cards
           (m - a[t + 4] < 1) ?                                         // or less than 1 non-trump ace
           "p"                                                          // return "p" to pass on the hand
           :                                                            // else return
           ((t++ + 9) *                                                 // the value of the trump suit (and increment the trump suit for output later)
           (5 - (int) (Math.log((j > 7) ? (~j & 7) : j) / Math.log(2))) // times the jack factor
           + " " + t);                                                  // followed by the trump suit
}

O.O.Balance

Posted 2016-01-18T20:22:59.387

Reputation: 1 499

0

C, 235 bytes

f(char*h){int i,j=1,m=0,t,n=0,a[8]={0};for(;*h;h+=2){t=h[1]-48;if(*h-74){m+=i=*h==65;a[--t+4]+=i;a[t]++;}else{j+=1<<t;n++;}}for(i=t=0;i<4;i++)t=a[i]<a[t]?t:i;printf(a[t]+n<6|m-a[t+4]<1?"p":"%d %d",(t+9)*(5-(int)log2(j>7?~j&7:j)),t+1);}

Port of my Java answer.

Try it online here.

Takes input as an array of characters in the format A4, where 4 is Clubs, 3 is Spades, 2 is Hearts and 1 is Diamonds. Output is 36 4 for a bid of 36 with trump suit Clubs, p for passing.

Ungolfed version:

f(char* h) { // function taking an array of characters as argument (and implicitly returning an unused int)
    int i,          // used as a loop variable and as a temporary variable
        j = 1,      // variable storing the jacks present in the hand in its four last-to-least significant bits
        m = 0,      // number of aces in the hand
        t,          // used as a temporary variable at first, later stores the trump suit
        n = 0,      // number of jacks in the hand
        a[8] = {0}; // in the lower 4 indices, stores the number of non-jack cards present in the hand for each suit; in the higher 4 indices, stores the number of aces present in the hand for each suit (0 or 1); partially initialized to zero, the compiler will do the rest

    for(; *h; h += 2) { // loop over all the cards in the hand
        t = h[1] - 48;  // determine the suit of the current card; 48 is the ASCII code for '0'
        if(*h - 74) {              // if it's not a jack; 74 is the ASCII code for 'J'
            m += (i = (*h == 65)); // increment the total number of aces if it's an ace (65 is the ASCII code for 'A')
            a[ --t + 4] += i;      // increment the number of aces for this suit if it's an ace
            a[t]++;                // increment the number of non-jack cards for this suit
        } else {         // if it's a jack
            j += 1 << t; // set the corresponding bit
            n++;         // and increment the total number of jacks
        }
    }

    for(i = t = 0; i < 4; i++)   // loop over the suits ...
        t = a[i] < a[t] ? t : i; // ... and find the one with the most cards, giving priority to higher-valued suits in case of a tie

    printf( (a[t] + n) < 6 |                             // if there are less than 6 trump cards
            (m - a[t + 4] < 1) ?                         // or less than 1 non-trump ace
            "p" : "%d %d",                               // print "p" to pass on the hand, else print two numbers
            (t + 9) *                                    // first the value of the trump suit ...
            (5 - (int) log2((j > 7) ? (~j & 7) : j)),    // ... times the jack factor,
            t + 1                                     ); // followed by the trump suit
}

O.O.Balance

Posted 2016-01-18T20:22:59.387

Reputation: 1 499

226 bytes – ceilingcat – 2018-08-02T21:30:31.520