2024-05-02 11:56:57 +00:00
|
|
|
from logging import getLogger
|
|
|
|
from random import choice, randint
|
|
|
|
from typing import List
|
|
|
|
|
|
|
|
from packit.agent import Agent
|
|
|
|
|
|
|
|
from adventure.models import Actor, Item, Room, World
|
|
|
|
|
|
|
|
logger = getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
def generate_room(agent: Agent, world_theme: str, existing_rooms: List[str]) -> Room:
|
|
|
|
name = agent(
|
|
|
|
"Generate one room, area, or location that would make sense in the world of {world_theme}. "
|
|
|
|
"Only respond with the room name, do not include the description or any other text. "
|
|
|
|
'Do not prefix the name with "the", do not wrap it in quotes. The existing rooms are: {existing_rooms}',
|
|
|
|
world_theme=world_theme,
|
|
|
|
existing_rooms=existing_rooms,
|
|
|
|
)
|
|
|
|
logger.info(f"Generating room: {name}")
|
|
|
|
desc = agent(
|
|
|
|
"Generate a detailed description of the {name} area. What does it look like? "
|
|
|
|
"What does it smell like? What can be seen or heard?",
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
|
|
|
items = []
|
|
|
|
actors = []
|
|
|
|
actions = {}
|
|
|
|
|
|
|
|
return Room(
|
|
|
|
name=name, description=desc, items=items, actors=actors, actions=actions
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def generate_item(
|
|
|
|
agent: Agent,
|
|
|
|
world_theme: str,
|
|
|
|
dest_room: str | None = None,
|
|
|
|
dest_actor: str | None = None,
|
|
|
|
existing_items: List[str] = [],
|
|
|
|
) -> Item:
|
|
|
|
if dest_actor:
|
|
|
|
dest_note = "The item will be held by the {dest_actor} character"
|
|
|
|
elif dest_room:
|
|
|
|
dest_note = "The item will be placed in the {dest_room} room"
|
|
|
|
else:
|
|
|
|
dest_note = "The item will be placed in the world"
|
|
|
|
|
|
|
|
name = agent(
|
|
|
|
"Generate one item or object that would make sense in the world of {world_theme}. {dest_note}. "
|
|
|
|
'Only respond with the item name, do not include a description or any other text. Do not prefix the name with "the", do not wrap it in quotes. '
|
|
|
|
"Do not create any duplicate items in the same room. Do not give characters any duplicate items. The existing items are: {existing_items}",
|
|
|
|
dest_note=dest_note,
|
|
|
|
existing_items=existing_items,
|
|
|
|
world_theme=world_theme,
|
|
|
|
)
|
|
|
|
logger.info(f"Generating item: {name}")
|
|
|
|
desc = agent(
|
|
|
|
"Generate a detailed description of the {name} item. What does it look like? What is it made of? What does it do?",
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
|
|
|
actions = {}
|
|
|
|
|
|
|
|
return Item(name=name, description=desc, actions=actions)
|
|
|
|
|
|
|
|
|
|
|
|
def generate_actor(
|
|
|
|
agent: Agent, world_theme: str, dest_room: str, existing_actors: List[str] = []
|
|
|
|
) -> Actor:
|
|
|
|
name = agent(
|
|
|
|
"Generate one person or creature that would make sense in the world of {world_theme}. The character will be placed in the {dest_room} room. "
|
|
|
|
'Only respond with the character name, do not include a description or any other text. Do not prefix the name with "the", do not wrap it in quotes. '
|
|
|
|
"Do not create any duplicate characters in the same room. The existing characters are: {existing_actors}",
|
|
|
|
dest_room=dest_room,
|
|
|
|
existing_actors=existing_actors,
|
|
|
|
world_theme=world_theme,
|
|
|
|
)
|
|
|
|
logger.info(f"Generating actor: {name}")
|
|
|
|
description = agent(
|
|
|
|
"Generate a detailed description of the {name} character. What do they look like? What are they wearing? "
|
|
|
|
"What are they doing? Describe their appearance from the perspective of an outside observer.",
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
backstory = agent(
|
|
|
|
"Generate a backstory for the {name} actor. Where are they from? What are they doing here? What are their "
|
|
|
|
'goals? Make sure to phrase the backstory in the second person, starting with "you are" and speaking directly to {name}.',
|
|
|
|
name=name,
|
|
|
|
)
|
|
|
|
|
|
|
|
health = 100
|
|
|
|
actions = {}
|
|
|
|
|
|
|
|
return Actor(
|
|
|
|
name=name,
|
|
|
|
backstory=backstory,
|
|
|
|
description=description,
|
|
|
|
health=health,
|
|
|
|
actions=actions,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2024-05-02 14:20:47 +00:00
|
|
|
def generate_world(agent: Agent, name: str, theme: str) -> World:
|
2024-05-02 11:56:57 +00:00
|
|
|
room_count = randint(3, 5)
|
|
|
|
logger.info(f"Generating a {theme} with {room_count} rooms")
|
|
|
|
|
|
|
|
existing_actors: List[str] = []
|
|
|
|
existing_items: List[str] = []
|
|
|
|
|
|
|
|
# generate the rooms
|
|
|
|
rooms = []
|
|
|
|
for i in range(room_count):
|
|
|
|
existing_rooms = [room.name for room in rooms]
|
|
|
|
room = generate_room(agent, theme, existing_rooms)
|
|
|
|
rooms.append(room)
|
|
|
|
|
|
|
|
item_count = randint(0, 3)
|
|
|
|
for j in range(item_count):
|
|
|
|
item = generate_item(
|
|
|
|
agent, theme, dest_room=room.name, existing_items=existing_items
|
|
|
|
)
|
|
|
|
room.items.append(item)
|
|
|
|
existing_items.append(item.name)
|
|
|
|
|
|
|
|
actor_count = randint(0, 3)
|
|
|
|
for j in range(actor_count):
|
|
|
|
actor = generate_actor(
|
|
|
|
agent, theme, dest_room=room.name, existing_actors=existing_actors
|
|
|
|
)
|
|
|
|
room.actors.append(actor)
|
|
|
|
existing_actors.append(actor.name)
|
|
|
|
|
|
|
|
# generate the actor's inventory
|
|
|
|
item_count = randint(0, 3)
|
|
|
|
for k in range(item_count):
|
|
|
|
item = generate_item(
|
|
|
|
agent, theme, dest_room=room.name, existing_items=existing_items
|
|
|
|
)
|
|
|
|
actor.items.append(item)
|
|
|
|
existing_items.append(item.name)
|
|
|
|
|
|
|
|
opposite_directions = {
|
|
|
|
"north": "south",
|
|
|
|
"south": "north",
|
|
|
|
"east": "west",
|
|
|
|
"west": "east",
|
|
|
|
}
|
|
|
|
|
|
|
|
# TODO: generate portals to link the rooms together
|
|
|
|
for room in rooms:
|
|
|
|
directions = ["north", "south", "east", "west"]
|
|
|
|
for direction in directions:
|
|
|
|
if direction in room.portals:
|
|
|
|
logger.debug(f"Room {room.name} already has a {direction} portal")
|
|
|
|
continue
|
|
|
|
|
|
|
|
opposite_direction = opposite_directions[direction]
|
|
|
|
|
|
|
|
if randint(0, 1):
|
|
|
|
dest_room = choice([r for r in rooms if r.name != room.name])
|
|
|
|
|
|
|
|
# make sure not to create duplicate links
|
|
|
|
if room.name in dest_room.portals.values():
|
|
|
|
logger.debug(
|
|
|
|
f"Room {dest_room.name} already has a portal to {room.name}"
|
|
|
|
)
|
|
|
|
continue
|
|
|
|
|
|
|
|
if opposite_direction in dest_room.portals:
|
|
|
|
logger.debug(
|
|
|
|
f"Room {dest_room.name} already has a {opposite_direction} portal"
|
|
|
|
)
|
|
|
|
continue
|
|
|
|
|
|
|
|
# create bidirectional links
|
|
|
|
room.portals[direction] = dest_room.name
|
|
|
|
dest_room.portals[opposite_directions[direction]] = room.name
|
|
|
|
|
2024-05-02 14:20:47 +00:00
|
|
|
return World(name=name, rooms=rooms, theme=theme)
|