0

Basically, I have an array that is shuffled. The array is a deck of cards as such:

var rank = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
var suit = ["♠", "♥","♦","♣"]
var deck = [String]()

I have a for Loop to create the deck with

    for t in suit {
        for r in rank {
            deck.append("\(r)\(t)")
        }
    }

I then in a function call an extension which I created to shuffle the deck. (This brings me back 52 cards assorted randomly)

        deck.shuffle()

The only thing is while results are random, I don't want cards to repeat. For example, if the result is a 2♠, I wouldn't want 2♥, 2♦, 2♣ following in the printed list.

Any help is appreciated! Thanks!

C. Doe
  • 1
  • 4
  • Have you considered just shuffle these 52 again until there no repetition? There are `52! = 8.065 x 10^67` ways to shuffle a deck so your chance the number of times you have to shuffle is pretty small – Mike Henderson Feb 07 '17 at 02:33
  • Yes, although what happens is that a UILabel Prints what Deck.First is, and shuffles afterward. My only issue is that I never want the order printed following the shuffle to have repetition. – C. Doe Feb 07 '17 at 02:37
  • You don't really want the deck shuffled? – Mike Taverne Feb 07 '17 at 04:31
  • So you never want a card followed by another card of the same rank? – Mike Taverne Feb 07 '17 at 04:32
  • Yes, basically I want a shuffle, but for the results to not be of the same rank. thanks for the response! – C. Doe Feb 07 '17 at 04:50
  • Check out this answer http://stackoverflow.com/questions/39170398/is-there-a-way-to-shuffle-an-array-so-that-no-two-consecutive-values-are-the-sam/39172247#39172247 – Grimxn Feb 07 '17 at 08:22

1 Answers1

0

I think that the best way to proceed is to use a modified Knuth shuffle. The code below is a complete example. Just run it in a shell with swiftc -o customshuffle customshuffle.swift && ./customshuffle after saving the content in customshuffle.swift.

import Foundation

let decksize = 52


let rankStrings = ["A","2","3","4","5","6","7","8","9","10","J","Q","K"]
let suitStrings = ["♠", "♥","♦","♣"]

struct card : Hashable, Equatable, CustomStringConvertible {
    var rank: Int //1,2...,11,12,13
    var suit: Int // 1,2,3,4

    var hashValue: Int {
      return rank + suit
    }
    static func == (lhs: card, rhs: card) -> Bool {
        return lhs.rank == rhs.rank && lhs.suit == rhs.suit
    }

    var description: String {
      return rankStrings[self.rank - 1] + suitStrings[self.suit - 1]
    }
}

// seems like Swift still lacks a portable random number generator
func portablerand(_ max: Int)->Int {
      #if os(Linux)
            return Int(random() % (max + 1)) // biased but I am in a hurry
       #else
            return Int(arc4random_uniform(UInt32(max)))
      #endif
}

// we populate a data structure where the
// cards are partitioned by rank and then suit (this is not essential)

var deck = [[card]]()
for i in 1...13 {
    var thisset = [card]()
    for j in 1...4 {
        thisset.append(card(rank:i,suit:j))
    }
    deck.append(thisset)
}

// we write answer in "answer"
var answer = [card]()
// we pick a card at random, first card is special
var rnd = portablerand(decksize)
answer.append(deck[rnd / 4].remove(at: rnd % 4))

while answer.count < decksize {
  // no matter what, we do not want to repeat this rank
  let lastrank = answer.last!.rank
  var myindices = [Int](deck.indices)
  myindices.remove(at: lastrank - 1)
  var totalchoice = 0
  var maxbag = -1
  for i in myindices {
      totalchoice = totalchoice + deck[i].count
      if  maxbag == -1 || deck[i].count  > deck[maxbag].count {
        maxbag = i
      }
  }
  if 2 * deck[maxbag].count >= totalchoice {
    // we have to pick from maxbag
    rnd = portablerand(deck[maxbag].count)
    answer.append(deck[maxbag].remove(at: rnd))
  } else {
    // any bag will do
    rnd = portablerand(totalchoice)
    for i in myindices {
        if rnd >= deck[i].count {
          rnd = rnd - deck[i].count
        } else {
          answer.append(deck[i].remove(at: rnd))
          break
        }
    }
  }
}


for card in answer {
  print(card)
}

This can be computed fast and it is reasonably fair, but not unbiased sadly. I suspect it might be hard to generate a "fair shuffle" with your constraint that runs fast.

Daniel Lemire
  • 3,470
  • 2
  • 25
  • 23