package main

import "math"

// ChessBot — бот на основе минимакса с альфа-бета отсечением.
// Ветвление в шахматах гораздо шире, чем в шашках, поэтому глубина
// поиска по умолчанию заметно меньше.
type ChessBot struct {
	Depth int // глубина поиска в полуходах; 0 -> используется значение по умолчанию
}

// Уровни сложности: разница только в глубине поиска.
const (
	ChessDifficultyEasy = 2 // "Простой" — быстрый, играет более-менее случайно с точки зрения стратегии
	ChessDifficultyHard = 4 // "Сильный" — заметно сильнее, всё ещё отвечает быстро
)

const defaultChessDepth = 3

// chessMoveStep — один ход (from->to), достаточный для одного
// вызова g.Move.
type chessMoveStep struct {
	From ChessPos
	To   ChessPos
}

// legalChessSteps перечисляет все допустимые ходы для стороны color.
func legalChessSteps(g *ChessGameState, color ChessColor) []chessMoveStep {
	var out []chessMoveStep
	for r := 0; r < 8; r++ {
		for c := 0; c < 8; c++ {
			p := g.Board[r][c]
			if p == nil || p.Color != color {
				continue
			}
			pos := ChessPos{r, c}
			for _, opt := range g.LegalMovesFrom(pos) {
				out = append(out, chessMoveStep{From: pos, To: opt.To})
			}
		}
	}
	return out
}

var chessPieceValue = map[ChessPieceType]int{
	ChessPawn: 100, ChessKnight: 320, ChessBishop: 330, ChessRook: 500, ChessQueen: 900, ChessKing: 0,
}

// chessCenterBonus — небольшой позиционный бонус за контроль центра
// (тем же духом, что и бонус за продвижение шашек к дамке): не
// претендует на глубокое понимание позиции, просто чуть предпочитает
// активные фигуры пассивным.
func chessCenterBonus(pos ChessPos) int {
	dr := pos.Row - 3
	if dr < 0 {
		dr = -dr
	}
	dc := pos.Col - 3
	if dc < 0 {
		dc = -dc
	}
	dist := dr + dc
	return 6 - dist
}

// evaluateChess оценивает позицию с точки зрения perspective:
// положительно — хорошо для perspective.
func evaluateChess(g *ChessGameState, perspective ChessColor) int {
	if g.Result != nil {
		if g.Result.Draw {
			return 0
		}
		if g.Result.Winner == perspective {
			return 1000000
		}
		return -1000000
	}
	score := 0
	for r := 0; r < 8; r++ {
		for c := 0; c < 8; c++ {
			p := g.Board[r][c]
			if p == nil {
				continue
			}
			value := chessPieceValue[p.Type] + chessCenterBonus(ChessPos{r, c})
			if p.Color == perspective {
				score += value
			} else {
				score -= value
			}
		}
	}
	return score
}

func minimaxChess(g *ChessGameState, depth int, alpha, beta int, perspective ChessColor) int {
	if g.Result != nil || depth == 0 {
		return evaluateChess(g, perspective)
	}
	steps := legalChessSteps(g, g.Turn)
	if len(steps) == 0 {
		return evaluateChess(g, perspective)
	}

	maximizing := g.Turn == perspective
	if maximizing {
		best := math.MinInt32
		for _, step := range steps {
			child := cloneChess(g)
			_ = child.Move(step.From, step.To)
			val := minimaxChess(child, depth-1, alpha, beta, perspective)
			if val > best {
				best = val
			}
			if best > alpha {
				alpha = best
			}
			if alpha >= beta {
				break
			}
		}
		return best
	}

	best := math.MaxInt32
	for _, step := range steps {
		child := cloneChess(g)
		_ = child.Move(step.From, step.To)
		val := minimaxChess(child, depth-1, alpha, beta, perspective)
		if val < best {
			best = val
		}
		if best < beta {
			beta = best
		}
		if alpha >= beta {
			break
		}
	}
	return best
}

// DecideMove выбирает лучший ход для текущего хода в g согласно
// минимаксу.
func (b ChessBot) DecideMove(g *ChessGameState) (chessMoveStep, bool) {
	steps := legalChessSteps(g, g.Turn)
	if len(steps) == 0 {
		return chessMoveStep{}, false
	}
	depth := b.Depth
	if depth <= 0 {
		depth = defaultChessDepth
	}

	perspective := g.Turn
	bestVal := math.MinInt32
	var bestStep chessMoveStep
	alpha, beta := math.MinInt32, math.MaxInt32
	for _, step := range steps {
		child := cloneChess(g)
		_ = child.Move(step.From, step.To)
		val := minimaxChess(child, depth-1, alpha, beta, perspective)
		if val > bestVal {
			bestVal = val
			bestStep = step
		}
		if bestVal > alpha {
			alpha = bestVal
		}
	}
	return bestStep, true
}

// PlayFullTurn исполняет один ход бота.
func (b ChessBot) PlayFullTurn(g *ChessGameState) error {
	step, ok := b.DecideMove(g)
	if !ok {
		return nil
	}
	return g.Move(step.From, step.To)
}
