go_games_collection/deck.go
2026-07-07 09:12:09 +03:00

77 lines
3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"crypto/rand"
"math/big"
)
// Deck — стопка карт. Индекс 0 — верх колоды (карта, которая уходит первой).
type Deck struct {
cards []Card
}
// NewDeck создаёт полную неперетасованную колоду из 52 карт.
func NewDeck() *Deck {
d := &Deck{cards: make([]Card, 0, 52)}
for s := Clubs; s <= Spades; s++ {
for r := Ace; r <= King; r++ {
d.cards = append(d.cards, Card{Suit: s, Rank: r})
}
}
return d
}
// Shuffle перемешивает колоду криптографически стойким рандомом
// (алгоритм Фишера-Йейтса).
func (d *Deck) Shuffle() {
for i := len(d.cards) - 1; i > 0; i-- {
jBig, err := rand.Int(rand.Reader, big.NewInt(int64(i+1)))
if err != nil {
// crypto/rand не должен падать в обычных условиях;
// если это случилось — не имеет смысла продолжать игру.
panic(err)
}
j := int(jBig.Int64())
d.cards[i], d.cards[j] = d.cards[j], d.cards[i]
}
}
// Len возвращает количество карт, оставшихся в колоде.
func (d *Deck) Len() int {
return len(d.cards)
}
// Draw снимает верхнюю карту колоды. Второе значение — false,
// если колода пуста.
func (d *Deck) Draw() (Card, bool) {
if len(d.cards) == 0 {
return Card{}, false
}
c := d.cards[0]
d.cards = d.cards[1:]
return c, true
}
// DrawN снимает n верхних карт (используется при раздаче).
// Если в колоде меньше карт, чем запрошено, возвращает столько,
// сколько есть.
//
// ВАЖНО: возвращает независимую копию, а не подсрез d.cards. Раньше
// здесь было "out := d.cards[:n]" — такой подсрез имеет ту же
// вместимость (cap), что и исходный массив колоды, вплоть до самого
// его конца. Это означало, что руки РАЗНЫХ игроков после раздачи
// физически указывали на один и тот же нижележащий массив: как
// только первый игрок брал карту (append к своей руке), Go писал
// новый элемент прямо в этот общий массив — по адресу, где у
// СЛЕДУЮЩЕГО игрока лежала его собственная карта, молча её затирая.
// Тот самый баг с "одна и та же карта одновременно в сбросе и в
// комбинации на столе" — именно отсюда.
func (d *Deck) DrawN(n int) []Card {
if n > len(d.cards) {
n = len(d.cards)
}
out := make([]Card, n)
copy(out, d.cards[:n])
d.cards = d.cards[n:]
return out
}