Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One-liner to generate Powerball picks in Swift?

Tags:

ruby

swift

With the U.S.'s large $1.5 Billion lottery this week, I wrote a function in Ruby to make Powerball picks. In Powerball, you choose 5 numbers from the range 1..69 (with no duplicates) and 1 number from the range 1..26.

This is what I came up with:

def pball
    Array(1..69).shuffle[0..4].sort + [rand(1..26)]
end

It works by creating an array of integers from 1 to 69, shuffling that array, choosing the first 5 numbers, sorting those, and finally adding on a number from 1 to 26.

To do this in Swift takes a bit more work since Swift doesn't have the built-in shuffle method on Array.

This was my attempt:

func pball() -> [Int] {
    let arr = Array(1...69).map{($0, drand48())}.sort{$0.1 < $1.1}.map{$0.0}[0...4].sort()
    return arr + [Int(arc4random_uniform(26) + 1)]
}

Since there is no shuffle method, it works by creating an [Int] with values in the range 1...69. It then uses map to create [(Int, Double)], an array of tuple pairs that contain the numbers and a random Double in the range 0.0 ..< 1.0. It then sorts this array using the Double values and uses a second map to return to [Int] and then uses the slice [0...4] to extract the first 5 numbers and sort() to sort them.

In the second line, it appends a number in the range 1...26. I tried adding this to the first line, but Swift gave the error:

Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions.

Can anyone suggest how to turn this into a 1-line function? Perhaps there is a better way to choose the 5 numbers from 1...69.

like image 796
vacawama Avatar asked Jan 16 '16 01:01

vacawama


2 Answers

Xcode 8.3 • Swift 3.1

import GameKit 

var powerballNumbers: [Int] {
    return (GKRandomSource.sharedRandom().arrayByShufflingObjects(in: Array(1...69)) as! [Int])[0..<5].sorted() + [Int(arc4random_uniform(26) + 1)]
}

powerballNumbers   // [5, 9, 62, 65, 69, 2]

Swift 2.x

import GameKit 

var powerballNumbers: [Int] {
    return (GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(Array(1...69)) as! [Int])[0...4].sort() + [Int(arc4random_uniform(26).successor())]
}

powerballNumbers   // [21, 37, 39, 42, 65, 23]
like image 175
Leo Dabus Avatar answered Sep 28 '22 09:09

Leo Dabus


I don't find the "one-liner" concept very compelling. Some languages lend themselves to it; others don't. I would suggest giving Swift a shuffle method to start with:

extension Array {
    mutating func shuffle () {
        for var i = self.count - 1; i != 0; i-- {
            let ix1 = i
            let ix2 = Int(arc4random_uniform(UInt32(i+1)))
            (self[ix1], self[ix2]) = (self[ix2], self[ix1])
        }
    }
}

But since I made this mutating, we still need more than one line to express the entire operation because we have to have a var reference to our starting array:

var arr = Array(1...69)
(1...4).forEach {_ in arr.shuffle()}
let result = Array(arr[0..<5]) + [Int(arc4random_uniform(26)) + 1]

If you really insist on the one-liner, and you don't count the code needed to implement shuffle, then you can do it, though less efficiently, by defining shuffle more like this:

extension Array {
    func shuffle () -> [Element] {
        var arr = self
        for var i = arr.count - 1; i != 0; i-- {
            let ix1 = i
            let ix2 = Int(arc4random_uniform(UInt32(i+1)))
            (arr[ix1], arr[ix2]) = (arr[ix2], arr[ix1])
        }
        return arr
    }
}

And here's your one-liner:

let result = Array(1...69).shuffle().shuffle().shuffle().shuffle()[0..<5] + [Int(arc4random_uniform(26)) + 1]

But oops, I omitted your sort. I don't see how to do that without getting the "too complex" error; to work around that, I had to split it into two lines:

var result = Array(1...69).shuffle().shuffle().shuffle().shuffle()[0..<5].sort(<)
result.append(Int(arc4random_uniform(26)) + 1)
like image 42
matt Avatar answered Sep 28 '22 07:09

matt