--- 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 |