Files
2nd/10_Wiki/Topics/AI_and_ML/War-Commander-전투-시스템.md
T
2026-05-10 22:08:15 +09:00

8.2 KiB
Raw Blame History

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-war-commander-전투-시스템 War Commander 전투 시스템 10_Wiki/Topics verified self
War Commander Combat
WC Battle System
RTS Combat Resolution
none A 0.85 applied
game-design
rts
combat-system
simulation
war-commander
2026-05-10 pending
language framework
python numpy, ECS-pattern

War Commander 전투 시스템

매 한 줄

"매 deterministic damage formula × stochastic hit roll × environmental modifier". War Commander (Kixeye 2011~) 의 browser-RTS 의 전투 system — 매 unit-level stat (HP, armor, damage, range) 의 layered modifier 의 final damage 의 resolve. 매 modern 2026 RTS (Wargame / WARNO / SteelDivision) 의 lineage 의 ancestral pattern.

매 핵심

매 damage 공식 layer

  1. Base damage — 매 weapon stat (e.g., 100).
  2. Armor reduction — 매 damage * (1 - armor_class_mult[target_armor]).
  3. Range falloff — 매 distance / max_range 의 linear or step decay.
  4. Cover modifier — 매 terrain (forest -25%, building -50%).
  5. Critical / random roll — 매 hit chance / crit multiplier.
  6. Splash AoE — 매 distance from impact center 의 attenuation.

매 unit class triangle

  • Infantry → Light vehicle: bonus.
  • Light vehicle → Tank: weak.
  • Tank → Infantry: strong.
  • Anti-air → Aircraft: dedicated counter.
  • 매 RPS (rock-paper-scissors) 의 enforcing 의 deck-building meta 의 driver.

매 응용

  1. 매 RTS combat solver 의 prototype.
  2. 매 game balance simulation (1000-run Monte Carlo).
  3. 매 tabletop wargame digital adaptation.
  4. 매 ML self-play agent 의 reward shaping.

💻 패턴

매 Damage 공식 core

import numpy as np
from dataclasses import dataclass

@dataclass
class Unit:
    hp: float
    armor_class: str       # "infantry", "light", "heavy", "air"
    damage: float
    weapon_class: str      # "small_arms", "AP", "HE", "AA"
    range: float
    accuracy: float        # 0..1

ARMOR_MULT = {
    # weapon → armor
    ("small_arms", "infantry"): 1.0, ("small_arms", "light"): 0.3, ("small_arms", "heavy"): 0.05,
    ("AP",        "infantry"): 0.5, ("AP",        "light"): 1.0, ("AP",        "heavy"): 1.2,
    ("HE",        "infantry"): 1.5, ("HE",        "light"): 0.8, ("HE",        "heavy"): 0.4,
    ("AA",        "air"):      1.5, ("AA",        "infantry"): 0.2,
}

def resolve_damage(attacker: Unit, defender: Unit, distance: float, cover: float = 0.0) -> float:
    if distance > attacker.range:
        return 0.0
    base = attacker.damage
    armor_mult = ARMOR_MULT.get((attacker.weapon_class, defender.armor_class), 0.5)
    range_falloff = 1.0 - 0.4 * (distance / attacker.range)  # up to -40%
    cover_mult = 1.0 - cover                                 # 0..1
    hit_roll = np.random.random() < attacker.accuracy
    if not hit_roll:
        return 0.0
    return base * armor_mult * range_falloff * cover_mult

매 Splash AoE

def splash_damage(impact_xy, damage, radius, units):
    results = []
    for u in units:
        d = np.linalg.norm(np.array(u.pos) - np.array(impact_xy))
        if d > radius:
            continue
        falloff = 1.0 - (d / radius) ** 2  # quadratic
        results.append((u, damage * falloff))
    return results

매 RPS counter matrix

COUNTER = {
    # attacker → defender → effectiveness
    "infantry":      {"infantry": 1.0, "light_vehicle": 0.6, "tank": 0.2, "aircraft": 0.0},
    "light_vehicle": {"infantry": 1.4, "light_vehicle": 1.0, "tank": 0.4, "aircraft": 0.0},
    "tank":          {"infantry": 0.8, "light_vehicle": 1.5, "tank": 1.0, "aircraft": 0.0},
    "anti_tank":     {"infantry": 0.5, "light_vehicle": 1.2, "tank": 1.8, "aircraft": 0.0},
    "anti_air":      {"infantry": 0.3, "light_vehicle": 0.5, "tank": 0.1, "aircraft": 2.0},
    "aircraft":      {"infantry": 1.2, "light_vehicle": 1.0, "tank": 0.8, "aircraft": 1.0},
}

