|
from langchain_upstage import ChatUpstage |
|
from langchain_core.prompts import PromptTemplate |
|
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser |
|
import ast |
|
from . import formatter |
|
import random |
|
from openai import OpenAI |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_initial_conversation(world_summary, player_profile, player_restriction, player_capability, entire_story): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
Please explain the initial introduction which includes the greeting message as a game master and explanation of the universe and situation to make player understand the context. |
|
Introduction should include brief explanation of the story before the first round begins, but do not include the spoiler of the detailed entire story. |
|
Introduction should include explanation of the player's profile, restriction, and capability to make player understand the context. |
|
Introduction should include background knowledge of the fictional universe to make player understand the situation. |
|
Introduction should be wrote in the tone of the game master. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Entire Story: {entire_story} |
|
--- |
|
Introduction: |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
introduction = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "entire_story": entire_story }) |
|
return introduction |
|
|
|
|
|
|
|
def create_round_description(world_summary, player_profile, player_restriction, player_capability, entire_story, round_story, previous_conversation, previous_round_result): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
Please create round description which will be displayed to player that can explain this round story to make player understand the situation. |
|
Round Description should include the main event of this round story. |
|
Round Description should be finished with the question to player to make a decision for the next step. |
|
Round Description should consider this round story, entire story, player profile, player restriction, player capability, previous conversation, and previous round effect. |
|
Round Description should be interesting and naturally connected to the previous conversation, with long and detailed explanation of story. |
|
Round Description should be wrote in the tone of the game master. |
|
Round Description must start with explanation of player's previous round result and reason if below Previous Round Result section is not empty. |
|
Round Description should reflect the player's previous choices and their effects on this round story, so it can include slightly different from this round story but follow the entire story in big picture. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Entire Story: {entire_story} |
|
--- |
|
This Round Story: {round_story} |
|
--- |
|
Previous Conversation: {previous_conversation} |
|
--- |
|
Previous Round Result: {previous_round_result} |
|
--- |
|
Round Description: |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
while(True): |
|
round_description = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "entire_story": entire_story, "round_story": round_story, "previous_conversation": previous_conversation, "previous_round_result": previous_round_result }) |
|
if "1." in round_description and '2.' in round_description: |
|
continue |
|
|
|
return round_description |
|
|
|
|
|
def get_required_capabilities(world_summary, player_profile, player_restriction, player_capability, round_description, player_response): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in english. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
This round story is included in This Round Description part. |
|
Please select the required player capability to successfully conduct the player's response action. |
|
Required Capability should be selected from player capability. |
|
Required Capability should be selected based on player response and round description. |
|
Response should be python list format consists of string which is keys of required player capability. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Round Description: {round_description} |
|
--- |
|
Player Response: {player_response} |
|
--- |
|
Required Capability: ["Required capabilities on english", ...] |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
|
|
while(True): |
|
try: |
|
response = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "round_description": round_description, "player_response": player_response }) |
|
response = '[' + response.split('[')[-1] |
|
response_list = ast.literal_eval(response) |
|
if len(response_list) != 0: |
|
return response_list |
|
except: |
|
continue |
|
|
|
|
|
def restrict_effect_range(effect): |
|
new_restriction = effect.get('player_restriction') |
|
new_capability = effect.get('player_capability') |
|
|
|
for key, value in effect.get('player_restriction').items(): |
|
if value > 10: |
|
new_restriction[key] = 10 |
|
elif value < -10: |
|
new_capability[key] = -10 |
|
|
|
for key, value in effect.get('player_capability').items(): |
|
if value > 20: |
|
new_capability[key] = 20 |
|
elif value < -20: |
|
new_capability[key] = -20 |
|
|
|
return { "player_restriction": new_restriction, "player_capability": new_capability } |
|
|
|
|
|
def get_expected_result(world_summary, player_profile, player_restriction, player_capability, round_description, player_response): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
This round story is included in This Round Description part. |
|
Please create the effect from player's response from this round story, and reason to explain the effect. |
|
Effect should be calculated based on player response, player restriction, player capability, player profile, and round description. |
|
Effect should reduce or increase player restriction in range between (-10, 10) with reasonable amount from player response's action. |
|
Effect should reduce or increase player capability in range between (-20, 20) with reasonable amount from player response's action if it seems to be change by player's action. |
|
Reason should explain why the effect is happened based on player response and round description. |
|
Reason should logically explain the amount of changes of player restriction and capability. |
|
Reason should be wrote in the tone of the game master. |
|
Response should follow below format which is python dictionary consist of effect and reason. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Round Description: {round_description} |
|
--- |
|
Player Response: {player_response} |
|
--- |
|
format: |
|
{{ |
|
effect: {{ |
|
player_restriction: {{ |
|
life: integer of life amount change between (-10, 10), |
|
money: integer of money amount change between (-10, 10) |
|
}}, |
|
player_capability: {{ |
|
stamina: integer of stamina amount change between (-20, 20), |
|
intelligence: integer of intelligence amount change between (-20, 20), |
|
combat_power: integer of combat power amount change between (-20, 20), |
|
agility: integer of agility amount change between (-20, 20), |
|
}} |
|
}} |
|
reason: "reason of the effect which is changes of player restriction, and capability based on player response and round description", |
|
}} |
|
--- |
|
""" |
|
) |
|
chain = prompt_template | llm | JsonOutputParser() |
|
|
|
while(True): |
|
try: |
|
response = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "round_description": round_description, "player_response": player_response }) |
|
|
|
|
|
if response.get('effect').get('player_restriction') == None or response.get("effect").get('player_capability') == None or response.get("reason") == None: |
|
raise Exception() |
|
|
|
response['effect'] = restrict_effect_range(response['effect']) |
|
return response |
|
except: |
|
continue |
|
|
|
|
|
def get_unexpected_result(world_summary, player_profile, player_restriction, player_capability, not_enough_capability, round_description, player_response): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
This round story is included in This Round Description part. |
|
Please create the unexpected effect from player's response from this round story, and reason to explain the effect. |
|
Player's response is failed to do intended action because some player's capability is not enough to conduct the action. |
|
Not enough capability is included in Required Capability part. |
|
Effect should be calculated based on player response, player restriction, player capability, player profile, and round description. |
|
Effect should reduce or increase player restriction in range between (-10, 10) with reasonable amount from player response's action failure. |
|
Effect should reduce or increase player capability in range between (-20, 20) with reasonable amount from player response's action failure if it seems to be change by player's action failure. |
|
Reason should explain why this kind of unintended effect is happened based on situation. |
|
Reason should logically explain the amount of changes of player restriction and capability. |
|
Reason should be wrote in the tone of the game master. |
|
Response should follow below format which is python dictionary consist of effect and reason. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Required Capability: {not_enough_capability} |
|
--- |
|
Round Description: {round_description} |
|
--- |
|
Player Response: {player_response} |
|
--- |
|
format: |
|
{{ |
|
effect: {{ |
|
player_restriction: {{ |
|
life: integer of life amount change between (-10, 10), |
|
money: integer of money amount change between (-10, 10) |
|
}}, |
|
player_capability: {{ |
|
stamina: integer of stamina amount change between (-20, 20), |
|
intelligence: integer of intelligence amount change between (-20, 20), |
|
combat_power: integer of combat power amount change between (-20, 20), |
|
agility: integer of agility amount change between (-20, 20), |
|
}} |
|
}} |
|
reason: "reason of the effect which is changes of player restriction, and capability based on player response's failure and round description", |
|
}} |
|
--- |
|
""" |
|
) |
|
chain = prompt_template | llm | JsonOutputParser() |
|
|
|
while(True): |
|
try: |
|
response = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "not_enough_capability": not_enough_capability, "round_description": round_description, "player_response": player_response }) |
|
|
|
|
|
if response.get('effect').get('player_restriction') == None or response.get("effect").get('player_capability') == None or response.get("reason") == None: |
|
raise Exception() |
|
|
|
response['effect'] = restrict_effect_range(response['effect']) |
|
return response |
|
except: |
|
continue |
|
|
|
|
|
def create_round_result(world_summary, player_profile, player_restriction, player_capability, round_description, player_response): |
|
required_caps = get_required_capabilities(world_summary, player_profile, player_restriction, player_capability, round_description, player_response) |
|
for cap in required_caps: |
|
required_cap = player_capability.get(cap) |
|
if required_cap and random.random() > required_cap/100: |
|
|
|
return get_unexpected_result(world_summary, player_profile, player_restriction, player_capability, required_caps, round_description, player_response) |
|
|
|
|
|
return get_expected_result(world_summary, player_profile, player_restriction, player_capability, round_description, player_response) |
|
|
|
|
|
|
|
|
|
|
|
def create_bad_ending(world_summary, player_profile, player_restriction, player_capability, entire_story, round_story, previous_conversation, round_result): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
Player played the game and reached the bad ending by previous decisions. |
|
Player reach bad ending when player restriction (life or money) is less or equal to 0. |
|
Please create bad ending scenario based on previous conversation and previous round's result. |
|
Consider fictional universe, player profile, player restriction, player capability, entire story, this round story, this round result, and previous conversation. |
|
Bad ending story should be interesting and naturally connected to the previous conversation. |
|
Bad ending should be wrote in the tone of the game master. |
|
Bad ending should mention the reason in story why player reached the bad ending and make the story interesting. |
|
Bad ending should mention the exact reason by telling player's restriction change. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Entire Story: {entire_story} |
|
--- |
|
This Round Story: {round_story} |
|
--- |
|
This Round Result: {round_result} |
|
--- |
|
Previous Conversation: {previous_conversation} |
|
--- |
|
Bad Ending: |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
bad_ending = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "entire_story": entire_story, "round_story": round_story, "round_result": round_result, "previous_conversation": previous_conversation }) |
|
|
|
return bad_ending |
|
|
|
|
|
def create_good_ending(world_summary, player_profile, player_restriction, player_capability, entire_story, previous_conversation): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please answer in korean. |
|
You are best TRPG game master, and user is playing the game as a player with your guidance. |
|
The detailed information about the fictional universe of game is included in below Fictional Universe part. |
|
User played the game and reached the entire story's ending which means game win, so it's good ending. |
|
Please create good ending scenario based on previous conversation and entires story. |
|
Consider fictional universe, player profile, player restriction, player capability, entire story, and previous conversation. |
|
Good ending should be appropriate for the end of the story, which comprehensively organizes the entire story flow and the user's decisions. |
|
Good ending story should be interesting and naturally connected to the previous conversation. |
|
Good ending should be last part which finishes the game story and make user happy. |
|
Good ending should be wrote in the tone of the game master. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
Player Restriction: {player_restriction} |
|
--- |
|
Player Capability: {player_capability} |
|
--- |
|
Entire Story: {entire_story} |
|
--- |
|
Previous Conversation: {previous_conversation} |
|
--- |
|
Good Ending: |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
good_ending = chain.invoke({ "world_summary": world_summary, "player_profile": player_profile, "player_restriction": player_restriction, "player_capability": player_capability, "entire_story": entire_story, "previous_conversation": previous_conversation }) |
|
|
|
return good_ending |
|
|
|
|
|
def convert_to_image_prompt(topic, world_summary, player_profile, round_description): |
|
llm = ChatUpstage() |
|
prompt_template = PromptTemplate.from_template( |
|
""" |
|
Please provide visual prompt of the host message which will be used for text-image generation. |
|
Host message is D&D game scenario scene with choices on {topic}. |
|
Visual prompt should consider the context and user persona. |
|
Visual prompt should represent the scenario's scene and choices in the image. |
|
--- |
|
Fictional Universe: {world_summary} |
|
--- |
|
Player Profile: {player_profile} |
|
--- |
|
This Round Scenario: {round_description} |
|
--- |
|
Visual Prompt: |
|
""" |
|
) |
|
chain = prompt_template | llm | StrOutputParser() |
|
|
|
response = chain.invoke({ "topic": topic, "world_summary": world_summary, "player_profile": player_profile, "round_description": round_description }) |
|
|
|
return response |
|
|
|
|
|
def generate_image(prompt): |
|
client = OpenAI() |
|
response = client.images.generate( |
|
model="dall-e-3", |
|
prompt=prompt, |
|
size="1024x1024", |
|
quality="standard", |
|
n=1, |
|
) |
|
|
|
image_url = response.data[0].url |
|
|
|
return image_url |
|
|
|
|
|
def play_game(game_scenario, world_summary, player_profile): |
|
entire_story = [f"{idx+1}. {scenario['title']}\n{scenario['story']}\n\n" for idx, scenario in enumerate(game_scenario)] |
|
conversation = "" |
|
previous_conversation = "" |
|
previous_round_result = "" |
|
player_restriction = { "life": 10, "money": 10 } |
|
player_capability = player_profile["params"] |
|
|
|
player_profile_str = formatter.player_profile_to_str(player_profile) |
|
introduction = create_initial_conversation(world_summary, player_profile_str, player_restriction, player_capability, entire_story) |
|
print(introduction) |
|
print('-----------------------------') |
|
for round_idx, round_scenario in enumerate(game_scenario): |
|
|
|
round_story = f"{round_idx+1}. {round_scenario['title']}: {round_scenario['story']}\n" |
|
round_description = create_round_description(world_summary, player_profile_str, player_restriction, player_capability, entire_story, round_story, previous_conversation, previous_round_result) |
|
print(f"Round {round_idx+1}: {round_description}") |
|
|
|
|
|
player_response = input("λΉμ λ§μ κ²°μ μ λ΄λ €μ£ΌμΈμ! νλμ λ¬Έμ₯μΌλ‘ λΉμ μ΄ ν νλκ³Ό κ·Έμ λν κ·Όκ±°μ μ΄μ λ₯Ό λͺ
ννκ² μ€λͺ
ν΄μ£ΌμΈμ: ") |
|
conversation += f"Game Master: {round_description}\nPlayer: {player_response}\n" |
|
|
|
|
|
round_result = create_round_result(world_summary, player_profile_str, player_restriction, player_capability, round_description, player_response) |
|
round_effect = round_result["effect"] |
|
round_result_explanation = round_result["reason"] |
|
for key, value in round_effect["player_restriction"].items(): |
|
if player_restriction.get(key) is not None: |
|
modified_value = player_restriction[key] + value |
|
if modified_value > 10: |
|
player_restriction[key] = 10 |
|
elif modified_value < -10: |
|
player_restriction[key] = -10 |
|
else: |
|
player_restriction[key] = modified_value |
|
|
|
for key, value in round_effect["player_capability"].items(): |
|
if player_capability.get(key) is not None: |
|
modified_value = player_capability[key] + value |
|
if modified_value > 100: |
|
player_capability[key] = 100 |
|
elif modified_value < -100: |
|
player_capability[key] = -100 |
|
else: |
|
player_capability[key] = modified_value |
|
|
|
|
|
previous_conversation = conversation |
|
previous_round_result = formatter.to_round_result(round_effect, round_result_explanation) |
|
|
|
print('-----------------------------') |
|
|
|
|
|
if player_restriction["life"] <= 0 or player_restriction["money"] <= 0: |
|
bad_ending = create_bad_ending(world_summary, player_profile_str, player_restriction, player_capability, entire_story, round_story, previous_conversation, previous_round_result) |
|
print(bad_ending) |
|
return |
|
|
|
|
|
|
|
|
|
good_ending = create_good_ending(world_summary, player_profile_str, player_restriction, player_capability, entire_story, previous_conversation) |
|
print(good_ending) |
|
return |
|
|
|
if __name__ == "__main__": |
|
game_scenario = [ |
|
{ |
|
"title": "νΈκ·ΈμνΈλ‘μ μ΄λ", |
|
"story": "νλ μ΄μ΄λ€μ κ°μ νΈκ·ΈμνΈ λ§λ²νκ΅λ‘λΆν° νΈμ§λ₯Ό λ°μ΅λλ€. νΈμ§μλ νΉλ³ν μλ¬΄κ° μ£Όμ΄μ‘μΌλ©°, μ΄ μ무λ₯Ό μμνλ©΄ λ§λ²μ¬μ μ μ°μ λ°μ μ μλ€λ λ΄μ©μ΄ μ ν μμ΅λλ€. νΈκ·ΈμνΈμ λμ°©ν νλ μ΄μ΄λ€μ μλ²μ€ λ€λΈλμ΄ κ΅μμκ²μ μ§μ μ무μ 첫 λ²μ§Έ λ¨μλ₯Ό λ°μ΅λλ€. 첫 λ²μ§Έ μ무λ κΈμ§λ μ²μμ νΉλ³ν λ§λ² μλ¬Όμ μ°Ύμλ΄λ κ²μ
λλ€. μ΄ μλ¬Όμ λ§λ²μ¬μ μ μ°μΌλ‘ κ°λ κΈΈμ μλ €μ£Όλ μ€μν μ΄μ μ
λλ€." |
|
}, |
|
{ |
|
"title": "κ³ λμ μμ ", |
|
"story": "첫 λ²μ§Έ λ¨μμμ μ»μ μ 보λ₯Ό λ°νμΌλ‘ νλ μ΄μ΄λ€μ νΈκ·ΈμνΈ λμκ΄μ κΈμ μΉμ
μμ κ³ λμ μμ μ μ°ΎμμΌ ν©λλ€. μ΄ μμ μλ λ§λ²μ¬μ μ μ°μ λν μ€μν μ λ³΄κ° λ΄κ²¨μμ΅λλ€. κ·Έλ¬λ μμ μ μ°Ύλ κ²μ μ½μ§ μμ΅λλ€. νΈκ·ΈμνΈμ λ€μν νΌμ¦κ³Ό ν¨μ μ νμ΄μΌ νλ©°, κ²½μνλ λ€λ₯Έ νμλ€κ³Ό λ§λ² λκ²°μ λ²μ¬μΌ ν μλ μμ΅λλ€." |
|
}, |
|
{ |
|
"title": "λΉλ°μ λ°©", |
|
"story": "κ³ λμ μμ μμ μ»μ λ¨μλ‘ νλ μ΄μ΄λ€μ νΈκ·ΈμνΈ λ΄μ μ¨κ²¨μ§ λΉλ°μ λ°©μ μ°ΎμμΌ ν©λλ€. μ΄ λ°©μ λ§λ²μ¬μ μ μ°κ³Ό κ΄λ ¨λ λ λ€λ₯Έ λ¨μλ₯Ό κ°μ§κ³ μμ΅λλ€. λΉλ°μ λ°©μ λ€μ΄κ°κΈ° μν΄μλ νΈκ·ΈμνΈμ μμ¬λ₯Ό κΉμ΄ μ΄ν΄ν΄μΌ νλ©°, κ³Όκ±°μ λ§λ²μ¬λ€μ΄ λ¨κΈ΄ μ¬λ¬ κ°μ§ μνμ ν΅κ³Όν΄μΌ ν©λλ€" |
|
}, |
|
{ |
|
"title": "μκ°μ λ―Έλ‘", |
|
"story": "λΉλ°μ λ°©μμ μ»μ λ¨μλ νλ μ΄μ΄λ€μ μκ°μ λ―Έλ‘λ‘ μλ΄ν©λλ€. μκ°μ λ―Έλ‘λ λ§λ²μΌλ‘ 보νΈλ μ₯μλ‘, κ³Όκ±°μ νμ¬κ° κ΅μ°¨νλ κ³³μ
λλ€. νλ μ΄μ΄λ€μ λ―Έλ‘ μμμ κ³Όκ±°μ μ€μν μ¬κ±΄λ€μ λͺ©κ²©νκ³ , μ΄λ₯Ό ν΅ν΄ λ§λ²μ¬μ μ μ°μ λν λ§μ§λ§ λ¨μλ₯Ό μ»μ΄μΌ ν©λλ€. νμ§λ§ λ―Έλ‘ μμλ κ°λ ₯ν μ κ³Ό ν¨μ μ΄ λμ¬λ¦¬κ³ μμ΅λλ€." |
|
}, |
|
{ |
|
"title": "λ§λ²μ¬μ μ μ°", |
|
"story": "λͺ¨λ λ¨μλ₯Ό λͺ¨μ νλ μ΄μ΄λ€μ λ§μΉ¨λ΄ λ§λ²μ¬μ μ μ°μ΄ μ¨κ²¨μ§ μ₯μμ λμ°©ν©λλ€. μ΄κ³³μμ μ΅μ’
보μ€μμ κ²°μ μ΄ λ²μ΄μ§λλ€. 보μ€λ₯Ό 물리μΉκ³ μ μ°μ μμ λ£κΈ° μν΄μλ νλ μ΄μ΄λ€μ λͺ¨λ μ§νμ μ©κΈ°κ° νμν©λλ€. μ΅μ’
κ²°μ μ μΉλ¦¬ν ν, νλ μ΄μ΄λ€μ λ§λ²μ¬μ μ μ°μ μμ λ£κ³ κ°μμ κΈΈμ λ λ©λλ€." |
|
} |
|
] |
|
world_summary = "μ΄ TRPGμ μΈκ³κ΄μ ν΄λ¦¬ν¬ν° μ리μ¦μ λ§λ² μΈκ³λ₯Ό λ°°κ²½μΌλ‘ ν©λλ€. νλ μ΄μ΄λ€μ νΈκ·ΈμνΈ λ§λ²νκ΅μ νμμΌλ‘, μ μ€μ μΈ λ§λ²μ¬μ μ μ°μ μ°ΎκΈ° μν νΉλ³ν μ무λ₯Ό λ°μ΅λλ€. μ΄ μΈκ³λ λ§λ², μ λΉλ‘μ΄ μλ¬Ό, κ³ λμ λΉλ°, κ·Έλ¦¬κ³ μνμ΄ κ°λν κ³³μΌλ‘, νλ μ΄μ΄λ€μ νΈκ·ΈμνΈ λ΄μΈμ λ€μν μ₯μλ₯Ό νννλ©° νΌμ¦κ³Ό μ λ€μ μλν΄μΌ ν©λλ€. κ° λ¨κ³λ₯Ό ν΅ν΄ λ§λ²μ¬μ μ μ°μ κ°κΉμμ§λ©°, κ·Έ κ³Όμ μμ λ§λ²μ μ§μκ³Ό λ₯λ ₯μ ν₯μμν€κ³ νλκ³Ό μ©κΈ°λ₯Ό μνλ°κ² λ©λλ€. μ΄ μλ리μ€λ νλ μ΄μ΄λ€μ΄ λ§λ² μΈκ³μ κΉμ λΉλ°μ νμ΄κ°λ©°, μμ μ μ μ¬λ ₯μ λ°ννλ ν₯λ―Έμ§μ§ν λͺ¨νμ μ 곡ν©λλ€." |
|
player_profile = { |
|
"name": "μ΄νꡬ", |
|
"gender": "male", |
|
"job": "λ§λ²μ¬", |
|
"params": { |
|
"stamina": 34, |
|
"intelligence": 58, |
|
"combat_power": 45, |
|
"agility": 100 |
|
} |
|
} |
|
play_game(game_scenario, world_summary, player_profile) |