package game import ( "errors" "fmt" "math/rand" "slices" "strings" ) type PileOfCards interface { Size() int IsEmpty() bool Contains(*Card) bool Cards() []*Card FilterCards(func(*Card) bool) []*Card AddCards(cards []*Card) AddCard(card *Card) RemoveCard(card *Card) RandomCard() *Card MoveCard(card *Card, into PileOfCards) MoveInto(into PileOfCards) ToList() string String() string } type PileOfCardsBase struct { cards []*Card } func NewPileOfCards() *PileOfCardsBase { return &PileOfCardsBase{[]*Card{}} } // Stringify to a []-bracket enclosed comma-seperated list of card names. func (poc *PileOfCardsBase) String() string { cardNames := make([]string, 0, len(poc.Cards())) for _, card := range poc.Cards() { cardNames = append(cardNames, card.Path()) } return fmt.Sprintf("[%s]", strings.Join(cardNames, ", ")) } var ErrInvalidPocString = errors.New("invalid PileOfCards format") // Initialise a PileOfCard from a string. // Return InvalidPocString on error. func (poc *PileOfCardsBase) FromString(in string) error { if in[0] != '[' || in[len(in)-1] != ']' { return ErrInvalidPocString } in = in[1 : len(in)-1] for cardName := range strings.SplitSeq(in, ", ") { if card, err := NewCardSafe(cardName); err != nil { return err } else { poc.AddCard(card) } } return nil } func (poc *PileOfCardsBase) Size() int { return len(poc.cards) } func (poc *PileOfCardsBase) IsEmpty() bool { return poc.Size() == 0 } func (poc *PileOfCardsBase) Contains(card *Card) bool { return slices.Contains(poc.cards, card) } func (poc *PileOfCardsBase) Cards() []*Card { return poc.cards } func (poc *PileOfCardsBase) FilterCards(f func(*Card) bool) []*Card { cards := []*Card{} for _, c := range poc.Cards() { if f(c) { cards = append(cards, c) } } return cards } func (poc *PileOfCardsBase) AddPoc(toAdd PileOfCards) { poc.AddCards(toAdd.Cards()) } func (poc *PileOfCardsBase) AddCards(cards []*Card) { for _, card := range cards { poc.AddCard(card) } } func (poc *PileOfCardsBase) AddCard(card *Card) { // Tokens can not be part of any pile of cards if !card.IsToken() { poc.cards = append(poc.cards, card) } } func (poc *PileOfCardsBase) RemoveCard(card *Card) { ncards := poc.Size() for i, c := range poc.cards { if c != card { continue } poc.cards[i] = poc.cards[ncards-1] poc.cards = poc.cards[:ncards-1] return } } func (poc *PileOfCardsBase) RandomCard() *Card { availCards := poc.Cards() var pick int if n := len(availCards); n > 1 { pick = rand.Intn(n - 1) } else { pick = 0 } return availCards[pick] } func (poc *PileOfCardsBase) MoveCard(c *Card, into PileOfCards) { poc.RemoveCard(c) into.AddCard(c) } func (poc *PileOfCardsBase) MoveInto(into PileOfCards) { into.AddCards(poc.cards) poc.cards = []*Card{} } func (poc *PileOfCardsBase) ToList() (list string) { m := make(map[string]int) for _, card := range poc.cards { path := card.Path() m[path]++ } for path, amount := range m { list += fmt.Sprintf("%d %s\n", amount, path) } return list[:len(list)-1] } func NewRandomPackFromPool(pool PileOfCards, packSize int) PileOfCards { pack := NewPileOfCards() for range packSize { card := pool.Cards()[rand.Intn(len(pool.Cards())-1)] pool.MoveCard(card, pack) } return pack }