Init
commit
9cfa4b826c
@ -0,0 +1,214 @@
|
|||||||
|
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
|
||||||
|
}
|
@ -0,0 +1,129 @@
|
|||||||
|
package deck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func showDeck(d []Card) {
|
||||||
|
fmt.Println("Showing whole deck")
|
||||||
|
for _, card := range d {
|
||||||
|
fmt.Println(card)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintCard(t *testing.T) {
|
||||||
|
tests := []struct{
|
||||||
|
input Card
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{Card{Hearts, Queen}, "Queen of Hearts"},
|
||||||
|
{Card{Suit: Joker}, "Joker"},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if test.input.String() != test.want {
|
||||||
|
t.Errorf("Got %s; wanted %s", test.input.String(), test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
d := New()
|
||||||
|
c := Card{Diamonds, Six}
|
||||||
|
if d[18] != c {
|
||||||
|
t.Errorf("Deck's 5th card should be %s, is %s", c, d[18])
|
||||||
|
showDeck(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortByRank(t *testing.T) {
|
||||||
|
d := New(SortByRank)
|
||||||
|
c := Card{Clubs, Five}
|
||||||
|
if d[18] != c {
|
||||||
|
t.Errorf("Deck's 18th card should be %s, is %s", c, d[18])
|
||||||
|
showDeck(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSortBySuit(t *testing.T) {
|
||||||
|
d := New()
|
||||||
|
d = SortByRank(d)
|
||||||
|
d = SortBySuit(d)
|
||||||
|
c := Card{Diamonds, Six}
|
||||||
|
if d[18] != c {
|
||||||
|
t.Errorf("Deck's 5th card should be %s, is %s", c, d[18])
|
||||||
|
showDeck(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddJokers(t *testing.T) {
|
||||||
|
d := New()
|
||||||
|
initLength := len(d)
|
||||||
|
d = AddNJokers(17)(d)
|
||||||
|
if initLength + 17 != len(d) {
|
||||||
|
t.Errorf("Wrong number of cards in deck after adding Jokers")
|
||||||
|
showDeck(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveCard(t *testing.T) {
|
||||||
|
d := New(RemoveCards(Card{Spades, Ace}))
|
||||||
|
for _, card := range d {
|
||||||
|
if card == (Card{Spades, Ace}) {
|
||||||
|
t.Error("The ace of spades is still in the deck after calling RemoveCard(Card{Spades, Ace})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
before := len(d)
|
||||||
|
d = RemoveCards(Card{Clubs, Jack})(d)
|
||||||
|
if len(d) != (before - 1) {
|
||||||
|
t.Error("Remove() didn't reduce card count by 1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRank(t *testing.T) {
|
||||||
|
d := New(RemoveRank(Seven))
|
||||||
|
for _, card := range d {
|
||||||
|
if card.Rank == Seven {
|
||||||
|
t.Error("There are still Sevens in the deck after calling RemoveRank(7)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSuit(t *testing.T) {
|
||||||
|
d := New(RemoveSuit(Hearts))
|
||||||
|
for _, card := range d {
|
||||||
|
if card.Suit == Hearts {
|
||||||
|
t.Error("There are still Hearts in the deck after calling RemoveSuit(Hearts)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddDeck(t *testing.T) {
|
||||||
|
d := New()
|
||||||
|
before := len(d)
|
||||||
|
d = AddDeck(d)
|
||||||
|
if len(d) != before*2 {
|
||||||
|
t.Error("AddDeck didn't double default deck size")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDraw(t *testing.T) {
|
||||||
|
d := New()
|
||||||
|
lenBefore := len(d)
|
||||||
|
firstTwo := d[0:2]
|
||||||
|
hand := make([]Card, 0, 5)
|
||||||
|
hand, d = Draw(hand, d)
|
||||||
|
hand, d = Draw(hand, d)
|
||||||
|
if firstTwo[0] != hand[0] {
|
||||||
|
t.Errorf("The first drawn card (got %s) should be the first in the deck (was %s)\n",
|
||||||
|
hand[0], firstTwo[0])
|
||||||
|
}
|
||||||
|
if firstTwo[1] != hand[1] {
|
||||||
|
t.Errorf("The second drawn card (got %s) should be the second in the deck (was %s)\n",
|
||||||
|
hand[0], firstTwo[0])
|
||||||
|
}
|
||||||
|
if len(d) != lenBefore - 2 {
|
||||||
|
t.Errorf(`Expected length of deck to be reduced by two when calling Draw() twice,
|
||||||
|
length before is %d, and length after is %d`, lenBefore, len(d))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// Code generated by "stringer -type=Suit,Rank"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package deck
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Spades-0]
|
||||||
|
_ = x[Diamonds-1]
|
||||||
|
_ = x[Clubs-2]
|
||||||
|
_ = x[Hearts-3]
|
||||||
|
_ = x[Joker-4]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Suit_name = "SpadesDiamondsClubsHeartsJoker"
|
||||||
|
|
||||||
|
var _Suit_index = [...]uint8{0, 6, 14, 19, 25, 30}
|
||||||
|
|
||||||
|
func (i Suit) String() string {
|
||||||
|
if i >= Suit(len(_Suit_index)-1) {
|
||||||
|
return "Suit(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Suit_name[_Suit_index[i]:_Suit_index[i+1]]
|
||||||
|
}
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[Ace-1]
|
||||||
|
_ = x[Two-2]
|
||||||
|
_ = x[Three-3]
|
||||||
|
_ = x[Four-4]
|
||||||
|
_ = x[Five-5]
|
||||||
|
_ = x[Six-6]
|
||||||
|
_ = x[Seven-7]
|
||||||
|
_ = x[Eight-8]
|
||||||
|
_ = x[Nine-9]
|
||||||
|
_ = x[Ten-10]
|
||||||
|
_ = x[Jack-11]
|
||||||
|
_ = x[Queen-12]
|
||||||
|
_ = x[King-13]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _Rank_name = "AceTwoThreeFourFiveSixSevenEightNineTenJackQueenKing"
|
||||||
|
|
||||||
|
var _Rank_index = [...]uint8{0, 3, 6, 11, 15, 19, 22, 27, 32, 36, 39, 43, 48, 52}
|
||||||
|
|
||||||
|
func (i Rank) String() string {
|
||||||
|
i -= 1
|
||||||
|
if i >= Rank(len(_Rank_index)-1) {
|
||||||
|
return "Rank(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||||
|
}
|
||||||
|
return _Rank_name[_Rank_index[i]:_Rank_index[i+1]]
|
||||||
|
}
|
Loading…
Reference in New Issue