2

i have some question about swift 2 random. I have an enum sub class of all cards example:

enum CardName : Int{

case Card2Heart = 0,
Card2Diamond,
Card2Club,
Card2Spade,
Card3Heart..... }

I want to select 10 random cards on the didMoveToView

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Elidotnet
  • 291
  • 3
  • 18

5 Answers5

2

To get a unique, random set of numbers you can do the following...

Using the Fisher-Yates shuffle from here... How do I shuffle an array in Swift?

You can do...

var numbers = Array(0...51)

numbers.shuffleInPlace()

let uniqueSelection = numbers[0..<10]

or...

let uniqueSelection = Array(0...51).shuffleInPlace()[0..<10]

This will create a random, unique selection of 10 numbers (cards) from the array of 52 cards that you start with.

You can then iterate this array to get the enums or create an array of all enums to start from etc... There are lots of ways to use this.

Community
  • 1
  • 1
Fogmeister
  • 76,236
  • 42
  • 207
  • 306
  • Iam getting this error Value of type 'Array' has no member 'shuffleInPlace' on both options – Elidotnet Feb 22 '16 at 17:24
  • @Elidotnet Yeah. You'll need to use the code from the linked stack overflow question. Shuffle in place doesn't exist on Array. You have to add it yourself. That's what the other question does. – Fogmeister Feb 22 '16 at 17:27
2

In Swift 4.2 (coming with Xcode 10) the task will become much easier:

enum CardName: CaseIterable {
    case Card2Heart
    case Card2Diamond
    case Card2Club
    case Card2Spade
    case Card3Heart
    // ...
}

let randomCard = CardName.allCases.randomElement()
print(randomCard)

let randomCards10 = CardName.allCases.shuffled().prefix(10)
print(randomCards10)

Note there is no need for the enum to inherit from Int.

Sulthan
  • 128,090
  • 22
  • 218
  • 270
1

Following your last comment, here's a little, simplified example with the constraint of having to keep your enum for making the cards.

We need to include the extensions linked by Fogmeister:

extension MutableCollectionType where Index == Int {
    /// Shuffle the elements of `self` in-place.
    mutating func shuffleInPlace() {
        // empty and single-element collections don't shuffle
        if count < 2 { return }

        for i in 0..<count - 1 {
            let j = Int(arc4random_uniform(UInt32(count - i))) + i
            guard i != j else { continue }
            swap(&self[i], &self[j])
        }
    }
}

extension CollectionType {
    /// Return a copy of `self` with its elements shuffled
    func shuffle() -> [Generator.Element] {
        var list = Array(self)
        list.shuffleInPlace()
        return list
    }
}

These extensions will allow us to shuffle an array of values.

Which array?

There's many ways, but the simplest option is probably to make an array of indices, which are simple integers (replace 52 with the actual number of cards in your enum):

Array(1...52) // [1, 2, 3, ... , 52]

We shuffle it:

Array(1...52).shuffle() // [33, 42, 7, ...]

Now we have an array of randomized indices. Let's make cards from this with your enum:

Array(0...51).shuffle().flatMap({ CardName(rawValue: $0) })

This is it, we have an array of cards in a random order:

let shuffledDeck = Array(0...51).shuffle().flatMap({ CardName(rawValue: $0) }) // [Card3Heart, Card2Diamond, ...]

and we can take cards from it:

func takeCardsFromDeck(number: Int) -> [CardName] {
    if shuffledDeck.count > number {
        let cards = Array(shuffledDeck[0..<number])
        shuffledDeck.removeRange(0..<number)
        return cards
    }
    return []
}

let tenRandomCards = takeCards(10)

Of course we need to remove from the deck the cards we've dealt, that way each card you draw is unique: we're using removeRange for that.

This example was kept simple on purpose: you still have to verify that there's enough cards in the deck before drawing, and lots of unsuspected other complexities. But it's so fun. ;)

If you want, you can search for additional inspiration in my implementation of these models and others (Deck, Dealer, Player, etc) in my PokerHands repository (MIT Licenced) on GitHub.

Swift 4.2

No need for these extensions anymore, we can use the .shuffle() and .shuffled() methods provided by Swift. Just remove the extensions, and rename the methods: the equivalent of our old "shuffleInPlace" is now .shuffle() and the equivalent of our old "shuffle" is now .shuffled().

Note: see Sulthan's answer for an even better solution using Swift 4.2.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
  • 1
    It is worth mentioning, for completeness sake, that this may add the same card multiple times. – Losiowaty Feb 22 '16 at 14:02
  • 1
    Of course. :) You're right, I've added a note to my answer. – Eric Aya Feb 22 '16 at 14:06
  • Hi thanks eric :-) i forget to mention the uniqe issue as well sorry. I need the 10 numbers will be unique and without the repeater option – Elidotnet Feb 22 '16 at 14:23
  • Then drawing cards from an enum is not the right approach IMO. I suggest using structs instead. May I suggest you have a look at my [PokerHands](https://github.com/ericdke/PokerHands/tree/master/SwiftyPokerHands) repository? You could study how I've addressed this matter with structs in the [Card](https://github.com/ericdke/PokerHands/blob/master/SwiftyPokerHands/SPHModelCard.swift) and [Dealer](https://github.com/ericdke/PokerHands/blob/master/SwiftyPokerHands/SPHModelDealer.swift) implementations (along with Deck, Player, etc). – Eric Aya Feb 22 '16 at 14:25
  • Hi eric thanks again i belive using struct is more approach but this is a simple learning i m very new to swift. in this case i need to get the enum results. if you have any idea how to achieve that i will be glad – Elidotnet Feb 22 '16 at 17:31
0

Here is the shuffleInPlace() code that you are missing;

extension MutableCollectionType where Index == Int {
mutating func shuffleInPlace() {
    if count < 2 { return }

    for i in 0..<count - 1 {
        let j = Int(arc4random_uniform(UInt32(count - i))) + i
        guard i != j else { continue }
        swap(&self[i], &self[j])
    }
  }
}
Rappe
  • 5
  • 4
0

how to randomly spread enum values set

import Darwin // arc4random_uniform

enum E:Int {
    case E1, E2, E3, E4, E5, E6, E7, E8, E9, E10
    static var set:[E] { return (E.E1.rawValue...E.E10.rawValue).flatMap { E(rawValue: $0) }}
}

func spread(i:Int = 0, arr:[E])->([E],[E]) {
    var i = i == 0 ? arr.count : i
    var e:[E] = []
    var arr = arr
    while  i > 0 && arr.count > 0 {
        let idx = Int(arc4random_uniform(UInt32(arr.count-1)))
        e.append(arr.removeAtIndex(idx))
        i -= 1
    }
    return (e,arr)
}

let e1 = spread(3, arr: E.set)
let e2 = spread(2, arr: e1.1)
// ... spread the rest
let e3 = spread(arr: e2.1)

print(e1, e2, e3, separator:"\n")
/*
([E.E8, E.E6, E.E4], [E.E1, E.E2, E.E3, E.E5, E.E7, E.E9, E.E10])
([E.E1, E.E7], [E.E2, E.E3, E.E5, E.E9, E.E10])
([E.E5, E.E3, E.E2, E.E9, E.E10], [])
*/
user3441734
  • 16,722
  • 2
  • 40
  • 59