package main import "math/rand" // oneOhOneNamePool — имена ботов в 101. Отдельный пул от других игр // коллекции — обычные русские женские имена для разнообразия. var oneOhOneNamePool = []string{ "Мария", "Анна", "Ольга", "Елена", "Наталья", "Светлана", "Ирина", "Татьяна", } func pickOneOhOneBotName(used map[string]bool) string { free := make([]string, 0, len(oneOhOneNamePool)) for _, name := range oneOhOneNamePool { if !used[name] { free = append(free, name) } } if len(free) == 0 { free = oneOhOneNamePool } return free[rand.Intn(len(free))] } func newOneOhOneBotNames(n int) []string { used := make(map[string]bool, n) names := make([]string, n) for i := 0; i < n; i++ { name := pickOneOhOneBotName(used) used[name] = true names[i] = name } return names } // OneOhOneBot — единственная (без градации уровней) стратегия бота // для 101: старается в первую очередь избавляться от специальных и // дорогих карт, чтобы и снизить будущий штраф, и потрепать // соперников. type OneOhOneBot struct{} // DecideAction решает, что делать текущему игроку: если есть // подходящая карта — возвращает (card, true) с наиболее выгодной // для игры картой; если подходящей карты нет — возвращает // (Card{}, false), и вызывающий код должен вызвать g.Draw(). func (OneOhOneBot) DecideAction(g *OneOhOneGameState) (Card, bool) { p := g.current() var candidates []Card for _, card := range p.Hand { if g.canPlay(card) { candidates = append(candidates, card) } } if len(candidates) == 0 { return Card{}, false } best := candidates[0] bestScore := oneOhOnePlayPriority(best) for _, c := range candidates[1:] { if s := oneOhOnePlayPriority(c); s > bestScore { best = c bestScore = s } } return best, true } // oneOhOnePlayPriority оценивает, насколько выгодно сыграть карту // прямо сейчас: специальные карты (создающие проблемы сопернику) // ценятся выше, среди обычных карт предпочтение отдаётся более // дорогим (чтобы не осталась в руке при чужой победе). func oneOhOnePlayPriority(c Card) int { skip, penalty := oneOhOneCardEffect(c) if skip { return 100 + penalty*10 } if c.Rank == Queen { return 90 // дама — хороший финальный аккорд, но и просто ценна для сброса } return oneOhOneCardValue(c.Rank) } // PlayFullTurn исполняет один полный ход бота (в контексте 101 — // одно решение: сыграть карту или взять из колоды, повторяя Draw // при необходимости до тех пор, пока подходящая карта не найдётся // или колода не иссякнет) — используется при автоматической // прокрутке партии. func (b OneOhOneBot) PlayFullTurn(g *OneOhOneGameState) error { if g.Phase != OneOhOnePhasePlay { return nil } turnPlayer := g.CurrentPlayerIdx for g.Phase == OneOhOnePhasePlay && g.CurrentPlayerIdx == turnPlayer { if card, ok := b.DecideAction(g); ok { return g.PlayCard(card) } if err := g.Draw(); err != nil { return err } } return nil }