Spaces:
Running
Running
humanda5
commited on
Commit
·
aabeb2d
1
Parent(s):
5b8b298
감정 고도화 테스트 중
Browse files- npc_social_network/npc/npc_base.py +83 -15
- test.ipynb +81 -2
npc_social_network/npc/npc_base.py
CHANGED
@@ -17,7 +17,7 @@ class NPC:
|
|
17 |
# npc 기억
|
18 |
self.memory_store = MemoryStore()
|
19 |
|
20 |
-
# 고차원 감정 상태 정의 (GoEmotions
|
21 |
self.emotion_state = {
|
22 |
# 기본 정서 (긍정)
|
23 |
"core_positive":{
|
@@ -58,6 +58,46 @@ class NPC:
|
|
58 |
"skepticism": 0 # 회의
|
59 |
}
|
60 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
# 확장 가능 속성
|
63 |
# self.emotion = "neutral"
|
@@ -76,24 +116,52 @@ class NPC:
|
|
76 |
x, y = self.get_position()
|
77 |
screen.blit(self.image, (x * tile_size, y * tile_size))
|
78 |
|
79 |
-
# 감정
|
80 |
def generate_dialogue(self):
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
return "망치를 들고 일할 준비가 되었어요!"
|
89 |
-
return "좋은 하루입니다."
|
90 |
|
91 |
-
# NPC가 새로운 기억을
|
92 |
-
def remember(self, content: str, importance: int = 5, emotion: str =
|
93 |
-
memory = Memory(content=content, importance = importance, emotion=emotion)
|
94 |
self.memory_store.add_memory(memory)
|
|
|
|
|
95 |
|
96 |
# 저장된 모든 기억(단기+장기)을 리스트로 반환
|
97 |
def recall(self):
|
98 |
return [m.content for m in self.memory_store.get_all_memories()]
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
# npc 기억
|
18 |
self.memory_store = MemoryStore()
|
19 |
|
20 |
+
# 고차원 감정 상태 정의 (GoEmotions 기반, 보여주기용)
|
21 |
self.emotion_state = {
|
22 |
# 기본 정서 (긍정)
|
23 |
"core_positive":{
|
|
|
58 |
"skepticism": 0 # 회의
|
59 |
}
|
60 |
}
|
61 |
+
|
62 |
+
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
63 |
+
self.emotion_decay_rate = {
|
64 |
+
# 기본 정서 (긍정)
|
65 |
+
"joy": 1, # 기쁨
|
66 |
+
"satisfaction": 1, # 만족
|
67 |
+
"gratitude": 1, # 감사
|
68 |
+
"calm": 1, # 평온
|
69 |
+
"anticipation": 1, # 기대
|
70 |
+
"pride": 1, # 자부심
|
71 |
+
"connectedness": 1, # 유대감
|
72 |
+
|
73 |
+
# 기본 정서 (부정) - 느림
|
74 |
+
"sadness": 0.2, # 슬픔
|
75 |
+
"anger": 0.5, # 분노
|
76 |
+
"anxiety": 0.4, # 불안
|
77 |
+
"disgust": 0.8, # 혐오
|
78 |
+
"fear": 0.6, # 공포
|
79 |
+
"regret": 0.3, # 후회
|
80 |
+
"frustration": 0.5, # 좌절
|
81 |
+
|
82 |
+
# 사회적 감정
|
83 |
+
"jealousy": 0.4, # 질투
|
84 |
+
"shame": 0.4, # 수치심
|
85 |
+
"guilt": 0.4, # 죄책감
|
86 |
+
"compassion": 1, # 동정심
|
87 |
+
"awe": 1, # 경외심
|
88 |
+
"empathy": 1, # 공감
|
89 |
+
|
90 |
+
# 인지적 상태
|
91 |
+
"confusion": 0.8, # 혼란
|
92 |
+
"nervousness": 0.7, # 긴장
|
93 |
+
"apathy": 0.4, # 무관심
|
94 |
+
"excitement": 1.2, # 흥분
|
95 |
+
"immersion": 1.0, # 몰입
|
96 |
+
"skepticism": 0.5, # 회의
|
97 |
+
}
|
98 |
+
|
99 |
+
# 내부: float 감정 수치 관리용 버퍼
|
100 |
+
self._emotion_buffer = {emo: 0.0 for cat in self.emotion_state for emo in self.emotion_state[cat]}
|
101 |
|
102 |
# 확장 가능 속성
|
103 |
# self.emotion = "neutral"
|
|
|
116 |
x, y = self.get_position()
|
117 |
screen.blit(self.image, (x * tile_size, y * tile_size))
|
118 |
|
119 |
+
# 감정 상태와 직업에 따른 대사 생성 (대표 감정 하나 반영)
|
120 |
def generate_dialogue(self):
|
121 |
+
dominant = self.get_dominant_emotion()
|
122 |
+
emotion_line = f"지금 저는 '{dominant}' 상태예요." if dominant else "기분이 안정돼 있어요."
|
123 |
+
job_line = {
|
124 |
+
"farmer": "밭일을 해야겠어요.",
|
125 |
+
"blacksmith": "대장간에 불을 지펴야겠군요."
|
126 |
+
}.get(self.job, "오늘도 바쁘네요.")
|
127 |
+
return f"{emotion_line} {job_line}"
|
|
|
|
|
128 |
|
129 |
+
# NPC가 새로운 기억을 저장하고 감정 상태에 반영
|
130 |
+
def remember(self, content: str, importance: int = 5, emotion: str = None):
|
131 |
+
memory = Memory(content=content, importance = importance, emotion=emotion or "neutral")
|
132 |
self.memory_store.add_memory(memory)
|
133 |
+
if emotion:
|
134 |
+
self.update_emotion(emotion)
|
135 |
|
136 |
# 저장된 모든 기억(단기+장기)을 리스트로 반환
|
137 |
def recall(self):
|
138 |
return [m.content for m in self.memory_store.get_all_memories()]
|
139 |
+
|
140 |
+
# 특정 감정을 감정 상태에 반영 (카테고리 기반)
|
141 |
+
def update_emotion(self, emotion: str, strength: int = 1):
|
142 |
+
if emotion in self._emotion_buffer:
|
143 |
+
self._emotion_buffer[emotion] += strength
|
144 |
+
|
145 |
+
# 시간이 지남에 따라 모든 감정이 서서히 감소
|
146 |
+
def decay_emotion(self):
|
147 |
+
for emotion in self._emotion_buffer:
|
148 |
+
rate = self.emotion_decay_rate.get(emotion, 1.0)
|
149 |
+
self._emotion_buffer[emotion] = max(0.0, self._emotion_buffer[emotion] - rate)
|
150 |
+
|
151 |
+
# 정수 기반 출력용 감정 상태 반환
|
152 |
+
def get_current_emotion_state(self):
|
153 |
+
display_state = {cat: {} for cat in self.emotion_state}
|
154 |
+
for cat in self.emotion_state:
|
155 |
+
for emo in self.emotion_state[cat]:
|
156 |
+
display_state[cat][emo] = float(self._emotion_buffer[emo])
|
157 |
+
return display_state
|
158 |
+
|
159 |
+
# 현재 가장 강한 감정 반환 (전체 감정 중 최댓값 1개)
|
160 |
+
def get_dominant_emotion(self):
|
161 |
+
max_value = 0.0
|
162 |
+
max_emotion = None
|
163 |
+
for emo, val in self._emotion_buffer.items():
|
164 |
+
if val > max_value:
|
165 |
+
max_value = val
|
166 |
+
max_emotion = emo
|
167 |
+
return max_emotion
|
test.ipynb
CHANGED
@@ -2711,7 +2711,13 @@
|
|
2711 |
"output_type": "stream",
|
2712 |
"text": [
|
2713 |
"pygame 2.6.1 (SDL 2.28.4, Python 3.11.11)\n",
|
2714 |
-
"Hello from the pygame community. https://www.pygame.org/contribute.html\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
2715 |
]
|
2716 |
},
|
2717 |
{
|
@@ -2727,7 +2733,7 @@
|
|
2727 |
"name": "stderr",
|
2728 |
"output_type": "stream",
|
2729 |
"text": [
|
2730 |
-
"
|
2731 |
" warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
|
2732 |
]
|
2733 |
}
|
@@ -2745,6 +2751,79 @@
|
|
2745 |
"\n",
|
2746 |
"# town_hall.py는 __main__이 아니라 직접 실행되므로"
|
2747 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2748 |
}
|
2749 |
],
|
2750 |
"metadata": {
|
|
|
2711 |
"output_type": "stream",
|
2712 |
"text": [
|
2713 |
"pygame 2.6.1 (SDL 2.28.4, Python 3.11.11)\n",
|
2714 |
+
"Hello from the pygame community. https://www.pygame.org/contribute.html\n",
|
2715 |
+
"\n",
|
2716 |
+
"👤 Elin (farmer)\n",
|
2717 |
+
" - 위치: (5, 4)\n",
|
2718 |
+
" - 대화: 오늘은 밭에 나갈 날이군요!\n",
|
2719 |
+
" - 기억: []\n",
|
2720 |
+
"아침 - Elin이(가) market에 도착했습니다.\n"
|
2721 |
]
|
2722 |
},
|
2723 |
{
|
|
|
2733 |
"name": "stderr",
|
2734 |
"output_type": "stream",
|
2735 |
"text": [
|
2736 |
+
"C:\\Users\\human\\AppData\\Roaming\\Python\\Python311\\site-packages\\IPython\\core\\interactiveshell.py:3675: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.\n",
|
2737 |
" warn(\"To exit: use 'exit', 'quit', or Ctrl-D.\", stacklevel=1)\n"
|
2738 |
]
|
2739 |
}
|
|
|
2751 |
"\n",
|
2752 |
"# town_hall.py는 __main__이 아니라 직접 실행되므로"
|
2753 |
]
|
2754 |
+
},
|
2755 |
+
{
|
2756 |
+
"cell_type": "code",
|
2757 |
+
"execution_count": null,
|
2758 |
+
"id": "ac67ce84",
|
2759 |
+
"metadata": {},
|
2760 |
+
"outputs": [
|
2761 |
+
{
|
2762 |
+
"name": "stdout",
|
2763 |
+
"output_type": "stream",
|
2764 |
+
"text": [
|
2765 |
+
"초기 감정 상태:\n",
|
2766 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n",
|
2767 |
+
"\n",
|
2768 |
+
"[1회 decay 후]\n",
|
2769 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n",
|
2770 |
+
"\n",
|
2771 |
+
"[2회 decay 후]\n",
|
2772 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n",
|
2773 |
+
"\n",
|
2774 |
+
"[3회 decay 후]\n",
|
2775 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n",
|
2776 |
+
"\n",
|
2777 |
+
"[4회 decay 후]\n",
|
2778 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n",
|
2779 |
+
"\n",
|
2780 |
+
"[5회 decay 후]\n",
|
2781 |
+
"{'core_positive': {'joy': 0, 'satisfaction': 0, 'gratitude': 0, 'calm': 0, 'anticipation': 0, 'pride': 0, 'connectedness': 0}, 'core_negative': {'sadness': 0, 'anger': 0, 'anxiety': 0, 'disgust': 0, 'fear': 0, 'regret': 0, 'frustration': 0}, 'social_emotion': {'jealousy': 0, 'shame': 0, 'guilt': 0, 'compassion': 0, 'awe': 0, 'empathy': 0}, 'cognitive_state': {'confusion': 0, 'nervousness': 0, 'apathy': 0, 'excitement': 0, 'immersion': 0, 'skepticism': 0}}\n"
|
2782 |
+
]
|
2783 |
+
}
|
2784 |
+
],
|
2785 |
+
"source": [
|
2786 |
+
"# 1. 경로 추가\n",
|
2787 |
+
"import sys, os\n",
|
2788 |
+
"sys.path.append(os.path.abspath(\".\")) # 프로젝트 루트 경로\n",
|
2789 |
+
"\n",
|
2790 |
+
"# 2. 클래스 import\n",
|
2791 |
+
"from npc_social_network.npc.npc_base import NPC\n",
|
2792 |
+
"\n",
|
2793 |
+
"# 3. NPC 생성 및 감정 입력\n",
|
2794 |
+
"npc = NPC(name=\"아린\", job=\"farmer\", path=[(0,0)], image=None)\n",
|
2795 |
+
"npc.remember(\"플레이어가 모욕함\", importance=6, emotion=\"anger\")\n",
|
2796 |
+
"npc.remember(\"무서운 소리를 들음\", importance=5, emotion=\"fear\")\n",
|
2797 |
+
"npc.remember(\"친구가 칭찬함\", importance=5, emotion=\"pride\")\n",
|
2798 |
+
"\n",
|
2799 |
+
"print(\"초기 감정 상태:\")\n",
|
2800 |
+
"print(npc.get_current_emotion_state())\n",
|
2801 |
+
"\n",
|
2802 |
+
"# 4. decay 반복 적용\n",
|
2803 |
+
"for step in range(1, 6):\n",
|
2804 |
+
" npc.decay_emotion()\n",
|
2805 |
+
" print(f\"\\n[{step}회 decay 후]\")\n",
|
2806 |
+
" print(npc.get_current_emotion_state())"
|
2807 |
+
]
|
2808 |
+
},
|
2809 |
+
{
|
2810 |
+
"cell_type": "code",
|
2811 |
+
"execution_count": 5,
|
2812 |
+
"id": "cc6f2948",
|
2813 |
+
"metadata": {},
|
2814 |
+
"outputs": [
|
2815 |
+
{
|
2816 |
+
"name": "stdout",
|
2817 |
+
"output_type": "stream",
|
2818 |
+
"text": [
|
2819 |
+
"d:\\private\\Portfolio\\npc_social_network\\npc\\npc_base.py\n"
|
2820 |
+
]
|
2821 |
+
}
|
2822 |
+
],
|
2823 |
+
"source": [
|
2824 |
+
"import npc_social_network.npc.npc_base as base\n",
|
2825 |
+
"print(base.__file__)"
|
2826 |
+
]
|
2827 |
}
|
2828 |
],
|
2829 |
"metadata": {
|