17. 보스 기여도 집계와 막타·보상 귀속 (C#)
난이도 중 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [코드리뷰 문제] C# - 보스 기여도(데미지) 집계와 막타·보상 귀속
// ----------------------------------------------------------------------------
// 시나리오:
// - 필드/레이드 보스에게 여러 플레이어(여러 파티)가 동시에 공격을 넣는다.
// - 서버는 공격이 적중할 때마다 보스 HP 를 깎고, 플레이어별 누적 데미지
// 기여도를 집계한다. 공격 처리는 여러 워커 스레드에서 동시에 들어온다.
// - 보스 HP 가 0 이하가 되는 순간 "처치"로 보고, 막타(last hit)를 넣은
// 플레이어와 기여도 비율에 따라 보상(골드/드랍/기여 점수)을 분배한다.
// - 데미지 값은 서버가 자체 계산한 권위 값이다(클라가 보낸 수치 신뢰 금지).
//
// 요구사항:
// - 보스 HP 차감과 기여도 누적은 동시 공격에서도 유실 없이 정확해야 한다.
// - 처치 보상은 정확히 한 번만 분배돼야 한다(이중 보상/이중 드랍 금지).
// - 막타 귀속과 기여도 합계는 실제 적용된 데미지와 일치해야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Collections.Generic;
using System.Linq;
public class Boss
{
public long Id;
public long Hp;
public long MaxHp;
public bool Dead;
public long LastHitter; // 막타 플레이어 id
// playerId -> 누적 데미지
public Dictionary<long, long> Contribution = new Dictionary<long, long>();
}
public interface IRewardService
{
void GrantKillRewards(Boss boss, long lastHitter, Dictionary<long, long> shares);
}
public class BossCombatService
{
private readonly IRewardService _reward;
public BossCombatService(IRewardService reward) { _reward = reward; }
// 공격 적중 시 호출(여러 워커 스레드에서 동시 호출됨). dmg 는 서버 계산값.
public void ApplyDamage(Boss boss, long attackerId, long dmg)
{
if (boss.Dead) return;
// (A) 기여도 누적
if (boss.Contribution.TryGetValue(attackerId, out var acc))
boss.Contribution[attackerId] = acc + dmg;
else
boss.Contribution[attackerId] = dmg;
// (B) HP 차감 + 막타/처치 판정
boss.Hp -= dmg;
boss.LastHitter = attackerId;
if (boss.Hp <= 0 && !boss.Dead)
{
boss.Dead = true;
DistributeRewards(boss);
}
}
// 처치 보상 분배
private void DistributeRewards(Boss boss)
{
long total = 0;
// (C) 기여도 합산 후 비율 분배
foreach (var kv in boss.Contribution)
total += kv.Value;
var shares = new Dictionary<long, long>();
foreach (var kv in boss.Contribution)
shares[kv.Key] = kv.Value * 100 / total; // 기여 비율(%)
_reward.GrantKillRewards(boss, boss.LastHitter, shares);
}
} 결함 코드 · C++
// ============================================================================
// [코드리뷰 문제] C++ - 보스 기여도(데미지) 집계와 막타·보상 귀속
// ----------------------------------------------------------------------------
// 시나리오:
// - 필드/레이드 보스에게 여러 플레이어(여러 파티)가 동시에 공격을 넣는다.
// - 서버는 공격이 적중할 때마다 보스 HP 를 깎고, 플레이어별 누적 데미지
// 기여도를 집계한다. 공격 처리는 여러 워커 스레드에서 동시에 들어온다.
// - 보스 HP 가 0 이하가 되는 순간 "처치"로 보고, 막타(last hit)를 넣은
// 플레이어와 기여도 비율에 따라 보상을 분배한다.
// - 데미지 값은 서버가 자체 계산한 권위 값이다.
//
// 요구사항:
// - 보스 HP 차감과 기여도 누적은 동시 공격에서도 유실 없이 정확해야 한다.
// - 처치 보상은 정확히 한 번만 분배돼야 한다(이중 보상 금지).
// - 막타 귀속과 기여도 합계는 실제 적용된 데미지와 일치해야 한다.
//
// 과제:
// 이 코드의 잠재적 문제를 모두 찾고, 왜/어떻게 깨지는지(동시 인터리빙 포함)
// 설명하고, 수정안과 더 나은 설계를 제시하라.
// (먼저 직접 리뷰를 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
#include <cstdint>
#include <unordered_map>
struct Boss {
int64_t id = 0;
int64_t hp = 0;
int64_t maxHp = 0;
bool dead = false;
int64_t lastHitter = 0;
std::unordered_map<int64_t, int64_t> contribution; // playerId -> 누적 데미지
};
struct IRewardService {
virtual void GrantKillRewards(const Boss& boss, int64_t lastHitter,
const std::unordered_map<int64_t, int64_t>& shares) = 0;
virtual ~IRewardService() = default;
};
class BossCombatService {
public:
explicit BossCombatService(IRewardService& reward) : reward_(reward) {}
// 공격 적중 시 호출(여러 워커 스레드에서 동시 호출됨). dmg 는 서버 계산값.
void ApplyDamage(Boss& boss, int64_t attackerId, int64_t dmg) {
if (boss.dead) return;
// (A) 기여도 누적
boss.contribution[attackerId] += dmg;
// (B) HP 차감 + 막타/처치 판정
boss.hp -= dmg;
boss.lastHitter = attackerId;
if (boss.hp <= 0 && !boss.dead) {
boss.dead = true;
DistributeRewards(boss);
}
}
private:
// 처치 보상 분배
void DistributeRewards(Boss& boss) {
int64_t total = 0;
// (C) 기여도 합산 후 비율 분배
for (auto& kv : boss.contribution)
total += kv.second;
std::unordered_map<int64_t, int64_t> shares;
for (auto& kv : boss.contribution)
shares[kv.first] = kv.second * 100 / total; // 기여 비율(%)
reward_.GrantKillRewards(boss, boss.lastHitter, shares);
}
IRewardService& reward_;
}; 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.
내 리뷰 · C++
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.