Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blackjack #2

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/todos.db
144 changes: 144 additions & 0 deletions blackjack/deck/card.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//go:generate stringer -type=Suit,Rank
package deck

import (
"fmt"
"math/rand"
"sort"
)

type Suit uint8
type Rank uint8

type option func([]Card) []Card

// Card represents a playing card
type Card struct {
Suit Suit
Rank Rank
}

const (
minRank = Ace
maxRank = King
)

const (
Spades Suit = iota
Diamonds
Clubs
Hearts
Joker
)

const (
Ace Rank = iota + 1
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Jack
Queen
King
)

// New produces a new deck of cards with options applied
func New(options ...option) []Card {
var cards []Card

for suit := 0; suit <= 3; suit++ {
for rank := minRank; rank <= maxRank; rank++ {
cards = append(cards, Card{
Suit: Suit(suit),
Rank: Rank(rank),
})
}
}
for _, opt := range options {
cards = opt(cards)
}
return cards
}

// WithSortingFunc is an option applied to produce
// a deck sorted according to provided function
func WithSortingFunc(less func(cards []Card) func(i, j int) bool) option {
return func(cards []Card) []Card {
sort.Slice(cards, less(cards))
return cards
}
}

// WithJokers is an option applied to produce
// a deck including a specified number of joker cards
func WithJokers(count int) option {
return func(cards []Card) []Card {
for i := 0; i < count; i++ {
cards = append(cards, Card{Suit: Joker, Rank: Rank(i)})
}
return cards
}
}

// WithShuffling is an option applied to produce
// a deck of shuffled cards
func WithShuffling(seed int64) option {
return func(card []Card) []Card {
rand.Seed(seed)
rand.Shuffle(len(card), func(i, j int) {
card[i], card[j] = card[j], card[i]
})
return card
}
}

// WithFilter is an option applied to produce
// a deck excluding cards of specified rank
func WithFilter(ranks ...Rank) option {
return func(cards []Card) []Card {
for _, rank := range ranks {
for idx, card := range cards {
if card.Rank == rank && card.Suit != Joker {
cards = append(cards[:idx], cards[idx+1:]...)
}
}
}
return cards
}
}

// WithDuplication produces a deck duplicated a specified number of times
func WithDuplication(count int) option {
return func(cards []Card) []Card {
finalDeck := cards
for i := 0; i < count; i++ {
finalDeck = append(finalDeck, cards...)
}
return finalDeck
}
}

// String uses the command stringer to output formatted representations of cards
func (c Card) String() string {
if c.Suit == Joker {
return c.Suit.String()
}

return fmt.Sprintf("%s of %s", c.Rank.String(), c.Suit.String())
}

// DefaultSort provides a default way to sort cards according to rank and suit
func DefaultSort(cards []Card) func(i, j int) bool {
return func(i, j int) bool {
return absRank(cards[i]) < absRank(cards[j])
}
}

func absRank(c Card) int {
return int(c.Suit)*int(maxRank) + int(c.Rank)
}
89 changes: 89 additions & 0 deletions blackjack/deck/card_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package deck

import (
"fmt"
"math/rand"
"testing"
"time"
)

func ExampleCard() {
fmt.Println(Card{Rank: Ace, Suit: Hearts})
fmt.Println(Card{Rank: Two, Suit: Spades})
fmt.Println(Card{Rank: Nine, Suit: Diamonds})
fmt.Println(Card{Rank: Jack, Suit: Clubs})
fmt.Println(Card{Suit: Joker})

// Output:
// Ace of Hearts
// Two of Spades
// Nine of Diamonds
// Jack of Clubs
// Joker
}

func TestNew(t *testing.T) {
cards := New()
// 13 ranks * 4 suits
if len(cards) != 13*4 {
t.Error("Wrong number of cards in a new deck.")
}
}

func TestDefaultSort(t *testing.T) {
cards := New(WithSortingFunc(DefaultSort))
want := Card{Rank: Ace, Suit: Spades}
if cards[0] != want {
t.Error("Expected Ace of Spades as first card. Received:", cards[0])
}
}

func TestWithJokers(t *testing.T) {
cards := New(WithJokers(4))
count := 0
for _, c := range cards {
if c.Suit == Joker {
count++
}
}
if count != 4 {
t.Error("Expected 4 Jokers, received:", count)
}
}

func TestWithShuffling(t *testing.T) {
seed := time.Now().Unix()

got := New(WithShuffling(seed))

want := New()
rand.Seed(seed)
rand.Shuffle(len(want), func(i, j int) { want[i], want[j] = want[j], want[i] })

for i := 0; i < len(want); i++ {
if want[i] != got[i] {
t.Errorf("Expected %v, got %v", want[i], got[i])
}
}
}

func TestWithDuplication(t *testing.T) {
cards := New(WithDuplication(3))

want := 13 * 4 * 4
if len(cards) != want {
t.Errorf("Expected %v card deck, got %v cards.", want, len(cards))
}
}

func TestWithFilter(t *testing.T) {
filterRank := Two

cards := New(WithFilter(filterRank))

for _, card := range cards {
if card.Rank == filterRank {
t.Errorf("Expected Rank %s to be filtered out, %s found in deck.", card.Rank, card.String())
}
}
}
57 changes: 57 additions & 0 deletions blackjack/deck/suit_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions blackjack/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/smelton01/deck

go 1.18
Empty file added blackjack/go.sum
Empty file.