package main // containsCard проверяет наличие конкретной карты в срезе. func containsCard(cards []Card, target Card) bool { for _, c := range cards { if c == target { return true } } return false } // wouldFormMeld проверяет, образует ли candidate валидную // комбинацию (сет или сиквенс) вместе с картами руки. func wouldFormMeld(hand []Card, candidate Card) bool { trial := append(append([]Card{}, hand...), candidate) for _, s := range FindAllSets(trial) { if containsCard(s, candidate) { return true } } for _, s := range FindAllSequences(trial) { if containsCard(s, candidate) { return true } } return false } // cardPotential — грубая эвристика полезности карты для будущих // комбинаций: сколько других карт руки поддерживают её как часть // сета (тот же ранг) или сиквенса (та же масть, ранг отличается не // больше чем на 2 — то есть в пределах будущей связки). func cardPotential(hand []Card, card Card) int { support := 0 for _, other := range hand { if other == card { continue } if other.Rank == card.Rank { support++ } if other.Suit == card.Suit { diff := int(other.Rank) - int(card.Rank) if diff < 0 { diff = -diff } if diff <= 2 { support++ } } } return support } // deadestCard находит карту руки с наименьшим потенциалом // (см. cardPotential); при равенстве потенциала предпочитает карту // с наибольшей очковой стоимостью — чтобы избавляться от тяжёлого // "мёртвого груза" в первую очередь. func deadestCard(hand []Card) Card { best := hand[0] bestPotential := cardPotential(hand, best) for _, c := range hand[1:] { p := cardPotential(hand, c) if p < bestPotential || (p == bestPotential && c.Value() > best.Value()) { best = c bestPotential = p } } return best } // layAllAvailableMelds выкладывает на стол все комбинации, которые // может собрать текущий игрок из своей руки, повторяя поиск после // каждой выкладки (так как рука меняется). Возвращает количество // выложенных комбинаций. func layAllAvailableMelds(g *GameState) int { laid := 0 for { hand := g.current().Hand sets := FindAllSets(hand) seqs := FindAllSequences(hand) var best []Card for _, s := range sets { if len(s) > len(best) { best = s } } for _, s := range seqs { if len(s) > len(best) { best = s } } if best == nil { return laid } if err := g.LayNewMeld(best); err != nil { return laid } laid++ if g.Phase == PhaseGameOver { return laid } } } // tryExtendTableMelds пытается подложить карты руки к уже // существующим на столе комбинациям (в том числе чужим). Полезно // боту любого уровня, чтобы не копить карты, которые уже подходят // под стол. func tryExtendTableMelds(g *GameState) { for { extended := false for i := range g.Table { for _, card := range g.current().Hand { if g.AddToMeld(i, card) == nil { extended = true break } } if g.Phase == PhaseGameOver { return } if extended { break } } if !extended { return } } }