humanda5
Complete step 10: 감정 μƒνƒœ UI 연동 및 ν…ŒμŠ€νŠΈ 확인
96692cf
# 감정-행동 연동 λ§€ν•‘ 및 행동 생성
# portfolio/npc_social_network/npc/npc_behavior.py
from .emotion_config import EMOTION_CATEGORY_MAP
class BehaviorManager:
def __init__(self):
self.mapping = {
# Core (κΈ°λ³Έ 감정)
"joy": "jump", # 기쁨 : 점프 λ›°λ‹€
"sadness": "sit_down", # μŠ¬ν”” : μ£Όμ € 앉닀
"anger": "shout", # λΆ„λ…Έ : μ†Œλ¦¬μΉ˜λ‹€
"fear": "run_away", # 곡포 : λ„λ§μΉ˜λ‹€
"disgust": "avoid", # 혐였 : ν”Όν•˜λ‹€
"surprise": "gasp", # λ†€λžŒ : κ°νƒ„ν•˜λ©° μˆ¨μ„ 듀이쉼
"neutral": "idle", # 쀑립 : 아무것도 ν•˜μ§€ μ•ŠμŒ
# Social (μ‚¬νšŒμ  감정)
"gratitude": "thank", # 감사 : κ°μ‚¬ν•˜λ‹€
"shame": "hide_face", # μˆ˜μΉ˜μ‹¬ : 얼꡴을 감좔닀
"guilt": "apologize", # 죄책감 : λ―Έμ•ˆν•˜λ‹€κ³  말함
"pride": "show_off", # μžλΆ€μ‹¬ : μžλž‘ν•˜λ‹€
"jealousy": "glare", # 질투 : 노렀보닀
"compassion": "console", # μ—°λ―Ό/동정 : μœ„λ‘œν•˜λ‹€
"love": "embrace", # μ‚¬λž‘/μ• μ • : ν¬μ˜Ήν•˜λ‹€
"admiration": "cheer", # μ‘΄κ²½/감탄 : ν™˜ν˜Έν•˜λ‹€
"empathy": "listen_carefully", # 곡감 : μ§‘μ€‘ν•˜λ‹€
"awe": "gasp", # 경외심 : κ°νƒ„ν•˜λ©° μˆ¨μ„ 듀이쉼
"attachment": "hold_hands", # μ• μ°© : μ†Œμœ ν•˜λ‹€
"comfort": "pat", # μœ„λ‘œ 좔ꡬ : ν† λ‹₯거리닀
# Cognitive (인지적 μƒνƒœ)
"anticipation": "prepare", # κΈ°λŒ€ : μ€€λΉ„ν•˜λ‹€
"curiosity": "inspect", # ν˜ΈκΈ°μ‹¬ : μ‚΄νŽ΄λ³΄λ‹€
"confusion": "scratch_head", # ν˜Όλž€ : 머리λ₯Ό 긁닀
"interest": "lean_forward", # ν₯λ―Έ : μ§‘μ€‘ν•˜λ‹€
"engagement": "focus", # λͺ°μž… : μ§‘μ€‘ν•˜λ‹€
"boredom": "idle", # 지루함 : 아무것도 ν•˜μ§€ μ•ŠμŒ
"relief": "exhale", # μ•ˆλ„ : μˆ¨μ„ 내쉬닀
"anxiety": "fidget", # λΆˆμ•ˆ/κΈ΄μž₯ : μ•ˆμ ˆλΆ€μ ˆ λͺ»ν•˜λ‹€
"calm": "relax", # ν‰μ˜¨ : μ§„μ •ν•˜λ‹€
"skepticism": "raise_eyebrow", # 회의 : λˆˆμΉμ„ μ˜¬λ¦¬λ‹€
# Complex (볡합 감정)
"nostalgia": "stare_into_distance", # ν–₯수
"bittersweet": "smile_then_look_down", # λ‹¬μ½€μŒ‰μ‹Έλ¦„ν•¨
"schadenfreude": "smirk", # λ‚¨μ˜ λΆˆν–‰μ—μ„œ λŠλΌλŠ” 기쁨
"hope": "brighten_up", # 희망
"resentment": "scowl", # λΆ„κ°œ
"anticipatory_joy": "rub_hands", # κΈ°λŒ€ μ†μ˜ 기쁨
"regret": "look_down", # ν›„νšŒ
"rumination": "mutter_to_self", # λ°˜μΆ”
"groundedness": "steady_posture", # μ•ˆμ •κ°
}
# 행동에 λ”°λ₯Έ 감정 (μ—­λ§€ν•‘)
self.action_to_emotion = {v: k for k, v in self.mapping.items()}
# Layer μš°μ„  μˆœμœ„ / weight μ„€μ • (νŠœλ‹ κ°€λŠ₯)
self.layer_priority = ["core", "social", "cognitive", "complex"]
self.layer_weights = {
"core" : 1.0,
"social" : 0.8,
"cognitive" : 0.6,
"complex" : 0.4,
}
def get_layer_dominant_emotions(self, buffer):
"""
layer별 dominant emotion 계산
"""
layer_emotions = {layer: [] for layer in self.layer_priority}
for emo, val in buffer.items():
if val > 0:
layer = EMOTION_CATEGORY_MAP.get(emo)
if layer:
layer_emotions[layer].append((emo, val))
# 각 layerμ—μ„œ κ°€μž₯ κ°•ν•œ 감정 μΆ”μΆœ
layer_dominant = {}
for layer, emos in layer_emotions.items():
if emos:
dominant_emo = max(emos, key=lambda x: x[1])
layer_dominant[layer] = dominant_emo
return layer_dominant
def decide_layered_sequence(self, layer_dominant_emotions):
"""
layer priority + weight 기반 μ‹œν€€μŠ€ 생성
"""
sequence = []
for layer in self.layer_priority:
if layer in layer_dominant_emotions:
emo, score = layer_dominant_emotions[layer]
weighted_score = round(score * self.layer_weights[layer], 2)
action = self.mapping.get(emo)
if action:
sequence.append((action, weighted_score))
return sequence
def perform_sequence(self, name, job, emotion_buffer, return_trace=False):
"""
Layer-aware Behavior μ‹œν€€μŠ€ ꡬ성 + Behavior Trace λ°˜ν™˜
"""
layer_dominant_emotions = self.get_layer_dominant_emotions(emotion_buffer)
sequence = self.decide_layered_sequence(layer_dominant_emotions)
if not sequence:
behavior_output = f"{name}은(λŠ”) νŠΉλ³„ν•œ 행동을 ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€."
if return_trace:
return behavior_output, []
else:
return behavior_output
# 행동 ν…μŠ€νŠΈ ν…œν”Œλ¦Ώ
behavior_lines = {
# Core
"jump": "κΈ°λ»μ„œ κΉ‘μΆ© λœλ‹ˆλ‹€.",
"sit_down": "μŠ¬νΌμ„œ μ£Όμ €μ•‰μŠ΅λ‹ˆλ‹€....",
"shout": "ν™”κ°€ λ‚˜μ„œ μ†Œλ¦¬λ₯Ό μ§€λ¦…λ‹ˆλ‹€!",
"run_away": "λ¬΄μ„œμ›Œ λ„λ§κ°‘λ‹ˆλ‹€!",
"avoid": "λ’€λ‘œ λ¬ΌλŸ¬μ„œ ν”Όν•˜λ € ν•©λ‹ˆλ‹€.",
"gasp": "깜짝 λ†€λžλ‹ˆλ‹€.",
"idle": "κ°€λ§Œνžˆ μžˆμŠ΅λ‹ˆλ‹€.",
# Social
"thank": "κ°μ‚¬ν•œ λ§ˆμŒμ„ ν‘œν˜„ν•©λ‹ˆλ‹€.",
"hide_face": "μˆ˜μΉ˜μ‹¬μ— 얼꡴을 κ°€λ¦½λ‹ˆλ‹€.",
"apologize": "λ―Έμ•ˆν•˜λ‹€κ³  λ§ν•©λ‹ˆλ‹€.",
"show_off": "μžλž‘μŠ€λŸ¬μš΄ λͺ¨μŠ΅μ„ λ³΄μ—¬μ€λ‹ˆλ‹€.",
"glare": "μƒλŒ€λ₯Ό λ…Έλ €λ΄…λ‹ˆλ‹€.",
"console": "μƒλŒ€λ°©μ„ λ”°λœ»ν•˜κ²Œ μœ„λ‘œν•©λ‹ˆλ‹€.",
"embrace": "μƒλŒ€λ₯Ό ν¬μ˜Ήν•©λ‹ˆλ‹€.",
"cheer": "κ°νƒ„ν•˜λ©° μ‘μ›ν•©λ‹ˆλ‹€.",
"listen_carefully": "μƒλŒ€μ˜ 말을 μ§‘μ€‘ν•΄μ„œ λ“£μŠ΅λ‹ˆλ‹€.",
"hold_hands": "손을 μž‘μŠ΅λ‹ˆλ‹€.",
"pat": "κ°€λ³κ²Œ 등을 λ‘λ“œλ € μœ„λ‘œν•©λ‹ˆλ‹€.",
# Cognitive
"prepare": "무언가λ₯Ό μ€€λΉ„ν•©λ‹ˆλ‹€.",
"inspect": "μœ μ‹¬νžˆ κ΄€μ°°ν•©λ‹ˆλ‹€.",
"scratch_head": "머리λ₯Ό κΈμ μž…λ‹ˆλ‹€.",
"lean_forward": "μ•žμœΌλ‘œ μˆ™μ΄λ©° μ§‘μ€‘ν•©λ‹ˆλ‹€.",
"focus": "λͺ°μž…ν•˜μ—¬ μ§‘μ€‘ν•©λ‹ˆλ‹€.",
"exhale": "μ•ˆλ„ν•˜λ©° μˆ¨μ„ μ‰½λ‹ˆλ‹€.",
"fidget": "λΆˆμ•ˆν•˜κ²Œ λͺΈμ„ μ›€μ§μž…λ‹ˆλ‹€.",
"relax": "ν‰μ˜¨ν•˜κ²Œ νœ΄μ‹ν•©λ‹ˆλ‹€.",
"raise_eyebrow": "μ˜μ‹¬μŠ€λŸ½κ²Œ λˆˆμΉμ„ μΉ˜μΌœλœΉλ‹ˆλ‹€.",
# Complex
"stare_into_distance": "λ¨Ό 곳을 바라보며 νšŒμƒμ— μž κΉλ‹ˆλ‹€.",
"smile_then_look_down": "λ―Έμ†Œλ₯Ό μ§“λ‹€κ°€ 고개λ₯Ό μˆ™μž…λ‹ˆλ‹€.",
"smirk": "λΉ„μ›ƒλŠ” ν‘œμ •μ„ μ§“μŠ΅λ‹ˆλ‹€.",
"brighten_up": "κΈ°λŒ€κ°μ— λˆˆλΉ›μ΄ λΉ›λ‚©λ‹ˆλ‹€.",
"scowl": "인상을 μ°Œν‘Έλ¦½λ‹ˆλ‹€.",
"rub_hands": "λ“€λœ¬ λ§ˆμŒμ— 손을 λΉ„λΉ•λ‹ˆλ‹€.",
"look_down": "고개λ₯Ό μˆ™μ΄λ©° ν›„νšŒν•©λ‹ˆλ‹€.",
"mutter_to_self": "ν˜Όμž£λ§μ„ μ€‘μ–Όκ±°λ¦½λ‹ˆλ‹€.",
"steady_posture": "μ•ˆμ •λœ μžμ„Έλ₯Ό μœ μ§€ν•©λ‹ˆλ‹€.",
}
# 행동 λ¬Έμž₯ 생성
lines = []
trace_pairs = []
for action, score in sequence:
msg = behavior_lines.get(action, f"{action} 행동을 ν•©λ‹ˆλ‹€.")
lines.append(f"({round(score,1)}) {msg}")
trace_pairs.append((action, round(score, 1)))
# 직업 라인 μΆ”κ°€
job_line = {
"farmer": "밭일도 ν•΄μ•Ό ν•˜κ² κ΅°μš”.",
"blacksmith": "λŒ€μž₯κ°„ λΆˆλ„ μ§€νŽ΄μ•Όμ£ .",
}.get(job, "μ˜€λŠ˜λ„ λ°”μœ ν•˜λ£¨λ„€μš”.")
behavior_output = f"{name} 행동 μ‹œν€€μŠ€: \n" + "\n".join(lines) + f"\n{job_line}"
# μ΅œμ’… 좜λ ₯ λ¬Έμž₯
if return_trace:
return behavior_output, trace_pairs
else:
return behavior_output