Spaces:
Sleeping
Sleeping
humanda5
commited on
Commit
·
96692cf
1
Parent(s):
558f7a4
Complete step 10: 감정 상태 UI 연동 및 테스트 확인
Browse files- app.py +10 -1
- npc_social_network/maps/engine/game_engine.py +95 -1
- npc_social_network/maps/villages/town_hall.py +55 -49
- npc_social_network/npc/npc_base.py +52 -3
- npc_social_network/npc/npc_behavior.py +7 -5
- npc_social_network/npc/npc_emotion.py +26 -2
- templates/main.html +5 -0
- templates/npc_simulation_started.html +14 -0
- test.ipynb +2 -1
app.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
# portfolio/app.py
|
2 |
-
from flask import Flask, render_template
|
3 |
from npc_social_network.routes.npc_route import npc_bp
|
4 |
# from stock.routes.stock_route import stock_bp
|
5 |
import os
|
|
|
6 |
|
7 |
def create_app():
|
8 |
app = Flask(__name__) # static/template 경로를 기본값
|
@@ -15,7 +16,15 @@ def create_app():
|
|
15 |
def index():
|
16 |
return render_template("main.html")
|
17 |
|
|
|
|
|
|
|
|
|
|
|
18 |
|
|
|
|
|
|
|
19 |
return app
|
20 |
|
21 |
app = create_app()
|
|
|
1 |
# portfolio/app.py
|
2 |
+
from flask import Flask, render_template, url_for, redirect
|
3 |
from npc_social_network.routes.npc_route import npc_bp
|
4 |
# from stock.routes.stock_route import stock_bp
|
5 |
import os
|
6 |
+
import subprocess
|
7 |
|
8 |
def create_app():
|
9 |
app = Flask(__name__) # static/template 경로를 기본값
|
|
|
16 |
def index():
|
17 |
return render_template("main.html")
|
18 |
|
19 |
+
@app.route("/run_npc_simulation")
|
20 |
+
def run_npc_simulation():
|
21 |
+
# subprocess로 Pygame 실행
|
22 |
+
python_exec = "python" # 가상환경 사용 필요시 full path로 변경 가능
|
23 |
+
subprocess.Popen([python_exec, "-m", "npc_social_network.maps.villages.town_hall"]) # 비동기로 실행 (창 띄움)
|
24 |
|
25 |
+
# 실행 후 사용자 안내 페이지 표시
|
26 |
+
return render_template("npc_simulation_started.html")
|
27 |
+
|
28 |
return app
|
29 |
|
30 |
app = create_app()
|
npc_social_network/maps/engine/game_engine.py
CHANGED
@@ -1,6 +1,11 @@
|
|
1 |
# portfolio/npc_social_network/maps/engine/game_engine.py
|
2 |
# 게임 루프 및 이벤트 처리
|
3 |
import pygame
|
|
|
|
|
|
|
|
|
|
|
4 |
from datetime import datetime, timedelta
|
5 |
|
6 |
class GameEngine:
|
@@ -10,12 +15,27 @@ class GameEngine:
|
|
10 |
def __init__(self, screen, font, village, time_states, npcs):
|
11 |
self.screen = screen
|
12 |
self.font = font
|
|
|
|
|
13 |
self.village = village
|
14 |
self.time_states = time_states
|
15 |
self.current_time_index = 0
|
16 |
self.frame_count = 0
|
17 |
self.frame_per_time_state = 200 # 기존: 시간 상태 전환 프레임
|
18 |
self.npcs = npcs # 전체 NPC 리스트
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
# 실제 시간 흐름 시뮬레이션용
|
21 |
self.current_time = datetime.now() # 현재 시뮬레이션 시간
|
@@ -32,6 +52,68 @@ class GameEngine:
|
|
32 |
)
|
33 |
self.screen.blit(time_text, (10, 10))
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
def update(self):
|
36 |
self.frame_count += 1
|
37 |
|
@@ -61,4 +143,16 @@ class GameEngine:
|
|
61 |
print(f"\n👤 {npc.name} ({npc.job})")
|
62 |
print(f" - 위치: ({tile_x}, {tile_y})")
|
63 |
print(f" - 대화: {npc.generate_dialogue()}")
|
64 |
-
print(f" - 기억: {npc.recall()}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# portfolio/npc_social_network/maps/engine/game_engine.py
|
2 |
# 게임 루프 및 이벤트 처리
|
3 |
import pygame
|
4 |
+
from pygame import Rect
|
5 |
+
import sys
|
6 |
+
from npc_social_network.maps.manager.image_loader import ImageLoader
|
7 |
+
from npc_social_network.maps.manager.village_map import VillageMap
|
8 |
+
from npc_social_network.npc.npc_manager import NPCManager
|
9 |
from datetime import datetime, timedelta
|
10 |
|
11 |
class GameEngine:
|
|
|
15 |
def __init__(self, screen, font, village, time_states, npcs):
|
16 |
self.screen = screen
|
17 |
self.font = font
|
18 |
+
# emotion HUD용 작은 글자 font 추가
|
19 |
+
self.emotion_font = pygame.font.SysFont("malgungothic", 14)
|
20 |
self.village = village
|
21 |
self.time_states = time_states
|
22 |
self.current_time_index = 0
|
23 |
self.frame_count = 0
|
24 |
self.frame_per_time_state = 200 # 기존: 시간 상태 전환 프레임
|
25 |
self.npcs = npcs # 전체 NPC 리스트
|
26 |
+
self.selected_npc = None # 선택된 NPC 상태 기본 필드
|
27 |
+
|
28 |
+
# 감정 바 색상 테이블 (간단 예시)
|
29 |
+
self.emotion_colors = {
|
30 |
+
"joy": (255, 215, 0),
|
31 |
+
"gratitude": (173, 216, 230),
|
32 |
+
"anger": (255, 69, 0),
|
33 |
+
"fear": (138, 43, 226),
|
34 |
+
"sadness": (70, 130 ,180),
|
35 |
+
"trust": (34, 139, 34),
|
36 |
+
"anticipation": (255, 215, 0),
|
37 |
+
"disgust": (128, 128, 128),
|
38 |
+
}
|
39 |
|
40 |
# 실제 시간 흐름 시뮬레이션용
|
41 |
self.current_time = datetime.now() # 현재 시뮬레이션 시간
|
|
|
52 |
)
|
53 |
self.screen.blit(time_text, (10, 10))
|
54 |
|
55 |
+
# 감정 상태 HUD 렌더링
|
56 |
+
if self.selected_npc:
|
57 |
+
self.draw_emotion_hud()
|
58 |
+
|
59 |
+
def draw_emotion_hud(self):
|
60 |
+
npc = self.selected_npc
|
61 |
+
top_n=5
|
62 |
+
top_emotions = npc.get_top_emotions(top_n=top_n)
|
63 |
+
|
64 |
+
# HUD 위치/크기 설정
|
65 |
+
hud_height = 150 # 원하는 높이
|
66 |
+
margin_right = 20 # 오른쪽 여백
|
67 |
+
margin_bottom = 20 # 아래쪽 여백 (화면 아래 띄우고 싶으면 사용)
|
68 |
+
|
69 |
+
hud_width = self.screen.get_width() * 0.45 # 화면 절반 너비
|
70 |
+
hud_rect = Rect(
|
71 |
+
self.screen.get_width() - hud_width - margin_right,
|
72 |
+
self.screen.get_height() - hud_height - margin_bottom,
|
73 |
+
hud_width,
|
74 |
+
hud_height
|
75 |
+
)
|
76 |
+
pygame.draw.rect(self.screen, (30, 30, 30), hud_rect)
|
77 |
+
|
78 |
+
padding_x = 20 # 오른쪽에서 얼마나 띄울지
|
79 |
+
padding_y = 10 # 위에서 얼마나 띄울지
|
80 |
+
spacing_y = 20 # 감정 간 간격
|
81 |
+
label_bar_gap = 100 # label과 bar 사이 간격
|
82 |
+
title_gap = 30 # 제목과 감정 표시 사이 간격
|
83 |
+
bar_width_max = hud_width - 2 * padding_x - label_bar_gap
|
84 |
+
bar_height = 18
|
85 |
+
|
86 |
+
# 작은 글자용 font 사용
|
87 |
+
font = self.emotion_font
|
88 |
+
|
89 |
+
# 제목
|
90 |
+
self.screen.blit(
|
91 |
+
font.render(f"{npc.name} 감정 상태", True, (255, 255, 255)),
|
92 |
+
(hud_rect.x + padding_x, hud_rect.y + padding_y)
|
93 |
+
)
|
94 |
+
|
95 |
+
# 감정 표시
|
96 |
+
for i, (emotion_name, value) in enumerate(top_emotions):
|
97 |
+
y = hud_rect.y + padding_y + title_gap + i * spacing_y
|
98 |
+
|
99 |
+
text_surface = font.render(f"{emotion_name} ({value:.1f})", True, (255, 255, 255))
|
100 |
+
self.screen.blit(text_surface, (hud_rect.x + padding_x, y))
|
101 |
+
|
102 |
+
# bar background
|
103 |
+
bar_bg_rect = Rect(hud_rect.x + padding_x + label_bar_gap, y + 3, bar_width_max, bar_height)
|
104 |
+
pygame.draw.rect(self.screen, (50, 50, 50), bar_bg_rect)
|
105 |
+
|
106 |
+
# bar fill
|
107 |
+
color = self.emotion_colors.get(emotion_name, (100, 200, 255))
|
108 |
+
# color 값이 유효한 튜플인지 확인
|
109 |
+
if not (isinstance(color, tuple) and len(color) == 3 and all(isinstance(c, int) for c in color)):
|
110 |
+
print(f"[경고] 잘못된 color 값 감지: {emotion_name} → {color}, 디폴트 색상 사용")
|
111 |
+
color = (100, 200, 255)
|
112 |
+
|
113 |
+
bar_fill_width = int((value / 10) * bar_width_max)
|
114 |
+
bar_fill_rect = Rect(hud_rect.x + padding_x + label_bar_gap, y + 3, bar_fill_width, bar_height)
|
115 |
+
pygame.draw.rect(self.screen, color, bar_fill_rect)
|
116 |
+
|
117 |
def update(self):
|
118 |
self.frame_count += 1
|
119 |
|
|
|
143 |
print(f"\n👤 {npc.name} ({npc.job})")
|
144 |
print(f" - 위치: ({tile_x}, {tile_y})")
|
145 |
print(f" - 대화: {npc.generate_dialogue()}")
|
146 |
+
print(f" - 기억: {npc.recall()}")
|
147 |
+
self.set_selected_npc(npc)
|
148 |
+
|
149 |
+
def set_selected_npc(self, npc):
|
150 |
+
self.selected_npc = npc
|
151 |
+
print(f"[HUD] {npc.name} 선택됨 → 감정 상태 표시 시작")
|
152 |
+
|
153 |
+
def clear_selected_npc(self):
|
154 |
+
"""
|
155 |
+
ESC 키로 Selected_npc 해제 메서드
|
156 |
+
"""
|
157 |
+
print("[HUD] 감정 상태 표시 해제됨")
|
158 |
+
self.selected_npc = None
|
npc_social_network/maps/villages/town_hall.py
CHANGED
@@ -1,51 +1,57 @@
|
|
1 |
# portfolio/npc_social_network/maps/villages/town_hall.py
|
2 |
-
# 메인 실행 파일
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
import
|
7 |
-
|
8 |
-
from npc_social_network.maps.manager.
|
9 |
-
from npc_social_network.maps.
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
pygame.display.
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
village
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
pygame.
|
51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# portfolio/npc_social_network/maps/villages/town_hall.py
|
|
|
2 |
|
3 |
+
def run_town_hall_simulation():
|
4 |
+
# 메인 실행 파일
|
5 |
+
import pygame
|
6 |
+
import sys
|
7 |
+
from npc_social_network.maps.manager.image_loader import ImageLoader
|
8 |
+
from npc_social_network.maps.manager.village_map import VillageMap
|
9 |
+
from npc_social_network.maps.engine.game_engine import GameEngine
|
10 |
+
|
11 |
+
# 초기 설정
|
12 |
+
pygame.init()
|
13 |
+
|
14 |
+
tile_size = 32
|
15 |
+
width, height = 20, 20
|
16 |
+
screen = pygame.display.set_mode((width * tile_size, height * tile_size))
|
17 |
+
pygame.display.set_caption("town_hall")
|
18 |
+
|
19 |
+
# 캐릭터가 움직이는 일정 시간
|
20 |
+
clock = pygame.time.Clock()
|
21 |
+
font = pygame.font.SysFont("malgungothic", 20) # 한글 출력용
|
22 |
+
|
23 |
+
# 로딩
|
24 |
+
image_loader = ImageLoader(tile_size)
|
25 |
+
village = VillageMap(image_loader, tile_size, width, height)
|
26 |
+
village.generate()
|
27 |
+
|
28 |
+
|
29 |
+
# npc 리스트 추출
|
30 |
+
npcs = village.npc_manager.npcs
|
31 |
+
|
32 |
+
# 게임 엔진 초기 세팅
|
33 |
+
engine = GameEngine(screen, font, village, ["아침", "점심", "저녁", "밤"], npcs)
|
34 |
+
|
35 |
+
# pygame 루프
|
36 |
+
runnig = True
|
37 |
+
while runnig:
|
38 |
+
screen.fill((255, 255, 255))
|
39 |
+
engine.draw()
|
40 |
+
pygame.display.update()
|
41 |
+
clock.tick(10) # 초당 10프레임
|
42 |
+
engine.update()
|
43 |
+
|
44 |
+
for event in pygame.event.get():
|
45 |
+
if event.type == pygame.QUIT:
|
46 |
+
runnig = False
|
47 |
+
elif event.type == pygame.MOUSEBUTTONDOWN:
|
48 |
+
engine.handle_click(pygame.mouse.get_pos())
|
49 |
+
elif event.type == pygame.KEYDOWN:
|
50 |
+
if event.key == pygame.K_ESCAPE:
|
51 |
+
engine.clear_selected_npc() # ESC로 선택 해제
|
52 |
+
|
53 |
+
pygame.quit()
|
54 |
+
sys.exit()
|
55 |
+
|
56 |
+
if __name__ == "__main__":
|
57 |
+
run_town_hall_simulation()
|
npc_social_network/npc/npc_base.py
CHANGED
@@ -4,6 +4,7 @@ from .npc_emotion import EmotionManager
|
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
from .emotion_config import EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
6 |
from .npc_relationship import RelationshipManager
|
|
|
7 |
|
8 |
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
9 |
POSITIVE_RELATION_EMOTIONS = [
|
@@ -45,6 +46,33 @@ class NPC:
|
|
45 |
self.behavior = BehaviorManager()
|
46 |
# 내부: float 감정 수치 관리용 버퍼
|
47 |
self._emotion_buffer = self.emotion.get_buffer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
49 |
# 확장 가능 속성
|
50 |
# self.emotion = "neutral"
|
@@ -76,10 +104,19 @@ class NPC:
|
|
76 |
behavior_output, behavior_trace = self.behavior.perform_sequence(
|
77 |
self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace = True
|
78 |
)
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
|
|
83 |
importance = min(int(dominant_score), 10) # importance 1~10제한
|
84 |
|
85 |
# Behavior Trace를 Memory에 기록
|
@@ -90,6 +127,13 @@ class NPC:
|
|
90 |
behavior_trace = behavior_trace
|
91 |
)
|
92 |
self.memory_store.add_memory(memory_entry)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
93 |
return behavior_output
|
94 |
|
95 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0):
|
@@ -201,3 +245,8 @@ class NPC:
|
|
201 |
# 관계 갱신
|
202 |
self.relationships.update_relationship(other_npc_name, base_influence)
|
203 |
|
|
|
|
|
|
|
|
|
|
|
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
from .emotion_config import EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
+
import random
|
8 |
|
9 |
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
10 |
POSITIVE_RELATION_EMOTIONS = [
|
|
|
46 |
self.behavior = BehaviorManager()
|
47 |
# 내부: float 감정 수치 관리용 버퍼
|
48 |
self._emotion_buffer = self.emotion.get_buffer()
|
49 |
+
# baseline 감정 값 부여
|
50 |
+
baseline_buffer = {}
|
51 |
+
for emo in self._emotion_buffer:
|
52 |
+
base_min = 0.05
|
53 |
+
base_max = 0.3
|
54 |
+
|
55 |
+
category = EMOTION_CATEGORY_MAP.get(emo, None)
|
56 |
+
|
57 |
+
if category == "core":
|
58 |
+
bias = self.personality.get("affect_bias", 1.0)
|
59 |
+
elif category == "social":
|
60 |
+
bias = self.personality.get("social_bias", 1.0)
|
61 |
+
elif category == "cognitive":
|
62 |
+
bias = self.personality.get("cognitive_bias", 1.0)
|
63 |
+
elif category == "complex":
|
64 |
+
bias = self.personality.get("complex_bias", 1.0)
|
65 |
+
else:
|
66 |
+
bias = self.personality.get("sensitive", 1.0)
|
67 |
+
|
68 |
+
baseline_value = base_min + (base_max - base_min) * bias
|
69 |
+
baseline_value *= self.personality.get("sensitive", 1.0) # 전체 민감도 적용
|
70 |
+
|
71 |
+
baseline_buffer[emo] = round(baseline_value, 3)
|
72 |
+
self._emotion_buffer[emo] = baseline_buffer[emo] # emotion_buffer 초기 적용
|
73 |
+
|
74 |
+
# baseline buffer를 EmotionManager에 등록
|
75 |
+
self.emotion.set_baseline(baseline_buffer)
|
76 |
|
77 |
# 확장 가능 속성
|
78 |
# self.emotion = "neutral"
|
|
|
104 |
behavior_output, behavior_trace = self.behavior.perform_sequence(
|
105 |
self.name, self.job, emotion_buffer=self._emotion_buffer, return_trace = True
|
106 |
)
|
107 |
+
|
108 |
+
# 단계별 dominant emotion sequence 계산
|
109 |
+
dominant_emotions = self.behavior.get_layer_dominant_emotions(self._emotion_buffer)
|
110 |
+
dominant_sequence = self.behavior.decide_layered_sequence(dominant_emotions)
|
111 |
+
|
112 |
+
# dominant_sequence가 empty인 경우 방어적 처리
|
113 |
+
if dominant_sequence:
|
114 |
+
dominant_score = max([score for _, score in dominant_sequence])
|
115 |
+
else:
|
116 |
+
# dominant_sequence가 비었으면 기본값 사용 (예: 감정 없음 상태)
|
117 |
+
dominant_score = 5
|
118 |
|
119 |
+
# 중요도 제한
|
120 |
importance = min(int(dominant_score), 10) # importance 1~10제한
|
121 |
|
122 |
# Behavior Trace를 Memory에 기록
|
|
|
127 |
behavior_trace = behavior_trace
|
128 |
)
|
129 |
self.memory_store.add_memory(memory_entry)
|
130 |
+
|
131 |
+
# 행동에 해당하는 감정에 update_emotion 호출 (정확하게 반영)
|
132 |
+
for action, score in behavior_trace:
|
133 |
+
emotion_name = self.behavior.action_to_emotion.get(action)
|
134 |
+
if emotion_name:
|
135 |
+
self.update_emotion(emotion_name, strength=score)
|
136 |
+
|
137 |
return behavior_output
|
138 |
|
139 |
def remember(self, content: str, importance: int = 5, emotion: str = None, strength: float = 1.0):
|
|
|
245 |
# 관계 갱신
|
246 |
self.relationships.update_relationship(other_npc_name, base_influence)
|
247 |
|
248 |
+
def get_top_emotions(self, top_n=6):
|
249 |
+
"""
|
250 |
+
현재 NPC 감정 상태 상위 N개 반환
|
251 |
+
"""
|
252 |
+
return self.emotion.get_top_emotions(top_n=top_n)
|
npc_social_network/npc/npc_behavior.py
CHANGED
@@ -53,6 +53,9 @@ class BehaviorManager:
|
|
53 |
"groundedness": "steady_posture", # 안정감
|
54 |
}
|
55 |
|
|
|
|
|
|
|
56 |
# Layer 우선 순위 / weight 설정 (튜닝 가능)
|
57 |
self.layer_priority = ["core", "social", "cognitive", "complex"]
|
58 |
self.layer_weights = {
|
@@ -105,7 +108,7 @@ class BehaviorManager:
|
|
105 |
if not sequence:
|
106 |
behavior_output = f"{name}은(는) 특별한 행동을 하지 않습니다."
|
107 |
if return_trace:
|
108 |
-
return behavior_output,
|
109 |
else:
|
110 |
return behavior_output
|
111 |
|
@@ -158,11 +161,11 @@ class BehaviorManager:
|
|
158 |
|
159 |
# 행동 문장 생성
|
160 |
lines = []
|
161 |
-
|
162 |
for action, score in sequence:
|
163 |
msg = behavior_lines.get(action, f"{action} 행동을 합니다.")
|
164 |
lines.append(f"({round(score,1)}) {msg}")
|
165 |
-
|
166 |
|
167 |
# 직업 라인 추가
|
168 |
job_line = {
|
@@ -171,10 +174,9 @@ class BehaviorManager:
|
|
171 |
}.get(job, "오늘도 바쁜 하루네요.")
|
172 |
|
173 |
behavior_output = f"{name} 행동 시퀀스: \n" + "\n".join(lines) + f"\n{job_line}"
|
174 |
-
behavior_trace = ", ".join(trace_lines)
|
175 |
|
176 |
# 최종 출력 문장
|
177 |
if return_trace:
|
178 |
-
return behavior_output,
|
179 |
else:
|
180 |
return behavior_output
|
|
|
53 |
"groundedness": "steady_posture", # 안정감
|
54 |
}
|
55 |
|
56 |
+
# 행동에 따른 감정 (역매핑)
|
57 |
+
self.action_to_emotion = {v: k for k, v in self.mapping.items()}
|
58 |
+
|
59 |
# Layer 우선 순위 / weight 설정 (튜닝 가능)
|
60 |
self.layer_priority = ["core", "social", "cognitive", "complex"]
|
61 |
self.layer_weights = {
|
|
|
108 |
if not sequence:
|
109 |
behavior_output = f"{name}은(는) 특별한 행동을 하지 않습니다."
|
110 |
if return_trace:
|
111 |
+
return behavior_output, []
|
112 |
else:
|
113 |
return behavior_output
|
114 |
|
|
|
161 |
|
162 |
# 행동 문장 생성
|
163 |
lines = []
|
164 |
+
trace_pairs = []
|
165 |
for action, score in sequence:
|
166 |
msg = behavior_lines.get(action, f"{action} 행동을 합니다.")
|
167 |
lines.append(f"({round(score,1)}) {msg}")
|
168 |
+
trace_pairs.append((action, round(score, 1)))
|
169 |
|
170 |
# 직업 라인 추가
|
171 |
job_line = {
|
|
|
174 |
}.get(job, "오늘도 바쁜 하루네요.")
|
175 |
|
176 |
behavior_output = f"{name} 행동 시퀀스: \n" + "\n".join(lines) + f"\n{job_line}"
|
|
|
177 |
|
178 |
# 최종 출력 문장
|
179 |
if return_trace:
|
180 |
+
return behavior_output, trace_pairs
|
181 |
else:
|
182 |
return behavior_output
|
npc_social_network/npc/npc_emotion.py
CHANGED
@@ -3,9 +3,11 @@
|
|
3 |
from .emotion_config import EMOTION_LIST, EMOTION_DECAY_RATE, EMOTION_CATEGORY_MAP
|
4 |
|
5 |
class EmotionManager:
|
6 |
-
def __init__(self, decay_rate, personality =
|
7 |
# 내부: float 감정 수치 관리용 버퍼
|
8 |
self._emotion_buffer = {emo: 0.0 for emo in EMOTION_LIST}
|
|
|
|
|
9 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
10 |
self.decay_rate = decay_rate
|
11 |
self.personality = personality or {}
|
@@ -80,4 +82,26 @@ class EmotionManager:
|
|
80 |
"""
|
81 |
emotion_buffer 복사본 반환
|
82 |
"""
|
83 |
-
return self._emotion_buffer.copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
from .emotion_config import EMOTION_LIST, EMOTION_DECAY_RATE, EMOTION_CATEGORY_MAP
|
4 |
|
5 |
class EmotionManager:
|
6 |
+
def __init__(self, decay_rate, personality=None, initial_baseline=None):
|
7 |
# 내부: float 감정 수치 관리용 버퍼
|
8 |
self._emotion_buffer = {emo: 0.0 for emo in EMOTION_LIST}
|
9 |
+
# baseline 버퍼 추가
|
10 |
+
self._baseline_buffer = initial_baseline or {emo: 0.0 for emo in EMOTION_LIST}
|
11 |
# 감정별 회복 속도 (ms 단위가 아닌 단위당 감소 속도)
|
12 |
self.decay_rate = decay_rate
|
13 |
self.personality = personality or {}
|
|
|
82 |
"""
|
83 |
emotion_buffer 복사본 반환
|
84 |
"""
|
85 |
+
return self._emotion_buffer.copy()
|
86 |
+
|
87 |
+
def get_current_emotions_state(self):
|
88 |
+
"""
|
89 |
+
현재 감정 상태 반환 (dict)
|
90 |
+
"""
|
91 |
+
return self._emotion_buffer
|
92 |
+
|
93 |
+
def get_top_emotions(self, top_n=5):
|
94 |
+
"""
|
95 |
+
현재 감정 버퍼에서 상위 N개 감정 반환
|
96 |
+
:return: List of (emotion_name, value) tuples
|
97 |
+
"""
|
98 |
+
# 정렬 후 상위 N개 반환
|
99 |
+
sorted_emotions = sorted(self._emotion_buffer.items(), key=lambda x: x[1], reverse=True)
|
100 |
+
top_emotions = sorted_emotions[:top_n]
|
101 |
+
return top_emotions
|
102 |
+
|
103 |
+
def set_baseline(self, baseline_buffer):
|
104 |
+
"""
|
105 |
+
baseline buffer를 외부에서 설정 (npc_base.py 초기화 시 사용)
|
106 |
+
"""
|
107 |
+
self._baseline_buffer = baseline_buffer.copy()
|
templates/main.html
CHANGED
@@ -1,3 +1,4 @@
|
|
|
|
1 |
<!DOCTYPE html>
|
2 |
<html lang="en">
|
3 |
<head>
|
@@ -8,5 +9,9 @@
|
|
8 |
<body>
|
9 |
<h1>Welcome to the Portfolio</h1>
|
10 |
<a href="{{ url_for('npc_social.home') }}">Go to Project 1</a>
|
|
|
|
|
|
|
|
|
11 |
</body>
|
12 |
</html>
|
|
|
1 |
+
<!--portfolio/templates/main.html-->
|
2 |
<!DOCTYPE html>
|
3 |
<html lang="en">
|
4 |
<head>
|
|
|
9 |
<body>
|
10 |
<h1>Welcome to the Portfolio</h1>
|
11 |
<a href="{{ url_for('npc_social.home') }}">Go to Project 1</a>
|
12 |
+
<br><br>
|
13 |
+
<form action="{{ url_for('run_npc_simulation') }}" method="get">
|
14 |
+
<button type = "submit"> Run NPC Simulation (pygame) </button>
|
15 |
+
</form>
|
16 |
</body>
|
17 |
</html>
|
templates/npc_simulation_started.html
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!--portfolio/templates/npc_simulation_started.html-->
|
2 |
+
<!DOCTYPE html>
|
3 |
+
<html lang="en">
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title>NPC Simulation Running</title>
|
8 |
+
</head>
|
9 |
+
<body>
|
10 |
+
<h1>NPC Simulation is now running!</h1>
|
11 |
+
<p>You can interact with the Pygame window that has opened.</p>
|
12 |
+
<a href="{{ url_for('index') }}">Back to Main Page</a>
|
13 |
+
</body>
|
14 |
+
</html>
|
test.ipynb
CHANGED
@@ -47,7 +47,8 @@
|
|
47 |
"source": [
|
48 |
"# 나중에 할 내용\n",
|
49 |
"# 1. ai 모델 학습을 위해 어떤 내용이 필요한지\n",
|
50 |
-
"# 2. 학습한 모델을 저장, 활용할 수
|
|
|
51 |
]
|
52 |
},
|
53 |
{
|
|
|
47 |
"source": [
|
48 |
"# 나중에 할 내용\n",
|
49 |
"# 1. ai 모델 학습을 위해 어떤 내용이 필요한지\n",
|
50 |
+
"# 2. 학습한 모델을 저장, 활용할 수 있는지\n",
|
51 |
+
"# 3. NPC 초기 기억 주입"
|
52 |
]
|
53 |
},
|
54 |
{
|