20. 즉사/부활(사망 상태) 처리 중 이동·아이템 사용 끼어듦

난이도 중 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 즉사/부활(사망 상태) 처리 중 이동·아이템 사용 끼어듦
// ----------------------------------------------------------------------------
// 시나리오:
//   - 플레이어(액터)는 HP 와 생존 상태(Alive/Dead)를 가진다.
//   - 피격(TakeDamage): HP 를 깎고, 0 이하가 되면 사망 처리(Die)한다. 사망 시
//     전리품을 드랍하고 부활 타이머를 건다.
//   - 부활(Respawn): HP/위치를 복구하고 다시 Alive 로 만든다.
//   - 이동(Move)/물약 사용(UseHealPotion): 살아 있을 때만 의미가 있다.
//   - 이 요청들은 서로 다른 IO/전투 워커 스레드에서 같은 플레이어에 대해 거의
//     동시에 들어올 수 있다(연타·매크로·동시 피격·죽는 순간의 입력).
//
// 요구사항:
//   - 사망 처리(전리품 드랍·부활 예약)는 정확히 한 번만 일어나야 한다.
//   - 죽은 상태에서는 이동/물약 사용 등 살아있는 행동이 적용되면 안 된다.
//   - HP 가 0 이하인데 Alive, 또는 HP > 0 인데 Dead 같은 모순 상태가 없어야 한다.
//   - 부활과 피격이 겹쳐도 "부활하자마자 이미 끝난 피격으로 즉사" 같은 일이 없어야 한다.
//
// 과제:
//   이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
//   설명하고, 수정안과 더 나은 설계를 제시하라.
//   (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================

using System;

public enum LifeState { Alive, Dead }

public interface IWorld
{
    void DropLoot(CombatActor actor);
    void ScheduleRespawn(CombatActor actor, int delaySec);
    (float x, float y) GetSpawnPoint();
}

public class CombatActor
{
    public LifeState State = LifeState.Alive;
    public int Hp = 100;
    public int MaxHp = 100;
    public (float x, float y) Pos;

    private readonly IWorld _world;
    public CombatActor(IWorld world) { _world = world; }

    // 이동: 살아 있을 때만 허용
    public bool Move(float dx, float dy)
    {
        // (A)
        if (State != LifeState.Alive) return false;
        Pos = (Pos.x + dx, Pos.y + dy);
        return true;
    }

    // 피격: HP 차감 후 0 이하이면 사망 처리
    public void TakeDamage(int dmg)
    {
        // (B)
        Hp -= dmg;
        if (Hp <= 0)
            Die();
    }

    private void Die()
    {
        State = LifeState.Dead;
        _world.DropLoot(this);                 // 전리품 드랍(1회만이어야 함)
        _world.ScheduleRespawn(this, 10);      // 부활 예약(1회만이어야 함)
    }

    // 부활: HP/위치 복구
    public void Respawn()
    {
        // (C)
        Hp = MaxHp;
        Pos = _world.GetSpawnPoint();
        State = LifeState.Alive;
    }

    // 물약 사용: HP 회복
    public bool UseHealPotion(int amount)
    {
        // (C)
        Hp += amount;
        if (Hp > MaxHp) Hp = MaxHp;
        return true;
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.