Files
2nd/10_Wiki/Topics/AI_and_ML/전투 전술(Battle Strategies).md
T
2026-05-10 22:08:15 +09:00

182 lines
6.3 KiB
Markdown
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.
---
id: wiki-2026-0508-전투-전술-battle-strategies
title: 전투 전술(Battle Strategies)
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Battle Strategies, 전투 전술, Combat Tactics]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [game-design, rts, tactics, combat]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
language: typescript
framework: ECS/BehaviorTree
---
# 전투 전술 (Battle Strategies)
## 매 한 줄
> **"매 전투 전술은 unit composition × terrain × tempo 의 곱"**. RTS/wargame design 의 핵심 axis 로, Sun Tzu 의 고전 doctrine 에서 출발해 modern wargame (WARNO, Steel Division 2, Broken Arrow) 에서는 fog-of-war + recon priority + combined-arms 로 진화. AI opponent 는 utility-based scoring 과 behavior tree 로 구현.
## 매 핵심
### 매 4대 축
- **Composition**: unit roster 의 다양성 — infantry / armor / artillery / recon / support
- **Terrain**: cover, elevation, choke point — line-of-sight 와 movement cost 의 변형
- **Tempo**: 매 OODA loop — observe → orient → decide → act, 반복 주기
- **Information**: fog-of-war, recon, EW (electronic warfare) — 정보 비대칭 활용
### 매 doctrine 분류
- **Attrition**: 매 firepower 압박 — losses 를 강제, 적 reinforcement curve 를 break
- **Maneuver**: 매 flanking + encirclement — Schwerpunkt (focal point) 에 mass 집중
- **Defensive-elastic**: 매 layered defense — kill zone 유도 후 counter-attack
- **Asymmetric**: 매 guerilla / hit-and-run — engagement 거부, 공급망 공격
### 매 응용
1. RTS AI controller — utility scoring 으로 매 frame 행동 선택.
2. Wargame mission design — objective gating 으로 player 가 doctrine 학습.
3. Auto-battler balance — 상성 표 + composition entropy 측정.
## 💻 패턴
### Utility-based unit AI
```typescript
type UnitState = { hp: number; ammo: number; pos: Vec2; supply: number }
type Action = "attack" | "retreat" | "hold" | "flank"
function score(s: UnitState, a: Action, ctx: BattleCtx): number {
switch (a) {
case "attack": return s.ammo * 0.4 + ctx.enemyExposed * 0.6 - s.hpDeficit * 0.5
case "retreat": return s.hpDeficit * 0.8 - ctx.allyNearby * 0.3
case "hold": return ctx.cover * 0.7 + ctx.objectiveDist * -0.2
case "flank": return ctx.enemyFlankExposed * 0.9 - s.supply * 0.4
}
}
function pickAction(s: UnitState, ctx: BattleCtx): Action {
const actions: Action[] = ["attack", "retreat", "hold", "flank"]
return actions.reduce((best, a) =>
score(s, a, ctx) > score(s, best, ctx) ? a : best
)
}
```
### Threat assessment grid
```typescript
function buildThreatMap(units: Unit[], grid: Grid): Float32Array {
const map = new Float32Array(grid.w * grid.h)
for (const u of units) {
if (u.team !== "enemy") continue
const range = u.weaponRange
for (let dy = -range; dy <= range; dy++) {
for (let dx = -range; dx <= range; dx++) {
const x = u.pos.x + dx, y = u.pos.y + dy
if (!grid.inBounds(x, y)) continue
const dist = Math.hypot(dx, dy)
if (dist > range) continue
map[y * grid.w + x] += u.firepower * (1 - dist / range)
}
}
}
return map
}
```
### Schwerpunkt detection
```typescript
function findSchwerpunkt(allies: Unit[], threatMap: Float32Array, grid: Grid): Vec2 {
let bestScore = -Infinity, bestPos = allies[0].pos
for (const u of allies) {
const idx = u.pos.y * grid.w + u.pos.x
const massNearby = allies.filter(a => Vec2.dist(a.pos, u.pos) < 200).length
const threatGap = 1 / (threatMap[idx] + 0.1)
const score = massNearby * threatGap
if (score > bestScore) { bestScore = score; bestPos = u.pos }
}
return bestPos
}
```
### Tempo controller (OODA)
```typescript
class TempoController {
private lastDecisionAt = 0
private cycleMs = 800
tick(now: number, ctx: BattleCtx) {
if (now - this.lastDecisionAt < this.cycleMs) return
const obs = this.observe(ctx)
const orient = this.orient(obs)
const decision = this.decide(orient)
this.act(decision)
this.lastDecisionAt = now
this.cycleMs = Math.max(300, 800 - ctx.pressureLevel * 100)
}
}
```
### Recon priority queue
```typescript
function reconTargets(unknown: Cell[], objectives: Vec2[]): Cell[] {
return unknown
.map(c => ({
cell: c,
score: objectives.reduce((s, o) => s + 1 / (Vec2.dist(c.pos, o) + 1), 0),
}))
.sort((a, b) => b.score - a.score)
.slice(0, 5)
.map(x => x.cell)
}
```
### Combined-arms morale boost
```typescript
function moraleBonus(unit: Unit, allies: Unit[]): number {
const nearby = allies.filter(a => Vec2.dist(a.pos, unit.pos) < 150)
const types = new Set(nearby.map(a => a.type))
return types.size >= 3 ? 0.25 : types.size >= 2 ? 0.1 : 0
}
```
## 매 결정 기준
| 상황 | Approach |
|---|---|
| Numeric superiority + open terrain | Attrition + frontal mass |
| Mobile force vs static defense | Maneuver, flank Schwerpunkt |
| Outnumbered, defensive | Elastic defense + kill zones |
| Unknown enemy composition | Recon-heavy, delay engagement |
| Long campaign, supply-limited | Asymmetric, denial |
**기본값**: combined-arms balanced doctrine + recon-first opening.
## 🔗 Graph
- 부모: [[제병협동 (Combined Arms)]] · [[Game_Design]]
- 변형: [[유닛 상성(Unit Counters)]] · [[제로잉 (Getting Zero-ed)]]
- 응용: [[WARNO]] · [[Eugen_Systems]] · [[4X_전략]]
- Adjacent: [[AI 추적 논리(AI Pursuit Logic)]] · [[Combat_Timeline_Difficulty_Scaling]]
## 🤖 LLM 활용
**언제**: tactical AI scripting, mission designer 의 pacing 검증, opening-book 생성.
**언제 X**: 매 frame-tight pathfinding 의 inner loop — LLM latency 가 너무 큼, behavior tree 에 위임.
## ❌ 안티패턴
- **Single-unit doctrine**: 단일 병종 spam — counter unit 1 종류로 전체 collapse.
- **Static formation**: tempo 무시 — 매 적의 OODA 가 빨라 reactive 가 됨.
- **Recon skip**: 매 fog-of-war 무시 → blind commit, ambush 함정.
- **Schwerpunkt 부재**: 매 force dispersion → 어디서도 decisive mass 못 만듦.
## 🧪 검증 / 중복
- Verified (Eugen Systems WARNO design docs, US FM 3-0 Operations).
- 신뢰도 A.
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — battle tactics doctrine + AI scoring patterns |