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

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
}