# Was it a Superb Shuffle™️

According to this question a Superb Shuffle™️ is defined as a full deck of cards (including jokers) which follows this set of rules:

• No two cards (except Jokers) of the same suit are adjacent.
• No card (except Jokers) is adjacent to one of the same value.
• No card (except Jokers) is adjacent to one of an adjacent value (one higher or one lower in this order, A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A. Notice that Ace can not be adjacent to either a 2 or a King).
• The Jokers can be in any position.

The cards are expressed as a value(A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K), followed by a suit (C, D, H, S). For example 'AS' is the Ace of Spades. The Jokers are represented by a single letter J.

• Write some code to indicate if an array of cards is a Superb Shuffle™️ or not.
• Use any language you like.
• Attempt to do so in the smallest number of bytes.

Test cases:

1: This is Superb™️

[
"AS", "5D", "9H", "KC", "2D", "6H", "10C", "QS", "3H", "7C", "9S",
"KD", "4C", "6S", "10D", "AC", "3S", "7D", "JH", "J", "4D", "8H",
"QC", "AD", "5H", "9C", "JS", "2H", "6C", "8S", "QD", "3C", "5S",
"9D", "KH", "2S", "6D", "10H", "J", "3D", "7H", "JC", "KS", "4H",
"8C", "10S", "AH", "5C", "7S", "JD", "2C", "4S", "8D", "QH"
]

# true


2: This is all sorted

[
"AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "10S", "JS", "QS", "KS",
"AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "10D", "JD", "QD", "KD",
"AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H", "9H", "10H", "JH", "QH", "KH",
"AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "10C", "JC", "QC", "KC",
"J", "J"
]

# false


3: Aces together

[
"AC", "AS", "AD", "AH", "5D", "9H", "KC", "2D", "6H", "10C", "QS",
"9S", "KD", "4C", "6S", "10D", "3S", "7D", "JH", "J", "4D", "8H",
"QC", "5H", "9C", "JS", "2H", "6C", "8S", "QD", "3C", "5S", "3H",
"9D", "KH", "2S", "6D", "10H", "J", "3D", "7H", "JC", "KS", "4H",
"8C", "10S", "5C", "7S", "JD", "2C", "4S", "8D", "7C", "QH"
]

# false


4: Crazy 8's

[
"AS", "5D", "9H", "KC", "2D", "6H", "10C", "QS", "3H", "7C", "9S",
"KD", "4C", "6S", "10D", "AC", "3S", "7D", "JH", "J", "4D", "AH",
"QC", "AD", "5H", "9C", "JS", "2H", "6C", "QD", "3C", "5S", "10S",
"9D", "KH", "2S", "6D", "10H", "J", "3D", "7H", "JC", "KS", "4H",
"5C", "7S", "JD", "2C", "4S","QH", "8D", "8S", "8C", "8H"
]

# false


5: Also superb

[
"AS", "6H", "9S", "AC", "4D", "9C", "QD", "2S", "7H", "10S", "2C",
"5D", "10C", "KD", "3S", "8H", "JS", "3C", "6D", "JC", "AH", "4S",
"9H", "QS", "4C", "7D", "QC", "2H", "5S", "10H", "KS", "5C", "8D",
"KC", "3H", "6S", "JH", "AD", "6C", "9D", "J", "4H", "7S", "QH",
"2D", "7C", "10D", "J", "5H", "8S", "KH", "3D", "8C", "JD"
]

# true


6: Ace by 2

[
"AS", "2S", "6H", "9S", "AC", "4D", "9C", "QD",  "7H", "10S", "2C",
"5D", "10C", "KD", "3S", "8H", "JS", "3C", "6D", "JC", "AH", "4S",
"9H", "QS", "4C", "7D", "QC", "2H", "5S", "10H", "KS", "5C", "8D",
"KC", "3H", "6S", "JH", "AD", "6C", "9D", "J", "4H", "7S", "QH",
"2D", "7C", "10D", "J", "5H", "8S", "KH", "3D", "8C", "JD"
]

# false


7: Ace by King

[
"AS", "KH", "2S", "6H", "9S", "AC", "4D", "9C", "QD",  "7H", "10S",
"2C", "5D", "10C", "KD", "3S", "8H", "JS", "3C", "6D", "JC", "AH",
"4S", "9H", "QS", "4C", "7D", "QC", "2H", "5S", "10H", "KS", "5C",
"8D", "KC", "3H", "6S", "JH", "AD", "6C", "9D", "J", "4H", "7S",
"QH", "2D", "7C", "10D", "J", "5H", "8S", "3D", "8C", "JD"
]

# false


8: Jokers together

