LLM 기반 던전 방 배치와 난이도 곡선 적합도 자동 채점 파이프라인
LLM으로 던전 방 배치를 생성하고 연결성·전투 예산·자원 경제·플레이어 체감 난이도를 결정론적으로 검사하는 자동 채점 파이프라인을 설계합니다. JSON 스키마, 시뮬레이션, 곡선 적합도 수식과 재생성 전략까지 정리합니다.

핵심 요약
LLM은 던전 방 배치와 콘텐츠 후보를 빠르게 제안할 수 있지만 연결성, 전투 예산, 열쇠와 문 관계 같은 규칙을 스스로 안정적으로 보장하지는 못합니다. 따라서 LLM은 구조화된 배치안을 생성하고 실제 합격 여부는 JSON 스키마 검증과 결정론적 제약 검사, 플레이 시뮬레이션, 난이도 곡선 비교로 자동 채점하는 방식이 안전합니다.
핵심은 다음 네 단계입니다.
- 방, 문, 적, 보상, 자원을 JSON으로 표현한다.
- LLM 출력에 스키마 검증과 그래프 제약 검사를 적용한다.
- 여러 플레이어 모델로 던전을 시뮬레이션한다.
- 목표 난이도 곡선과 실제 측정값의 오차를 점수화하고 실패 원인을 분류한다.
왜 LLM 기반 던전 방 배치에 자동 채점이 필요한가?
LLM은 다음과 같은 설계 의도를 자연어로 전달받는 데 강합니다.
- 1~3분 안에 첫 전투를 배치한다.
- 안전한 휴식 공간은 두 번 연속 등장하지 않게 한다.
- 열쇠를 얻은 뒤 우회로를 통해 잠긴 보물방에 접근하게 한다.
- 보스 전투 전에는 회복 자원을 제공하되 보상이 과도하지 않게 한다.
문제는 자연어 의도와 실제 레벨 규칙 사이에 차이가 생긴다는 점입니다. 방이 겹치거나 시작점에서 보스까지 경로가 없을 수 있고 적의 전투력이 목표보다 높아 회복 물약을 모두 소모하게 만들 수도 있습니다.
| 역할 | LLM이 담당할 일 | 결정론적 시스템이 담당할 일 |
|---|---|---|
| 공간 구성 | 방 유형과 배치 후보 생성 | 겹침, 경계 이탈, 연결성 검사 |
| 진행 구조 | 열쇠와 문, 분기 구조 제안 | 선행 조건과 도달 가능성 검증 |
| 전투 설계 | 적 조합과 전투 콘셉트 제안 | 전투력 예산과 소요 시간 계산 |
| 보상 설계 | 보상 테마와 배치 제안 | 경제 예산, 기대 수익, 회복량 검사 |
| 수정 | 실패 원인에 대한 대안 생성 | 재검증과 점수 비교 |
LLM의 출력이 곧 정답이 되면 검증 결과가 모델의 표현 능력에 좌우됩니다. 반대로 LLM을 후보 생성기와 수정기로 제한하면 결과를 반복 재현하고 비교할 수 있습니다.
던전 방 배치는 어떤 데이터 구조로 표현할까?
방 배치를 이미지나 자연어가 아니라 그래프와 속성 데이터로 저장하는 것이 좋습니다. 방은 노드이고 문이나 통로는 간선입니다. 위치와 크기는 충돌 검사를 위한 공간 정보로 관리합니다.
{
"dungeonId": "crypt_01",
"seed": 48192,
"rooms": [
{
"id": "r_start",
"type": "start",
"x": 0,
"y": 0,
"width": 8,
"height": 6,
"encounters": [],
"rewards": [],
"resources": { "heal": 0, "gold": 0 }
},
{
"id": "r_guardian",
"type": "combat",
"x": 12,
"y": 0,
"width": 10,
"height": 8,
"encounters": [
{ "enemyId": "skeleton", "count": 3 }
],
"rewards": [
{ "type": "gold", "value": 80 }
],
"resources": { "heal": 0, "gold": 80 }
}
],
"connections": [
{ "from": "r_start", "to": "r_guardian", "lock": null }
]
}
실제 프로젝트에서는 type, encounters, rewards를 게임 데이터베이스의 ID와 연결해야 합니다. LLM이 적의 능력치를 직접 생성하게 하면 밸런스 데이터가 오염될 수 있으므로 적은 enemyId로만 참조하게 하는 편이 안전합니다.
LLM 출력은 어떻게 제한해야 할까?
프롬프트에는 설계 목표보다 출력 계약을 먼저 명시합니다. JSON Schema를 사용해 필수 필드, 열거형, 배열 길이, 숫자 범위를 제한하고 검증에 실패하면 해당 출력은 채점 대상에서 제외합니다.
추천하는 출력 계약은 다음과 같습니다.
- 좌표는 정수 격자 단위로만 출력한다.
- 방 ID와 연결 ID는 중복될 수 없다.
- 방 유형은
start,combat,treasure,rest,boss,exit중 하나다. - 적과 아이템은 등록된 ID만 사용한다.
- 잠금 문에는 반드시 대응하는 열쇠 ID가 있다.
- 시작 방과 출구 방은 정확히 하나씩 존재한다.
- LLM은 점수를 직접 쓰지 않고 설계 의도와 배치 데이터만 출력한다.
검증 전에 좌표 정렬, ID 정규화, 누락된 선택 필드의 기본값 적용을 수행합니다. 정규화 결과도 원본과 함께 저장해야 디버깅 시 모델 출력과 시스템 변환을 구분할 수 있습니다.
던전 배치의 유효성은 어떻게 검사할까?
1단계: 공간 제약 검사
각 방을 직사각형으로 간주하면 다음을 빠르게 검사할 수 있습니다.
- 방끼리 겹치지 않는가?
- 모든 방이 던전 경계 안에 있는가?
- 통로의 양 끝이 실제 방에 연결되는가?
- 시작 방에서 출구 방까지 경로가 존재하는가?
그래프 탐색은 BFS 또는 DFS로 충분합니다. 잠금 문이 있는 경우에는 열쇠를 획득한 상태를 포함한 상태 공간 탐색이 필요합니다. 단순히 방 그래프가 연결되어 있는지만 확인하면 열쇠보다 문이 먼저 나오는 진행 불가능 문제가 통과할 수 있습니다.
2단계: 진행 구조 검사
플레이어 상태를 다음처럼 정의할 수 있습니다.
state = (roomId, keys, health, resources)
문을 통과할 때 필요한 열쇠가 keys에 없으면 이동할 수 없게 하고 열쇠를 획득하면 새로운 상태를 큐에 추가합니다. 이 방식으로 보스 방과 출구에 도달 가능한지 검사합니다.
또한 필수 경로와 선택 경로를 분리해 확인합니다. 필수 경로에는 막다른 길이 지나치게 많지 않아야 하며 선택 경로에는 탐험을 보상하는 보물, 정보, 전투 중 하나가 있어야 합니다.
3단계: 플레이 시뮬레이션
배치가 규칙상 유효해도 실제 플레이가 재미있다는 보장은 없습니다. 따라서 여러 플레이어 모델을 사용해 같은 던전을 반복 실행합니다.
| 플레이어 모델 | 주요 행동 | 측정값 |
|---|---|---|
| 안전형 | 회복과 우회로를 우선 | 클리어율, 소요 시간 |
| 공격형 | 전투를 적극적으로 선택 | 체력 잔량, 전투 횟수 |
| 탐험형 | 모든 분기와 보상 탐색 | 탐험률, 경제 수익 |
| 속도형 | 필수 경로만 이동 | 보스 도달 시간, 우회 비율 |
무작위성을 넣을 때는 반드시 시드를 기록합니다. 같은 시드에서 같은 규칙을 적용해야 점수 변화가 배치 수정 때문인지 운의 차이 때문인지 판별할 수 있습니다.
난이도 곡선 적합도는 어떻게 계산할까?
방별 난이도는 단일 전투력 숫자보다 여러 지표의 가중합으로 계산하는 것이 유용합니다. 예를 들어 E는 적 전투력, C는 예상 피격량, R은 회복 자원 부족도, P는 진행 압박도를 의미하게 만들 수 있습니다.
모든 지표는 0과 1 사이로 정규화하고 가중치의 합은 1로 둡니다. 목표 난이도 곡선 T_i는 방의 진행 위치나 플레이 시간에 따라 정의합니다. 초반은 낮게 시작하고 중반에 상승시키며 보스 전투에서 최고점에 도달하게 하는 식입니다.
방별 오차를 평균 절대 오차로 계산하면 특정 방의 큰 이탈을 쉽게 확인할 수 있습니다.
여기서 ω_i는 보스 전 방이나 첫 전투처럼 중요한 구간에 더 큰 가중치를 주는 값입니다. 곡선 점수만으로 합격시키지 말고 하드 제약 위반에는 별도 페널티를 적용해야 합니다.
| 채점 항목 | 예시 통과 조건 |
|---|---|
| 연결성 | 시작점에서 출구까지 모든 필수 경로가 존재 |
| 전투 예산 | 구간별 예상 피해와 전투 시간이 목표 범위 안에 있음 |
| 회복 경제 | 클리어 시 회복 자원 잔량이 목표 범위 안에 있음 |
| 난이도 곡선 | S_curve가 기준점 이상이고 급격한 하락이 없음 |
| 탐험 보상 | 선택 경로마다 유의미한 보상이 존재 |
| 반복 플레이 | 여러 시뮬레이션 시드에서 결과가 과도하게 흔들리지 않음 |

