You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
4.7 KiB
Go
215 lines
4.7 KiB
Go
2 years ago
|
package deck
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math/rand"
|
||
|
"sort"
|
||
|
)
|
||
|
|
||
|
//go:generate stringer -type=Suit,Rank
|
||
|
|
||
|
// Suit represents the suit of a card:
|
||
|
// one of Spades, Diamonds, Clubs, Hearts,
|
||
|
// or Joker
|
||
|
type Suit uint8
|
||
|
|
||
|
const (
|
||
|
Spades Suit = iota
|
||
|
Diamonds
|
||
|
Clubs
|
||
|
Hearts
|
||
|
Joker
|
||
|
)
|
||
|
|
||
|
// Rank represents the rank of a card:
|
||
|
// Ace, Two... Ten, Jack... King
|
||
|
type Rank uint8
|
||
|
|
||
|
const (
|
||
|
_ Rank = iota
|
||
|
Ace
|
||
|
Two
|
||
|
Three
|
||
|
Four
|
||
|
Five
|
||
|
Six
|
||
|
Seven
|
||
|
Eight
|
||
|
Nine
|
||
|
Ten
|
||
|
Jack
|
||
|
Queen
|
||
|
King
|
||
|
)
|
||
|
|
||
|
// Card contains both a Suit and a Rank.
|
||
|
// In the special case that Suit=Joker,
|
||
|
// Rank is often not relevant.
|
||
|
type Card struct {
|
||
|
Suit Suit
|
||
|
Rank Rank
|
||
|
}
|
||
|
|
||
|
// Formats the Card type as a string
|
||
|
func (c Card) String() string {
|
||
|
if c.Suit == Joker {
|
||
|
return "Joker"
|
||
|
}
|
||
|
return fmt.Sprintf("%s of %s", c.Rank, c.Suit)
|
||
|
}
|
||
|
|
||
|
// Generates a new deck (slice of Cards) which contains
|
||
|
// a full playing card deck sorted by Suit with
|
||
|
// no jokers.
|
||
|
// The function accepts functional arguments to
|
||
|
// modify the deck during creation
|
||
|
func New(opts ...func([]Card) []Card) []Card {
|
||
|
deck := make([]Card, 52, 52)
|
||
|
i := 0
|
||
|
for s := 0; s < 4; s++ {
|
||
|
for v := 1; v < 14; v++ {
|
||
|
deck[i] = Card{Suit: Suit(s), Rank: Rank(v)}
|
||
|
i++
|
||
|
}
|
||
|
}
|
||
|
for _, opt := range opts {
|
||
|
deck = opt(deck)
|
||
|
}
|
||
|
return deck
|
||
|
}
|
||
|
|
||
|
func lessBySuit(c1 Card, c2 Card) bool {
|
||
|
if c1.Suit < c2.Suit {
|
||
|
return true
|
||
|
}
|
||
|
if c1.Suit == c2.Suit && c1.Rank < c2.Rank {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func lessByRank(c1 Card, c2 Card) bool {
|
||
|
if c1.Rank < c2.Rank {
|
||
|
return true
|
||
|
}
|
||
|
if c1.Rank == c2.Rank && c1.Suit < c2.Suit {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// Sorts the deck (slice of Cards) by Suit, then by Rank
|
||
|
func SortBySuit(deck []Card) []Card {
|
||
|
sort.Slice(deck, func(i, j int) bool {
|
||
|
return lessBySuit(deck[i], deck[j])
|
||
|
})
|
||
|
return deck
|
||
|
}
|
||
|
|
||
|
// Sorts the deck (slice of Cards) by Rank, then by Suit
|
||
|
func SortByRank(deck []Card) []Card {
|
||
|
sort.Slice(deck, func(i, j int) bool {
|
||
|
return lessByRank(deck[i], deck[j])
|
||
|
})
|
||
|
return deck
|
||
|
}
|
||
|
|
||
|
// This returns a closure that adds n joker cards to the end
|
||
|
// of a deck (slice of Cards). The returned function
|
||
|
// can be passed as an option into New()
|
||
|
func AddNJokers(n int) func(deck []Card) []Card {
|
||
|
return func(deck []Card) []Card {
|
||
|
toAdd := make([]Card, n, n)
|
||
|
for i, _ := range toAdd {
|
||
|
toAdd[i] = Card{Suit: Joker}
|
||
|
}
|
||
|
return append(deck, toAdd...)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Shuffle randomizes the order of cards in a slice
|
||
|
func Shuffle(deck []Card) []Card {
|
||
|
rand.Shuffle(len(deck), func(i, j int) {
|
||
|
deck[i], deck[j] = deck[j], deck[i]
|
||
|
})
|
||
|
return deck
|
||
|
}
|
||
|
|
||
|
// RemoveCards takes Card arguments, and returns a closure
|
||
|
// that will remove all Cards matching one of the arguments
|
||
|
// from the deck (slice of Cards).
|
||
|
// The returned function can be passed as an option into New()
|
||
|
func RemoveCards(cards ...Card) func([]Card) []Card {
|
||
|
return func(deck []Card) []Card {
|
||
|
output := make([]Card, 0, len(deck))
|
||
|
badKeys := make(map[Card]struct{})
|
||
|
for _, card := range cards {
|
||
|
badKeys[card] = struct{}{}
|
||
|
}
|
||
|
for _, card := range deck {
|
||
|
if _, ok := badKeys[card]; !ok {
|
||
|
output = append(output, card)
|
||
|
}
|
||
|
}
|
||
|
return output
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RemoveSuit takes Suit arguments, and returns a closure
|
||
|
// that will remove all Cards with a Suit equal to one of the
|
||
|
// arguments from the deck (slice of Cards).
|
||
|
// The returned function can be passed as an option into New()
|
||
|
func RemoveSuit(suits ...Suit) func([]Card) []Card {
|
||
|
return func(deck []Card) []Card {
|
||
|
output := make([]Card, 0, len(deck))
|
||
|
badKeys := make(map[Suit]struct{})
|
||
|
for _, suit := range suits {
|
||
|
badKeys[suit] = struct{}{}
|
||
|
}
|
||
|
for _, card := range deck {
|
||
|
if _, ok := badKeys[card.Suit]; !ok {
|
||
|
output = append(output, card)
|
||
|
}
|
||
|
}
|
||
|
return output
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RemoveRank takes Rank arguments, and returns a closure
|
||
|
// that will remove all Cards with a Rank equal to one of the
|
||
|
// arguments from the deck (slice of Cards).
|
||
|
// The returned function can be passed as an option into New()
|
||
|
func RemoveRank(ranks ...Rank) func([]Card) []Card {
|
||
|
return func(deck []Card) []Card {
|
||
|
output := make([]Card, 0, len(deck))
|
||
|
badKeys := make(map[Rank]struct{})
|
||
|
for _, rank := range ranks {
|
||
|
badKeys[rank] = struct{}{}
|
||
|
}
|
||
|
for _, card := range deck {
|
||
|
if _, ok := badKeys[card.Rank]; !ok {
|
||
|
output = append(output, card)
|
||
|
}
|
||
|
}
|
||
|
return output
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AddDeck appends a default deck to the deck passed in by the user
|
||
|
// (slice ef Cards), where the default deck is that created by New()
|
||
|
// with no options.
|
||
|
func AddDeck(deck []Card) []Card {
|
||
|
newDeck := New()
|
||
|
deck = append(deck, newDeck...)
|
||
|
return deck
|
||
|
}
|
||
|
|
||
|
// Draw takes the top card from deck and appends it to hand, returning the
|
||
|
// hand and the deck as they are after the card is moved.
|
||
|
func Draw(hand []Card, deck []Card) (newHand []Card, newDeck []Card) {
|
||
|
topCard := deck[0]
|
||
|
newHand = append(hand, topCard)
|
||
|
newDeck = deck[1:]
|
||
|
return newHand, newDeck
|
||
|
}
|