130 lines
2.6 KiB
Go
130 lines
2.6 KiB
Go
package game
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
"strings"
|
|
)
|
|
|
|
// Suit خال کارت.
|
|
type Suit uint8
|
|
|
|
const (
|
|
Spades Suit = iota
|
|
Hearts
|
|
Diamonds
|
|
Clubs
|
|
)
|
|
|
|
var suitChar = [4]byte{'S', 'H', 'D', 'C'}
|
|
var suitName = [4]string{"spades", "hearts", "diamonds", "clubs"}
|
|
|
|
func (s Suit) String() string { return suitName[s] }
|
|
|
|
// ParseSuit نام انگلیسی خال را به Suit تبدیل میکند.
|
|
func ParseSuit(name string) (Suit, error) {
|
|
switch strings.ToLower(name) {
|
|
case "spades", "s":
|
|
return Spades, nil
|
|
case "hearts", "h":
|
|
return Hearts, nil
|
|
case "diamonds", "d":
|
|
return Diamonds, nil
|
|
case "clubs", "c":
|
|
return Clubs, nil
|
|
}
|
|
return 0, fmt.Errorf("invalid suit: %q", name)
|
|
}
|
|
|
|
// Rank ارزش کارت؛ 2..10 معمولی، 11=J 12=Q 13=K 14=A.
|
|
type Rank uint8
|
|
|
|
const (
|
|
Jack Rank = 11
|
|
Queen Rank = 12
|
|
King Rank = 13
|
|
Ace Rank = 14
|
|
)
|
|
|
|
// Card یک کارت بازی.
|
|
type Card struct {
|
|
Suit Suit
|
|
Rank Rank
|
|
}
|
|
|
|
// String کد کوتاه کارت مثل "AS", "10H", "2C".
|
|
func (c Card) String() string {
|
|
var r string
|
|
switch c.Rank {
|
|
case Ace:
|
|
r = "A"
|
|
case King:
|
|
r = "K"
|
|
case Queen:
|
|
r = "Q"
|
|
case Jack:
|
|
r = "J"
|
|
default:
|
|
r = fmt.Sprintf("%d", c.Rank)
|
|
}
|
|
return r + string(suitChar[c.Suit])
|
|
}
|
|
|
|
// ParseCard کد کوتاه کارت را پارس میکند (مثلاً "10H", "AS").
|
|
func ParseCard(code string) (Card, error) {
|
|
code = strings.ToUpper(strings.TrimSpace(code))
|
|
if len(code) < 2 {
|
|
return Card{}, fmt.Errorf("invalid card: %q", code)
|
|
}
|
|
rankStr := code[:len(code)-1]
|
|
suitStr := code[len(code)-1:]
|
|
|
|
s, err := ParseSuit(suitStr)
|
|
if err != nil {
|
|
return Card{}, fmt.Errorf("invalid card %q: %w", code, err)
|
|
}
|
|
|
|
var r Rank
|
|
switch rankStr {
|
|
case "A":
|
|
r = Ace
|
|
case "K":
|
|
r = King
|
|
case "Q":
|
|
r = Queen
|
|
case "J":
|
|
r = Jack
|
|
default:
|
|
var n int
|
|
if _, err := fmt.Sscanf(rankStr, "%d", &n); err != nil || n < 2 || n > 10 {
|
|
return Card{}, fmt.Errorf("invalid rank in card %q", code)
|
|
}
|
|
r = Rank(n)
|
|
}
|
|
return Card{Suit: s, Rank: r}, nil
|
|
}
|
|
|
|
// NewDeck یک دسته ۵۲ کارتی مرتب میسازد.
|
|
func NewDeck() []Card {
|
|
deck := make([]Card, 0, 52)
|
|
for s := Suit(0); s < 4; s++ {
|
|
for r := Rank(2); r <= Ace; r++ {
|
|
deck = append(deck, Card{Suit: s, Rank: r})
|
|
}
|
|
}
|
|
return deck
|
|
}
|
|
|
|
// Shuffle دسته را با crypto/rand بهصورت Fisher-Yates بُر میزند.
|
|
func Shuffle(deck []Card) {
|
|
for i := len(deck) - 1; i > 0; i-- {
|
|
jBig, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
|
|
if err != nil {
|
|
continue // عملاً رخ نمیدهد
|
|
}
|
|
j := int(jBig.Int64())
|
|
deck[i], deck[j] = deck[j], deck[i]
|
|
}
|
|
}
|