152 lines
5.9 KiB
Markdown
152 lines
5.9 KiB
Markdown
# План: Тактический ИИ и Ролевая Логика (M4)
|
||
|
||
**Цель:** Реализовать роль Вратаря с логикой перекрытия углов и систему зонирования для нападающих и защитников.
|
||
|
||
**Архитектура:**
|
||
- Введение `RoleGoalie` в перечисление ролей.
|
||
- Реализация специализированного метода `updateGoalieAI`, который вычисляет целевую точку на векторе «Центр ворот -> Шайба» с ограничением по радиусу.
|
||
- Внедрение системы зон (Защитная, Средняя, Атакующая) для динамического изменения агрессивности игроков.
|
||
|
||
**Стек:** Go
|
||
|
||
**Спека:** [docs/specs/2026-05-09-tactical-ai-design.md](../specs/2026-05-09-tactical-ai-design.md)
|
||
|
||
---
|
||
|
||
## Файловая структура
|
||
|
||
- `internal/entities/player.go` — добавление `RoleGoalie`.
|
||
- `internal/game/world.go` — константы зон, логика ИИ вратаря и зонирования.
|
||
- `internal/game/world_test.go` — тесты на поведение вратаря и смену зон.
|
||
|
||
---
|
||
|
||
## Задачи
|
||
|
||
### Задача 1: Расширение ролей и константы зон
|
||
|
||
**Файлы:**
|
||
- Изменить: `internal/entities/player.go`
|
||
- Изменить: `internal/game/world.go`
|
||
|
||
- [ ] **Шаг 1: Добавить `RoleGoalie` в `PlayerRole`**
|
||
```go
|
||
type PlayerRole int
|
||
const (
|
||
RoleStriker PlayerRole = iota
|
||
RoleDefender
|
||
RoleGoalie // Добавить это
|
||
)
|
||
```
|
||
|
||
- [ ] **Шаг 2: Добавить константы зон в `world.go`**
|
||
```go
|
||
const (
|
||
ZoneWidth = WorldWidth / 3
|
||
GoalieMaxDistance = 80.0 // Максимальный выход вратаря из ворот
|
||
)
|
||
```
|
||
|
||
- [ ] **Шаг 3: Коммит**
|
||
`git add internal/entities/player.go internal/game/world.go`
|
||
`git commit -m "feat(ai): add RoleGoalie and zone constants"`
|
||
|
||
### Задача 2: Реализация ИИ Вратаря (Перехватчик)
|
||
|
||
**Файлы:**
|
||
- Изменить: `internal/game/world.go`
|
||
|
||
- [ ] **Шаг 1: Реализовать `updateGoalieAI`**
|
||
Логика:
|
||
1. Определить центр своих ворот (`GoalLeft` или `GoalRight`).
|
||
2. Вычислить вектор от центра ворот к шайбе.
|
||
3. Целевая точка = Центр ворот + (Нормализованный вектор * min(дистанция_до_шайбы, GoalieMaxDistance)).
|
||
4. Ограничить Y в пределах ворот (например, +/- 100 пикселей от центра).
|
||
|
||
```go
|
||
func (w *World) updateGoalieAI(p *entities.Player, puckPos entities.Vector2D) entities.Vector2D {
|
||
goalPos := GoalLeft
|
||
if p.Team == TeamRight {
|
||
goalPos = GoalRight
|
||
}
|
||
|
||
// Вектор от ворот к шайбе
|
||
dir := puckPos.Sub(goalPos)
|
||
dist := dir.Len()
|
||
|
||
// Ограничиваем выход из ворот
|
||
moveDist := dist
|
||
if moveDist > GoalieMaxDistance {
|
||
moveDist = GoalieMaxDistance
|
||
}
|
||
|
||
target := goalPos.Add(dir.Normalize().Mul(moveDist))
|
||
|
||
// Ограничение по Y (чтобы не уходил за пределы ворот)
|
||
if target.Y < WorldCenterY-100 { target.Y = WorldCenterY-100 }
|
||
if target.Y > WorldCenterY+100 { target.Y = WorldCenterY+100 }
|
||
|
||
return target
|
||
}
|
||
```
|
||
|
||
- [ ] **Шаг 2: Интегрировать в `updatePlayerAI`**
|
||
```go
|
||
switch p.Role {
|
||
case entities.RoleGoalie:
|
||
target = w.updateGoalieAI(p, puckPos)
|
||
// ... остальные роли
|
||
}
|
||
```
|
||
|
||
- [ ] **Шаг 3: Коммит**
|
||
`git commit -m "feat(ai): implement goalie interceptor logic"`
|
||
|
||
### Задача 3: Система зонирования для полевых игроков
|
||
|
||
**Файлы:**
|
||
- Изменить: `internal/game/world.go`
|
||
|
||
- [ ] **Шаг 1: Реализовать определение зоны**
|
||
```go
|
||
func (w *World) getPlayerZone(p *entities.Player) int {
|
||
// 0: Защитная, 1: Средняя, 2: Атакующая
|
||
// Для TeamLeft: X < ZoneWidth (Защитная), X < 2*ZoneWidth (Средняя), else (Атакующая)
|
||
// Для TeamRight: X > 2*ZoneWidth (Защитная), X > ZoneWidth (Средняя), else (Атакующая)
|
||
if p.Team == TeamLeft {
|
||
if p.Pos.X < ZoneWidth { return 0 }
|
||
if p.Pos.X < 2*ZoneWidth { return 1 }
|
||
return 2
|
||
} else {
|
||
if p.Pos.X > 2*ZoneWidth { return 0 }
|
||
if p.Pos.X > ZoneWidth { return 1 }
|
||
return 2
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Шаг 2: Обновить логику Striker и Defender с учетом зон**
|
||
- **Striker**: В зоне 0 (защитной) стремится к центру поля, в зоне 2 (атакующей) — агрессивно к шайбе.
|
||
- **Defender**: В зоне 0 — агрессивно к шайбе, в зоне 2 — возвращается к своим воротам.
|
||
|
||
- [ ] **Шаг 3: Коммит**
|
||
`git commit -m "feat(ai): implement player zoning logic"`
|
||
|
||
### Задача 4: Тестирование и верификация
|
||
|
||
**Файлы:**
|
||
- Изменить: `internal/game/world_test.go`
|
||
|
||
- [ ] **Шаг 1: Тест `TestWorld_GoaliePositioning`**
|
||
- Проверить, что вратарь TeamLeft при шайбе в центре поля стоит в районе `GoalLeft` и не уходит дальше `GoalieMaxDistance`.
|
||
- Проверить, что вратарь смещается вслед за шайбой по Y.
|
||
|
||
- [ ] **Шаг 2: Тест `TestWorld_ZoningBehavior`**
|
||
- Проверить, что защитник в атакующей зоне стремится вернуться назад.
|
||
|
||
- [ ] **Шаг 3: Запуск тестов**
|
||
`go test ./internal/game/ -v`
|
||
|
||
- [ ] **Шаг 4: Коммит**
|
||
`git commit -m "test(ai): add goalie and zoning tests"`
|