go_games_collection/bot_heuristics.go
2026-07-04 13:51:04 +03:00

133 lines
3.7 KiB
Go
Raw 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
// 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
}
}
}