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++
// ============================================================================
// [코드리뷰 문제] C++ - 즉사/부활(사망 상태) 처리 중 이동·아이템 사용 끼어듦
// ----------------------------------------------------------------------------
// 시나리오:
// - 플레이어(액터)는 HP 와 생존 상태(Alive/Dead)를 가진다.
// - 피격(takeDamage): HP 를 깎고, 0 이하가 되면 사망 처리(die)한다. 사망 시
// 전리품을 드랍하고 부활 타이머를 건다.
// - 부활(respawn): HP/위치를 복구하고 다시 Alive 로 만든다.
// - 이동(move)/물약 사용(useHealPotion): 살아 있을 때만 의미가 있다.
// - 이 요청들은 서로 다른 IO/전투 워커 스레드에서 같은 플레이어에 대해 거의
// 동시에 들어올 수 있다(연타·매크로·동시 피격·죽는 순간의 입력).
//
// 요구사항:
// - 사망 처리(전리품 드랍·부활 예약)는 정확히 한 번만 일어나야 한다.
// - 죽은 상태에서는 이동/물약 사용 등 살아있는 행동이 적용되면 안 된다.
// - HP 가 0 이하인데 Alive, 또는 HP > 0 인데 Dead 같은 모순 상태가 없어야 한다.
// - 부활과 피격이 겹쳐도 "부활하자마자 이미 끝난 피격으로 즉사" 같은 일이 없어야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙·데이터레이스
// 포함) 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <utility>
enum class LifeState { Alive, Dead };
struct Vec2 { float x; float y; };
class IWorld {
public:
virtual ~IWorld() = default;
virtual void dropLoot(class CombatActor* actor) = 0;
virtual void scheduleRespawn(class CombatActor* actor, int delaySec) = 0;
virtual Vec2 getSpawnPoint() = 0;
};
class CombatActor {
public:
LifeState state = LifeState::Alive;
int hp = 100;
int maxHp = 100;
Vec2 pos{0, 0};
explicit CombatActor(IWorld* world) : world_(world) {}
// 이동: 살아 있을 때만 허용
bool move(float dx, float dy) {
// (A)
if (state != LifeState::Alive) return false;
pos.x += dx;
pos.y += dy;
return true;
}
// 피격: HP 차감 후 0 이하이면 사망 처리
void takeDamage(int dmg) {
// (B)
hp -= dmg;
if (hp <= 0)
die();
}
// 부활: HP/위치 복구
void respawn() {
// (C)
hp = maxHp;
pos = world_->getSpawnPoint();
state = LifeState::Alive;
}
// 물약 사용: HP 회복
bool useHealPotion(int amount) {
// (C)
hp += amount;
if (hp > maxHp) hp = maxHp;
return true;
}
private:
void die() {
state = LifeState::Dead;
world_->dropLoot(this); // 전리품 드랍(1회만이어야 함)
world_->scheduleRespawn(this, 10); // 부활 예약(1회만이어야 함)
}
IWorld* world_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.