Add RNG seed parameter to NewDeck

This commit is contained in:
Trevor Slocum 2020-01-12 07:41:12 -08:00
parent 2c10757229
commit 96f4a21031
4 changed files with 105 additions and 66 deletions

View File

@ -1,5 +1,6 @@
0.1.1:
- Add Copy, Sort and Reverse methods to Cards
- Add RNG seed parameter to NewDeck
0.1.0:
- Initial release

View File

@ -5,6 +5,62 @@ import "sort"
// Cards is a slice of Cards.
type Cards []Card
// StandardCards is a slice of standard cards.
var StandardCards = Cards{
NewCard(FaceAce, SuitHearts),
NewCard(Face2, SuitHearts),
NewCard(Face3, SuitHearts),
NewCard(Face4, SuitHearts),
NewCard(Face5, SuitHearts),
NewCard(Face6, SuitHearts),
NewCard(Face7, SuitHearts),
NewCard(Face8, SuitHearts),
NewCard(Face9, SuitHearts),
NewCard(Face10, SuitHearts),
NewCard(FaceJack, SuitHearts),
NewCard(FaceQueen, SuitHearts),
NewCard(FaceKing, SuitHearts),
NewCard(FaceAce, SuitDiamonds),
NewCard(Face2, SuitDiamonds),
NewCard(Face3, SuitDiamonds),
NewCard(Face4, SuitDiamonds),
NewCard(Face5, SuitDiamonds),
NewCard(Face6, SuitDiamonds),
NewCard(Face7, SuitDiamonds),
NewCard(Face8, SuitDiamonds),
NewCard(Face9, SuitDiamonds),
NewCard(Face10, SuitDiamonds),
NewCard(FaceJack, SuitDiamonds),
NewCard(FaceQueen, SuitDiamonds),
NewCard(FaceKing, SuitDiamonds),
NewCard(FaceAce, SuitClubs),
NewCard(Face2, SuitClubs),
NewCard(Face3, SuitClubs),
NewCard(Face4, SuitClubs),
NewCard(Face5, SuitClubs),
NewCard(Face6, SuitClubs),
NewCard(Face7, SuitClubs),
NewCard(Face8, SuitClubs),
NewCard(Face9, SuitClubs),
NewCard(Face10, SuitClubs),
NewCard(FaceJack, SuitClubs),
NewCard(FaceQueen, SuitClubs),
NewCard(FaceKing, SuitClubs),
NewCard(FaceAce, SuitSpades),
NewCard(Face2, SuitSpades),
NewCard(Face3, SuitSpades),
NewCard(Face4, SuitSpades),
NewCard(Face5, SuitSpades),
NewCard(Face6, SuitSpades),
NewCard(Face7, SuitSpades),
NewCard(Face8, SuitSpades),
NewCard(Face9, SuitSpades),
NewCard(Face10, SuitSpades),
NewCard(FaceJack, SuitSpades),
NewCard(FaceQueen, SuitSpades),
NewCard(FaceKing, SuitSpades),
}
func (c Cards) Len() int {
return len(c)
}

62
deck.go
View File

@ -1,68 +1,42 @@
package cards
import (
"crypto/rand"
"errors"
"math/big"
"math/rand"
"time"
)
// ErrNotEnoughCards is the error returned when there aren't enough cards in
// the deck to complete an action.
var ErrNotEnoughCards = errors.New("not enough cards in deck")
// Deck defines a playing card deck containing any number of cards.
type Deck struct {
Cards Cards
r *rand.Rand
}
// StandardDeck initializes a standard deck of 52 cards.
func StandardDeck() *Deck {
var d Deck
for _, suit := range StandardSuits {
for _, face := range StandardFaces {
d.Cards = append(d.Cards, NewCard(face, suit))
}
// NewDeck initializes a deck of the supplied cards. A seed value of 0 is
// replaced with the current time in nanoseconds.
func NewDeck(c Cards, seed int64) *Deck {
if seed == 0 {
seed = time.Now().UnixNano()
}
return &d
return &Deck{Cards: c.Copy(), r: rand.New(rand.NewSource(seed))}
}
// Shuffle randomizes the deck.
func (d *Deck) Shuffle(times int) error {
if len(d.Cards) == 0 {
return ErrNotEnoughCards
func (d *Deck) Shuffle() {
for i := range d.Cards {
j := d.r.Intn(i + 1)
d.Cards[i], d.Cards[j] = d.Cards[j], d.Cards[i]
}
var old, shuffled Cards
for shuf := times; shuf > 0; shuf-- {
old = d.Cards
shuffled = nil
for i := len(old); i > 0; i-- {
nBig, e := rand.Int(rand.Reader, big.NewInt(int64(i)))
if e != nil {
panic(e)
}
n := int(nBig.Int64())
shuffled = append(shuffled, old[n])
old = old.Remove(n)
}
d.Cards = shuffled
}
return nil
}
// Draw removes cards from the deck and returns them as a slice.
func (d *Deck) Draw(count int) (cards Cards, err error) {
func (d *Deck) Draw(count int) (cards Cards, ok bool) {
if count > len(d.Cards) {
return nil, ErrNotEnoughCards
return nil, false
}
hand := d.Cards[0:count]
cards = d.Cards[0:count].Copy()
d.Cards = d.Cards[count:]
return hand, nil
return cards, true
}

View File

@ -4,8 +4,10 @@ import (
"testing"
)
var deckTestCase = []int{1, 2, 13}
func TestDeck(t *testing.T) {
d := StandardDeck()
d := NewDeck(StandardCards, 0)
if len(d.Cards) != 52 {
t.Errorf("expected 52 cards, got %d", len(d.Cards))
@ -25,32 +27,38 @@ func TestDeck(t *testing.T) {
}
}
var discard Cards
for i := 0; i < 52; i++ {
c, err := d.Draw(1)
if err != nil || len(c) == 0 {
t.Errorf("failed to draw card: %s", err)
for _, draw := range deckTestCase {
d = NewDeck(StandardCards, 0)
var discard Cards
l := len(d.Cards) / draw
for i := 0; i < l; i++ {
c, ok := d.Draw(draw)
if !ok {
t.Errorf("failed to draw %d iteration %d", draw, i)
} else if len(c) != draw {
t.Errorf("failed to draw %d iteration %d: expected %d cards, got %d", draw, i, draw, len(c))
}
discard = append(discard, c...)
}
discard = append(discard, c[0])
}
if len(d.Cards) != 0 {
t.Errorf("failed to draw %d: expected 0 cards after drawing, got %d", draw, len(d.Cards))
}
if len(d.Cards) != 0 {
t.Errorf("expected 0 cards after drawing, got %d", len(d.Cards))
}
for _, suit := range StandardSuits {
for _, face := range StandardFaces {
found := 0
for _, c := range discard {
if c.Equal(NewCard(face, suit)) {
found++
for _, suit := range StandardSuits {
for _, face := range StandardFaces {
found := 0
for _, c := range discard {
if c.Equal(NewCard(face, suit)) {
found++
}
}
if found != 1 {
t.Errorf("failed to draw %d: discard: expected 1 %s, got %d", draw, NewCard(face, suit), found)
}
}
if found != 1 {
t.Errorf("failed to draw 52 cards: expected 1 %s, got %d", NewCard(face, suit), found)
}
}
}
}