자동 채점 파이프라인은 어떻게 구현할까?
type ScoreReport = {
valid: boolean;
finalScore: number;
violations: string[];
metrics: Record<string, number>;
};
export function evaluateDungeon(dungeon: Dungeon, target: TargetCurve): ScoreReport {
const schemaErrors = validateSchema(dungeon);
if (schemaErrors.length > 0) {
return {
valid: false,
finalScore: 0,
violations: schemaErrors,
metrics: {}
};
}
const violations = [
...checkRoomOverlap(dungeon),
...checkReachability(dungeon),
...checkKeyLockOrder(dungeon),
...checkContentBudget(dungeon)
];
const simulations = runSimulations(dungeon, {
seeds: [11, 23, 47, 71, 103],
playerModels: ['safe', 'aggressive', 'explorer', 'speed']
});
const metrics = aggregateMetrics(simulations);
const curveScore = compareDifficultyCurve(metrics, target);
const penalty = calculatePenalty(violations, metrics);
return {
valid: violations.length === 0,
finalScore: Math.max(0, curveScore - penalty),
violations,
metrics
};
}
실행 순서는 다음과 같이 고정합니다.
- LLM에 시드, 방 수, 테마, 목표 난이도 곡선, 금지 조건을 전달한다.
- 구조화된 JSON 응답을 받아 스키마 검증과 정규화를 수행한다.
- 그래프, 공간, 열쇠와 문, 콘텐츠 예산을 검사한다.
- 통과한 배치만 플레이어 모델 시뮬레이션에 넣는다.
- 곡선 점수와 제약 페널티를 계산한다.
- 실패 원인과 개선 방향을 LLM에 전달해 제한된 횟수만큼 재생성한다.
- 최고 점수 결과와 모든 평가 리포트를 버전별로 저장한다.
flowchart LR
A[시드와 설계 목표] --> B[LLM 구조화 출력]
B --> C[스키마 검증과 정규화]
C --> D[결정론적 제약 검사]
D --> E[플레이 시뮬레이션]
E --> F[난이도 곡선 적합도 계산]
F --> G{실패 원인 분류}
G -->|수정 가능| H[LLM 수정 요청]
H --> C
G -->|통과| I[버전 저장과 리포트]
LLM 수정 요청에는 어떤 피드백을 넣어야 할까?
점수가 낮다는 피드백은 수정에 거의 도움이 되지 않습니다. 실패한 규칙, 발생 위치, 관측값, 목표 범위를 함께 전달해야 합니다.
예를 들어 다음처럼 작성합니다.
r_guardian의 예상 피해량은 0.82로 목표 상한 0.65를 초과했다.
r_guardian과 r_rest 사이의 평균 이동 시간은 18초로 목표 범위 8~12초보다 길다.
r_treasure는 시작점에서 접근 가능하지만 열쇠 보상이 없어 선택 경로 보상 규칙을 위반했다.
기존 방 ID와 연결 ID는 유지하고 위반 항목만 수정하라.
수정 범위를 제한하면 매 반복마다 던전 전체가 바뀌는 문제를 줄일 수 있습니다. 또한 기존 방 ID 유지, 필수 경로 변경 금지, 적 ID는 데이터베이스 목록에서만 선택 같은 불변 조건을 명시하는 것이 좋습니다.
자동 채점에서 자주 발생하는 실패는 무엇인가?
- 곡선만 맞추고 플레이가 불가능한 경우: 하드 제약을 곡선 점수보다 먼저 검사합니다.
- 평균값은 정상인데 특정 구간이 과도하게 어려운 경우: 평균 오차와 함께 최대 국소 오차를 계산합니다.
- LLM이 존재하지 않는 적이나 아이템을 만드는 경우: 자유 텍스트 대신 ID 열거형을 사용합니다.
- 시뮬레이션 결과가 실행마다 달라지는 경우: 난수 시드, 플레이어 모델, 규칙 버전을 모두 저장합니다.
- 재생성이 무한 반복되는 경우: 반복 횟수와 비용 상한을 두고 최고 결과를 반환합니다.
- 경제 밸런스가 전투 밸런스에 밀리는 경우: 골드, 회복량, 소비 아이템을 별도 점수와 페널티로 관리합니다.
자주 묻는 질문 FAQ
LLM이 방 배치를 직접 최종 결정하게 해도 될까?
권장하지 않습니다. LLM은 후보 생성과 실패 원인에 대한 수정안 생성에 사용하고 최종 합격 여부는 스키마, 그래프 검사, 시뮬레이션처럼 재현 가능한 시스템이 결정해야 합니다.
플레이어 시뮬레이션은 몇 번 실행해야 할까?
절대적인 정답은 없습니다. 개발 초기에는 여러 행동 모델과 고정 시드 몇 개로 빠르게 평가하고 출시 후보에서는 더 많은 시드와 실제 플레이 로그 기반 모델을 추가하는 방식이 현실적입니다.
난이도 곡선 점수만 높으면 좋은 던전인가?
아닙니다. 곡선 적합도는 목표 난이도와의 유사성을 보여줄 뿐입니다. 연결성, 반복 플레이의 변동성, 보상 경제, 탐험 가치, 플레이 시간 같은 별도 지표를 함께 통과해야 합니다.
정리
LLM 기반 던전 생성의 핵심은 모델에게 규칙을 맡기는 것이 아니라 모델의 창의적인 제안과 결정론적인 평가를 분리하는 데 있습니다. 방 배치를 그래프로 구조화하고 하드 제약을 먼저 검사한 뒤 여러 플레이어 모델로 시뮬레이션하고 목표 난이도 곡선과 실제 지표의 오차를 점수화하면 생성 결과를 설계 데이터로 비교할 수 있습니다.
이 구조를 적용하면 디자이너는 수많은 배치를 직접 검수하는 대신 실패 원인 리포트와 상위 후보를 중심으로 판단할 수 있습니다. 최종 목표는 LLM이 만든 레벨을 그대로 사용하는 것이 아니라 반복 가능한 평가 체계 안에서 빠르게 탐색하고 검증하는 것입니다.



