package main

import "math/rand"

// oneOhOneNamePool — имена ботов в 101. Отдельный пул от других игр
// коллекции — обычные русские женские имена для разнообразия.
var oneOhOneNamePool = []string{
	"Мария", "Анна", "Ольга", "Елена", "Наталья", "Светлана", "Ирина", "Татьяна",
}

// oneOhOneNamePoolEN — англоязычная версия того же пула.
var oneOhOneNamePoolEN = []string{
	"Mary", "Anna", "Olivia", "Helen", "Natalie", "Sylvia", "Irene", "Diana",
}

func activeOneOhOneNamePool() []string {
	if CurrentLang() == LangEN {
		return oneOhOneNamePoolEN
	}
	return oneOhOneNamePool
}

func pickOneOhOneBotName(used map[string]bool) string {
	pool := activeOneOhOneNamePool()
	free := make([]string, 0, len(pool))
	for _, name := range pool {
		if !used[name] {
			free = append(free, name)
		}
	}
	if len(free) == 0 {
		free = pool
	}
	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 // дама — хороший финальный аккорд, но и просто ценна для сброса
	}
	if c.Rank == Eight {
		return 85 // восьмёрка создаёт сопернику проблему покрытия
	}
	return oneOhOneCardValue(c.Rank)
}

// DecideSuit выбирает, какую масть назвать после дамы: масть, карт
// которой у бота больше всего на руке (чтобы собственные будущие
// ходы было легче подбирать под неё).
func (OneOhOneBot) DecideSuit(g *OneOhOneGameState) Suit {
	p := g.current()
	counts := map[Suit]int{}
	for _, c := range p.Hand {
		counts[c.Suit]++
	}
	best := Clubs
	bestCount := -1
	for s := Clubs; s <= Spades; s++ {
		if counts[s] > bestCount {
			best = s
			bestCount = counts[s]
		}
	}
	return best
}

// PlayFullTurn исполняет один полный ход бота (в контексте 101 —
// одно решение: сыграть карту или взять из колоды, повторяя Draw
// при необходимости до тех пор, пока подходящая карта не найдётся
// или колода не иссякнет) — используется при автоматической
// прокрутке партии.
func (b OneOhOneBot) PlayFullTurn(g *OneOhOneGameState) error {
	if g.Phase == OneOhOnePhaseChooseSuit {
		return g.ChooseSuit(b.DecideSuit(g))
	}
	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
}
