// My Golang was rusty, so I thought I’d try this little Golang // programming exercise. // _whiteCaps_: "Golang map iteration is returned in random order // specifically to make sure that people don't rely on the order." // MereInterest: "Sounds like a fast and idiomatic way to shuffle a // deck of cards is then to convert to a map and back." // --- https://news.ycombinator.com/item?id=22268676 // I haven’t validated the speed claim, but it doesn’t seem random // enough. Here’s a non-cherry-picked sample output: // Bad // ♠9 ♠J ♠K ♡Q ♢9 ♠4 ♠5 ♠10 ♡J ♠8 ♡6 ♢A ♢2 // ♢4 ♢Q ♣6 ♣8 ♣J ♡3 ♡9 ♢K ♣3 ♣Q ♠A ♠2 ♠3 // ♠6 ♠Q ♡4 ♡10 ♢8 ♢10 ♣4 ♣K ♠7 ♡7 ♢5 ♢6 ♢7 // ♣7 ♡2 ♡K ♢3 ♢J ♣2 ♣5 ♣9 ♡A ♡5 ♡8 ♣A ♣10 // On average you would expect about one card in a shuffled deck to be // (cyclically) followed in the deck by its succeeding card, and on // about one out of 50 shuffles, that card would be followed by its // successor. Here we have ♠4 ♠5, ♢A ♢2, ♠A ♠2 ♠3, and ♢5 ♢6 ♢7. The // next shuffle delivered this: // Bad // ♠10 ♡6 ♢A ♢10 ♣9 ♠K ♡3 ♡10 ♢Q ♣7 ♣Q ♠2 ♠4 // ♡9 ♢4 ♢8 ♣K ♢6 ♣6 ♣10 ♠A ♠8 ♡2 ♡8 ♡Q ♢5 // ♣4 ♣8 ♠5 ♠J ♠Q ♡A ♡7 ♣2 ♣5 ♣J ♠6 ♠7 ♢3 // ♢J ♢K ♣A ♡4 ♡5 ♡K ♢7 ♢9 ♠3 ♠9 ♡J ♢2 ♣3 // This has ♡4 ♡5, ♠J ♠Q, and ♠6 ♠7; while not as spectacular as the // previous case, I think we can conclude that this shuffling method // has a heavy bias toward putting consecutive cards in consecutive // order. // Among the things I learned: // - I can’t define methods on an ordinary slice type, but I can on a // type alias to it. // - I can rely on named slice return values being initialized to nil // so I can append to them. // - I can enumerate “enum types” like these with ++. // - I can’t printf runes with "%s", so I have to string() them. // - I can initialize Card objects positionally. // - Golang map iteration order is more random than I expected, but // still not random enough for shuffling a deck of cards. package main import ( "fmt" "math/rand" "os" ) type Suit int const ( Spades Suit = iota Hearts Diamonds Clubs ) func (suit Suit) String() string { return string([]rune("♠♡♢♣")[suit]) } type Rank int const ( Jack Rank = iota + 11 Queen King ) var named_ranks = map[Rank]string{ 1: "A", Jack: "J", Queen: "Q", King: "K", } func (rank Rank) String() string { val, ok := named_ranks[rank] if ok { return val } return fmt.Sprintf("%d", rank) } type Card struct { Suit Suit Rank Rank } func (card Card) String() string { return fmt.Sprintf("%s%s", card.Suit, card.Rank) } type Deck []Card func Sorted() (deck Deck) { for suit := Spades; suit <= Clubs; suit++ { for rank := Rank(1); rank <= King; rank++ { deck = append(deck, Card{suit, rank}) } } return } // Shuffle in the miserable way suggested in the comment on the orange // website. func Badshuffle(deck Deck) (rv Deck) { shuffled := map[Card]bool{} for _, card := range deck { shuffled[card] = true } for card, _ := range shuffled { rv = append(rv, card) } return } // Shuffle using rand.Perm(). func Libshuffle(deck Deck) (shuffled Deck) { for _, i := range rand.Perm(len(deck)) { shuffled = append(shuffled, deck[i]) } return } // Use the Fisher–Yates shuffle explicitly. func FisherYates(deck Deck) (shuffled Deck) { shuffled = append(shuffled, deck...) for i := range shuffled { j := i + rand.Int()%(len(shuffled)-i) shuffled[i], shuffled[j] = shuffled[j], shuffled[i] } return } func (deck Deck) Show() { for i, card := range deck { sep := " " if i%13 == 12 { sep = "\n" } fmt.Printf("%3s%s", card, sep) } } func main() { rand.Seed(int64(os.Getpid())) deck := Sorted() fmt.Printf("Unshuffled\n") deck.Show() // Map iteration order doesn’t produce a rotation of the same // order as expected, even during the same run. But it usually // produces multiple sequences of two cards in sorted order, and // often even three or four. for i := 0; i < 2; i++ { fmt.Printf("Bad\n") Badshuffle(deck).Show() } fmt.Printf("rand.Perm\n") Libshuffle(deck).Show() fmt.Printf("Fisher–Yates\n") FisherYates(deck).Show() }