[
"AS", "5D", "9H", "KC", "2D", "6H", "10C", "QS", "3H", "7C", "9S",
"KD", "4C", "6S", "10D", "AC", "3S", "7D", "JH", "J", "J", "4D",
"8H", "QC", "AD", "5H", "9C", "JS", "2H", "6C", "8S", "QD", "3C",
"5S", "9D", "KH", "2S", "6D", "10H", "3D", "7H", "JC", "KS", "4H",
"8C", "10S", "AH", "5C", "7S", "JD", "2C", "4S", "8D", "QH"
]

# true


9: Adjacent suit/value separated by Jokers

[
"AS", "6H", "9S", "AC", "4D", "J", "4H", "2S", "7H", "10S", "2C",
"5D", "10C", "KD", "3S", "8H", "JS", "3C", "6D", "JC", "AH", "4S",
"9H", "QS", "4C", "7D", "QC", "2H", "5S", "10H", "KS", "5C", "8D",
"KC", "3H", "6S", "JH", "AD", "6C", "9D", "J", "QD", "7S", "QH",
"2D", "7C", "10D", "8C", "5H", "8S", "KH", "3D", "9C", "JD"
]

# true

• If any of the cards are missing, that is not a Superb Shuffle" - Boo-urns to input validation! Commented Jun 22, 2018 at 8:01
• I personally agree with @Arnauld and Shaggy. A challenge validating these two things of a shuffled 54-cards deck: 1. No adjacents of the same suit. 2. No adjacents of the same, one lower, or one higher values seems like a good challenge. If we should also validate: Are there exactly 54 cards (not too few or too many); are all cards in the correct format; are there no weird ASCII inputs; are there no empty strings; are there no duplicated cards; etc. etc. seems a overkill to me personally. Commented Jun 22, 2018 at 8:10
• @KevinCruijssen I missed that one Commented Jun 22, 2018 at 8:30
• Sequences such as 4D, J, 3D or J, JS may also result in false negatives. Commented Jun 22, 2018 at 9:36
• @Arnauld Ah, shit.. Had a solution that I was about to post, but it indeed fails for 4D, J, 3D.. Commented Jun 22, 2018 at 9:40

# JavaScript (ES7), 88 bytes

Returns true for Superb™️ or false for Ugly™️.

a=>a.every(r=s=([a,b,c])=>!s|!b|!((r-(r='34567891JQKA'.search(a)))**2%13<2|s==(s=c||b)))


Try it online!

### How?

Each card is split into 3 characters a, b and c, some of which might be undefined.

The rank is given by the position of a in the string "34567891JQKA" ('2' gives -1). The suit is given by b for all cards but 10's, for which we need to test c instead, and Jokers that have an undefined suit.

We keep track of the previous rank in r and the previous suit in s.

We use the expression $$\(r - new\_rank)^2 \bmod 13\$$ to detect invalid consecutive ranks. This gives:

• $$\0\$$ if both ranks are equal
• $$\1\$$ if the absolute difference between the ranks is $$\1\$$
• $$\1\$$ if we have an Ace followed by a Two or a Two followed by an Ace, because the squared difference is $$\144\$$ and we have $$\144 \equiv 1 \pmod{13}\$$.
• an integer greater than $$\1\$$ in all other cases

We simply compare the new suit with s to detect identical consecutive suits.

We use the expression !s | !b to detect that either the previous or the current card is a Joker, in which case the results of the other tests are discarded.

• Definitely Ugly™️ ;) Commented Jun 22, 2018 at 9:35

# Retina 0.8.2, 68 bytes

.*,(.*)
$1,$&
J\b
--
10
T
\b\w
$&$&
TAo2-9TJQKA\b.
(\w).{2,4}\1.*


Try it online! Link includes test cases. Outputs 0 if superb, 1 if not. Explanation:

.*,(.*)
$1,$&


Copy the last card to the beginning to simulate wrap-around.

J\b
--


Replace the jokers with two-character placeholders, which is enough to ensure that the surrounding cards aren't matched with each other.

10
T


And replace the 10s with tens.

\b\w
$&$&


Duplicate the rank of each card.

TAo2-9TJQKA\b.


(\w).{2,4}\1.*


Look for adjacent cards of the same suit or same or adjacent rank.

# Java 10, 246210205178 170 bytes

d->{var r=1>0;int p=-1,P=p,q,u;for(var c:d)r&=p<0|(u=(p-(p=(q=c.length())<2?-1:"A234567891JQK".indexOf(c.charAt(0))))%12)<-1|u>1&P!=(P=q<2?p:c.charAt(q>2?2:1));return r;}


Try it online.

Explanation:

d->{                   // Method with String-array parameter and boolean return-type
var r=1>0;           //  Result-boolean, starting at true
int p=-1,            //  Previous value, starting at -1
P=p,             //  Previous suit, starting at -1
q,u;             //  Temp integers
for(var c:d)         //  Loop over the cards of the input-deck:
r&=p<0             //   Validate whether the previous value is -1
|(u=(p-         //   Or if the difference between the previous and current value,
(p=          //   where the current value is:
//   (and replace the previous with the current value for the next
//   iteration at the same time)
(q=c.length())<2?
//    Is it a Joker:
-1      //     Use -1
:        //    Else:
"A234567891JQK".indexOf(c.charAt(0))
//     Use 0-12 depending on the order
))%12)         //   (Modulo-12 for 'A' and 'K')
<-1|u>1  //   is more than 2
&P!=            //   And the previous and current suits are not equal,
(P=         //   where the current suit is:
//   (and replace the previous with the current suit for the next
//   iteration at the same time)
q<2?     //    Is it a Joker:
p       //     Use -1
:        //    Else:
c.charAt(q>2?2:1));
//     Use the suit-character
//     where the q>2?2:1 is for cards of value 10
return r;}           //  Return if all validations inside the loop have succeeded


# Python 2, 108 bytes

lambda l:all('J'in(a,b)or(q(a[0])-q(b[0])+1)%13>2<a[-1]!=b[-1]for a,b in zip(l,l[1:]))
q='234567891JQK'.find


Try it online!

# Python 3, 109 bytes

lambda l:all([(q(a[0])-q(b[0])+1)%13*(A!=B)>2for(*a,A),(*b,B)in zip(l,l[1:])if a>[]<b])
q='234567891JQK'.find


Try it online!

Python 3 wins bytes with iterable unpacking but loses bytes by refusing to chain together comparisons of differently-typed items. The unpacking needs to handle both 'J' and '10S', which means it can either extract the first value or the last value, but not both.

# Ruby, 123 bytes

->a{a.each_cons(2).all?{|a,b|a==?J||b==?J||(s="A234567891JQK".chars).zip(s.rotate).all?{|f|((a.chars|b.chars)-[?0]-f)[2]}}}


Try it online!

### Explanation:

Without going too much into detail:

• split every pair into single characters
• remove '0' if any
• remove duplicate characters
• try to remove pairs of adjacent values

If after the treatment, 3 characters are left, the pair is good.

# Python 3, 130 125 bytes

5 bytes saved thanks to Chas Brown

v="A234567891JQK".find;s=lambda d:len(d)<2or("J"in d[:2]or 1<abs(v(d[0][0])-v(d[1][0]))<12and d[0][-1]!=d[1][-1])and s(d[1:])


My first post here, so this could probably be golfed down a little more.

### Explanation

Recursive lambda; checks if the first two cards of the current list are of different suits, are different ranks, and their ranks differ by more than 1 (but less than 12, to account for Ace-King), or if one of the two cards is a Joker, in which case it's fine, then consumes the first element of the list and recurses.

• Save 5 bytes by replacing v="A234567891JQK" with v="A234567891JQK".find; then you can use abs(v(d[0][0])-v(d[1][0]). Also, welcome to PPCG! Nice first answer. Commented Jun 23, 2018 at 4:36
• @ChasBrown Ah, good catch. Thanks! Commented Jun 23, 2018 at 18:06
• @nthistle Switching the order of Boolean logic saves a byte Commented Jul 12, 2018 at 20:15
• 124 bytes Commented Jul 12, 2018 at 20:16

# Ruby, 119 bytes

f=->s,c=20,d=?Z{a,*b=s;a ?(x=a[-1];x==?J||(e=((i="A234567891JQK".index(a[0]))-c)%13;e>1&&e<12)&&x!=d)&&f[b,i||20,x]:!p}


Try it online!

Recursive version.
It walks through the array and compares the current card to the last card. The rank is searched in the string "A234567891JQK" and jokers are skipped. It starts with a dummy previous card "20Z" which will accept any neighbour.

• If the 20Z card can accept any neighbour, aren’t it’s rules the same as the rules for Jokers? You could cut two bytes by making it a joker? Commented Jun 22, 2018 at 12:40
• Actually jokers are made to have rank 20 and color "J", so I could change the first ?Z to ?J but I'm not sure I could save bytes. Handling the edge case of having two consecutive jokers forces me to check explicitly for "J" Commented Jun 22, 2018 at 13:20

# Python 2, 122 bytes

lambda a:all((12>abs(R(c[0])-R(d[0]))>1and c[-1]!=d[-1])or'J'in c[-1]+d[-1]for c,d in zip(a,a[1:]))
R='A234567891JQK'.find


Try it online!