매 Combat tick (ECS-style)

def combat_tick(world, dt: float):
    for atk in world.attackers():
        target = world.find_target(atk)
        if target is None:
            continue
        atk.cooldown -= dt
        if atk.cooldown > 0:
            continue
        dist = world.distance(atk, target)
        cover = world.cover_at(target.pos)
        dmg = resolve_damage(atk, target, dist, cover)
        target.hp -= dmg
        atk.cooldown = atk.fire_rate
        if target.hp <= 0:
            world.kill(target)

매 Morale / suppression layer

@dataclass
class Morale:
    value: float = 100.0  # 0..100
    suppressed: bool = False

def apply_suppression(unit, near_misses: int, dt: float):
    decay = 5.0 * dt
    impact = near_misses * 8.0
    unit.morale.value = max(0, unit.morale.value - impact + decay)
    unit.morale.suppressed = unit.morale.value < 30
    if unit.morale.suppressed:
        unit.accuracy *= 0.5  # 매 panic 의 effect

매 Monte Carlo balance test

def simulate_engagement(force_a, force_b, n_runs=1000):
    wins_a = 0
    for _ in range(n_runs):
        a = [Unit(**u.__dict__) for u in force_a]  # deep copy
        b = [Unit(**u.__dict__) for u in force_b]
        while a and b:
            for atk in a + b:
                targets = b if atk in a else a
                if not targets: break
                tgt = min(targets, key=lambda t: t.hp)
                tgt.hp -= resolve_damage(atk, tgt, distance=200, cover=0.2)
                if tgt.hp <= 0:
                    targets.remove(tgt)
            a = [u for u in a if u.hp > 0]
            b = [u for u in b if u.hp > 0]
        if a and not b:
            wins_a += 1
    return wins_a / n_runs

# 매 50% 의 target 의 balanced 일 때
print(simulate_engagement(soviet_tank_platoon, nato_inf_squad))

매 Line-of-sight check

def has_los(world, src_pos, dst_pos):
    steps = int(np.linalg.norm(np.array(dst_pos) - np.array(src_pos)) / 1.0)
    for t in np.linspace(0, 1, steps):
        p = np.array(src_pos) + t * (np.array(dst_pos) - np.array(src_pos))
        if world.height_at(p) > min(world.height_at(src_pos), world.height_at(dst_pos)) + 5:
            return False
    return True

매 결정 기준

상황 Approach
매 deterministic balance hit_roll 의 제거 — 매 expected damage 의 use
매 cinematic feel Crit / miss 의 high variance
매 esports 의 fair Low variance + high skill ceiling formula
매 simulation accuracy Penetration / angle / armor thickness 의 detailed model
매 web / browser RTS Simple layered formula (War Commander 의 origin)

기본값: 매 layered multiplicative formula + 매 RPS counter matrix + 매 small variance hit roll.

🔗 Graph

🤖 LLM 활용

언제: 매 RTS prototype 의 combat formula 의 baseline. 매 game-balance Monte Carlo 의 simulator 의 design. 언제 X: 매 modern simulation-grade armor model (penetration / angle / spalling) 의 required. 매 physics-based ballistics 의 needed.

안티패턴

  • Pure random damage: 매 player frustration — 매 perceived unfairness.
  • Hard counter only (no soft): 매 RPS 의 brittle — 매 deck composition 의 collapse.
  • Range = binary: 매 range falloff 의 없으면 매 kiting 의 broken.
  • No cooldown: 매 DPS 의 burst infinite.
  • Single-layer formula: 매 modifier stacking 의 없으면 매 design space 의 limited.

🧪 검증 / 중복

  • Verified (War Commander mechanics docs / community wikis 2011-2018, comparable RTS combat lit — Kixeye dev posts, Wargame / WARNO public formulas).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — full content (RTS combat resolution patterns / War Commander lineage)