# Check if hand from card game "Rooster" is valid for the second stage

Here in Russia, we have a little-known card game with the name, when translated directly, means Rooster.

The cards used are two full 54-card decks of playing cards from 2 through aces, with jokers. The game for each player divides into two stages. In the first stage, each player has 14 cards dealt, and with those cards has to get 51 or more points with card combinations.

## Rules:

1.Each combination has to be at least three cards long, with no cards repeating, and can be either:

• A combination of cards of the same value (not points), for example:
        Ace of spades, ace of hearts, ace of clubs
Six of hearts, six of clubs, six of diamonds, six of spades

• A combination of cards that are in a row, like a straight in poker, examples:
        Jack of clubs, queen of clubs, king of spades, ace of hearts
Seven of spades, eight of diamonds, nine of clubs
Ace of diamonds, two of clubs, three of hearts, four of diamonds, five of spades

1. Your hand score is calculated by the sum of points in every card combination according to the chart below:
2-10: according to the cards face value for each card (For example, Three of spades is 3 points)
Jack,Queen,King: 10 points for each card
Ace: 1 point if used before a Two, otherwise 11 points for each card
Joker: 0 points

1. Jokers can substitute any card in a combination, but cannot be used more than once in each combination. The cards they substitute cannot be used again in the same combination.

2. Cards cannot be used twice. If you used a card, you can't then use it in a different combination

3. But remember, since the deck is two 54-card decks, duplicates of one card are possible, and, if duplicates are in a hand, using one card will not forbid you from using the other duplicate.

Taking a hand of 14 cards, output one of the possible things:

1. A truthy value if the cards given can form combinations in such a way that your hand score is more or equal than 51
2. A falsy value if your hand does not reach the score of 51

## Input:

Each card is given in a format VS, where:

1. V is the face value. Possible values: [2,3,4,5,6,7,8,9,T,J,Q,K,A]
2. S is the suit of a card. Possible values: [S,H,D,C,J] A joker will be represented as JJ

Your input may be in any form you wish, as long as each card values are not separated. For example:

1. A list of cards. (['TS','3H','JJ','9C',etc...])
2. Cards separated from each other with a whitespace. (TS 3H JJ 9C etc...)

## Output:

Either:

1. A Truthy/Falsy
2. A 1/0

This is codegolf, so lowest bytecount for each language wins! I hope the rules are clear, and good luck!

edit 1: got mixed up in terminology

• That looks like a nice challenge, but it would be great to add some test cases. Apr 10, 2020 at 19:36
• @Arnauld, currently working on them)
– Dion
Apr 10, 2020 at 19:37
• May a Joker be used as, say, a 5th Ace? Apr 10, 2020 at 20:04
• @Arnauld No, will clarify that substituted joker cards are still counted and can't be used in one combination
– Dion
Apr 10, 2020 at 20:06
• FWIW, this seems similar to Rami in French, or Rummy in English. Apr 11, 2020 at 8:35

# Ruby, 415 376 bytes

->a{s=[*2..9]+*4+[11,1]
g=->d,j{x=0
65.times{|r|v=r/5;r%=5
p=w=0
c=d*1
y=[r,c[v]/5,c[v]%5].min
z=v+1+r
r<3?z<13?(v-1).upto(z){|n|p+=s[n];c[n]>4?c[n]-=5:w+=1}:w=2:r>y+1?w=2:(p+=s[v]*r;c[v]-=y*5;w=r-y)
w>1||w>j||(x=[p+g[c,j-w],x].max)}
x}
b=(0..12).map{[]}
a.map{|i|b["23456789TJQKA".index i]<<i}
h=b.size-(b-=[?J]).size
b.map!{|i|i.size*5+(i&i).size}
g[b,h]>50}


Try it online!

Revised version, using recursive search tree instead of brute force search. This version runs to completion on TIO.

The TIO generates random test cases. It is interesting to modify the x>50 to simply x to see the raw scores achieved. Sometimes the way to achieve the best score is not obvious and it can take a moment's scribbling to work out manually how the computer got as high a score as it did.

Explanation

Cards are taken as an unsorted array a and the suit letters are parsed into 13-element array b, one element for each card value. After jokers have been removed and counted separately, the total number of cards and number of distinct suits are stored as a single number 5*total + distinct suits. Distinct suits are only important when dealing with n of a kind type groups: the number of available cards is max(total cards, distinct suits) plus jokers.

Searching is done recursively by "nested" function g. There are 5 different types of groups of cards to search for, as below. Note that the best use of 34567 would be a 5 straight but if a second 5 is available the best use would be two separate 3 straights 345 and 567, so greedily assigning to the largest possible group is not always the best strategy. Instead we look for groups separately. Straights of 6 and over will be found as a combination of shorter straights.

Type          corresponding value of r%5
3 straight    0
4 straight    1
5 straight    2
3 of a kind   3
4 of a kind   4


There are 13 possible starting values for each type, so the total number of distinct possible groups to search for is 13*5=65 (some straights are not possible with the highest starting values.)

When function g finds a group, it passes the remaining cards on recursively to a new instance of itself, then compares the score of the group it found (plus any additional groups found by recursion) with the highest score found so far in variable x, and updates x if necessary.

Return value of recursive function g is the highest score found. Return value of outer function f is true or false from g>50.

Commented code

->a{s=[*2..9]+*4+[11,1]             #14-element array of card scores. Final element is ace low, accessed as s[-1]
g=->d,j{                                #function g takes an array of card data, and a joker count as input
x=0                                     #largest score found so far is 0
65.times{|r|v=r/5;r%=5                  #iterate through 13 possible card values and 5 possible group types
p=w=0                                   #p=points for this iteration;w=wildcards required
c=d*1                                   #make a copy of d for this iteration
y=[r,c[v]/5,c[v]%5].min                 #y=number of cards removed by successful n of a kind. [cards required,cards available,distinct suits].min
z=v+1+r                                 #z=top value of straight
r<3?                                    #if r<3 consider a straight
z<13?(v-1).upto(z){|n|               #if z is in range (no more than ace high) iterate through cards
p+=s[n];c[n]>4?c[n]-=5:w+=1}:      #add score to p. If card available, remove from c, else increment wildcards required
w=2:                                 #if z out of range set wildcards to 2 to indicate failure
r>y+1?w=2:                           #if r>2 consider a straight. If insufficient cards for r with 1 joker, set w to 2 to indicate failure.
(p+=s[v]*r;c[v]-=y*5;w=r-y)          #add score to p, remove cards from c, set wildcards required
w>1||w>j||(x=[p+g[c,j-w],x].max)}       #if wildcards required no more than 1 or jokers available, take the score, sum with results of further recursion and compare with x
x}                                      #return x, the highest score of all 65 iterations
b=(0..12).map{[]}                       #make a 13 element array to store totals of cards
a.map{|i|b["23456789TJQKA".index i]<<i}   #add the suit letter for each card to b
h=b.size-(b-=[?J]).size           #any "jacks" of suit J are actually jokers. count jokers into h and remove from b.
b.map!{|i|i.size*5+(i&i).size}          #collate the suit letters into a single number: total cards*5 + number of distinct suits
g[b,h]>50}                              #call g to find highest score. return truthy if over 50, else falsy.