Was it a Superb Shuffle™️

8

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 

AJFaraday

Posted 2018-06-22T07:43:26.840

Reputation: 10 466

2If any of the cards are missing, that is not a Superb Shuffle" - Boo-urns to input validation! – Shaggy – 2018-06-22T08:01:32.030

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. – Kevin Cruijssen – 2018-06-22T08:10:10.343

@KevinCruijssen I missed that one – AJFaraday – 2018-06-22T08:30:56.397

2Sequences such as 4D, J, 3D or J, JS may also result in false negatives. – Arnauld – 2018-06-22T09:36:23.693

@Arnauld Ah, shit.. Had a solution that I was about to post, but it indeed fails for 4D, J, 3D.. – Kevin Cruijssen – 2018-06-22T09:40:04.743

The TIO link of my Java answer contains some suggested test cases. Two truthy: one with 4D,J,4H and 9D,J,QD (same value or suit with Joker in between); and one with J,J (two adjacent Jokers). And two falsey: one with AS,2D and one with AS,KC (to test if Ace is both adjacent to 2s and Kings). (Already tested the existing Ruby and JS answers, and they are working correctly for these test cases.) – Kevin Cruijssen – 2018-06-22T12:01:48.993

@AdmBorkBork Thanks for letting me know – AJFaraday – 2018-06-22T12:29:43.400

@KevinCruijssen I've added them, thanks. – AJFaraday – 2018-06-22T12:29:53.117

I prefer to deal with a Superb Owl. (stupid joke primarily in the USA) – Carl Witthoft – 2018-06-22T17:25:21.590

@CarlWitthoft I like to think that a Superb Owl would be able to produce a Superb Shuffle. – AJFaraday – 2018-06-22T17:32:08.433

I'm not clear on what's actually guaranteed of the input. Is it always a permutation of the correct 54 cards? – xnor – 2018-06-23T04:05:49.183

@xnor that is the assumption, yes. I initially made validation of that part of the challenge, but there were some strong objections to that. – AJFaraday – 2018-06-23T07:03:27.253

Answers

9

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.

Arnauld

Posted 2018-06-22T07:43:26.840

Reputation: 111 334

2Definitely Ugly™️ ;) – AJFaraday – 2018-06-22T09:35:09.513

4

Retina 0.8.2, 68 bytes

.*,(.*)
$1,$&
J\b
--
10
T
\b\w
$&$&
T`Ao`2-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.

T`Ao`2-9TJQKA`\b.

Calculate the adjacent rank.

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

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

Neil

Posted 2018-06-22T07:43:26.840

Reputation: 95 035

3

Java 10, 246 210 205 178 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

Kevin Cruijssen

Posted 2018-06-22T07:43:26.840

Reputation: 67 575

3

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.

xnor

Posted 2018-06-22T07:43:26.840

Reputation: 115 687

2

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.

G B

Posted 2018-06-22T07:43:26.840

Reputation: 11 099

2

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.

nthistle

Posted 2018-06-22T07:43:26.840

Reputation: 81

1Save 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. – Chas Brown – 2018-06-23T04:36:27.220

@ChasBrown Ah, good catch. Thanks! – nthistle – 2018-06-23T18:06:10.747

@nthistle Switching the order of Boolean logic saves a byte – mbomb007 – 2018-07-12T20:15:58.717

124 bytes – mbomb007 – 2018-07-12T20:16:08.923

1

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.

crashoz

Posted 2018-06-22T07:43:26.840

Reputation: 611

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? – AJFaraday – 2018-06-22T12:40:20.777

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" – crashoz – 2018-06-22T13:20:27.277

1

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!

Chas Brown

Posted 2018-06-22T07:43:26.840

Reputation: 8 959