encourage agents to retry after action errors, allow constant numeric values in config, fix up some common JSON errors
This commit is contained in:
parent
200615ab2b
commit
949cd5687a
|
@ -7,6 +7,7 @@ from adventure.context import (
|
||||||
get_agent_for_actor,
|
get_agent_for_actor,
|
||||||
world_context,
|
world_context,
|
||||||
)
|
)
|
||||||
|
from adventure.errors import ActionError
|
||||||
from adventure.utils.conversation import loop_conversation
|
from adventure.utils.conversation import loop_conversation
|
||||||
from adventure.utils.search import (
|
from adventure.utils.search import (
|
||||||
find_actor_in_room,
|
find_actor_in_room,
|
||||||
|
@ -79,11 +80,11 @@ def action_move(direction: str) -> str:
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if not portal:
|
if not portal:
|
||||||
return f"You cannot move {direction} from here."
|
raise ActionError(f"You cannot move {direction} from here.")
|
||||||
|
|
||||||
destination_room = find_room(action_world, portal.destination)
|
destination_room = find_room(action_world, portal.destination)
|
||||||
if not destination_room:
|
if not destination_room:
|
||||||
return f"The {portal.destination} room does not exist."
|
raise ActionError(f"The {portal.destination} room does not exist.")
|
||||||
|
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} moves through {direction} to {destination_room.name}"
|
f"{action_actor.name} moves through {direction} to {destination_room.name}"
|
||||||
|
@ -106,7 +107,7 @@ def action_take(item: str) -> str:
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
action_item = find_item_in_room(action_room, item)
|
action_item = find_item_in_room(action_room, item)
|
||||||
if not action_item:
|
if not action_item:
|
||||||
return f"The {item} item is not in the room."
|
raise ActionError(f"The {item} item is not in the room.")
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} takes the {item} item")
|
broadcast(f"{action_actor.name} takes the {item} item")
|
||||||
action_room.items.remove(action_item)
|
action_room.items.remove(action_item)
|
||||||
|
@ -127,13 +128,15 @@ def action_ask(character: str, question: str) -> str:
|
||||||
# sanity checks
|
# sanity checks
|
||||||
question_actor, question_agent = get_actor_agent_for_name(character)
|
question_actor, question_agent = get_actor_agent_for_name(character)
|
||||||
if question_actor == action_actor:
|
if question_actor == action_actor:
|
||||||
return "You cannot ask yourself a question. Stop talking to yourself. Try another action."
|
raise ActionError(
|
||||||
|
"You cannot ask yourself a question. Stop talking to yourself. Try another action."
|
||||||
|
)
|
||||||
|
|
||||||
if not question_actor:
|
if not question_actor:
|
||||||
return f"The {character} character is not in the room."
|
raise ActionError(f"The {character} character is not in the room.")
|
||||||
|
|
||||||
if not question_agent:
|
if not question_agent:
|
||||||
return f"The {character} character does not exist."
|
raise ActionError(f"The {character} character does not exist.")
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} asks {character}: {question}")
|
broadcast(f"{action_actor.name} asks {character}: {question}")
|
||||||
first_prompt = (
|
first_prompt = (
|
||||||
|
@ -183,13 +186,15 @@ def action_tell(character: str, message: str) -> str:
|
||||||
# sanity checks
|
# sanity checks
|
||||||
question_actor, question_agent = get_actor_agent_for_name(character)
|
question_actor, question_agent = get_actor_agent_for_name(character)
|
||||||
if question_actor == action_actor:
|
if question_actor == action_actor:
|
||||||
return "You cannot tell yourself a message. Stop talking to yourself. Try another action."
|
raise ActionError(
|
||||||
|
"You cannot tell yourself a message. Stop talking to yourself. Try another action."
|
||||||
|
)
|
||||||
|
|
||||||
if not question_actor:
|
if not question_actor:
|
||||||
return f"The {character} character is not in the room."
|
raise ActionError(f"The {character} character is not in the room.")
|
||||||
|
|
||||||
if not question_agent:
|
if not question_agent:
|
||||||
return f"The {character} character does not exist."
|
raise ActionError(f"The {character} character does not exist.")
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} tells {character}: {message}")
|
broadcast(f"{action_actor.name} tells {character}: {message}")
|
||||||
first_prompt = (
|
first_prompt = (
|
||||||
|
@ -236,11 +241,16 @@ def action_give(character: str, item: str) -> str:
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
destination_actor = find_actor_in_room(action_room, character)
|
destination_actor = find_actor_in_room(action_room, character)
|
||||||
if not destination_actor:
|
if not destination_actor:
|
||||||
return f"The {character} character is not in the room."
|
raise ActionError(f"The {character} character is not in the room.")
|
||||||
|
|
||||||
|
if destination_actor == action_actor:
|
||||||
|
raise ActionError(
|
||||||
|
"You cannot give an item to yourself. Try another action."
|
||||||
|
)
|
||||||
|
|
||||||
action_item = find_item_in_actor(action_actor, item)
|
action_item = find_item_in_actor(action_actor, item)
|
||||||
if not action_item:
|
if not action_item:
|
||||||
return f"You do not have the {item} item in your inventory."
|
raise ActionError(f"You do not have the {item} item in your inventory.")
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} gives {character} the {item} item.")
|
broadcast(f"{action_actor.name} gives {character} the {item} item.")
|
||||||
action_actor.items.remove(action_item)
|
action_actor.items.remove(action_item)
|
||||||
|
@ -260,7 +270,7 @@ def action_drop(item: str) -> str:
|
||||||
with action_context() as (action_room, action_actor):
|
with action_context() as (action_room, action_actor):
|
||||||
action_item = find_item_in_actor(action_actor, item)
|
action_item = find_item_in_actor(action_actor, item)
|
||||||
if not action_item:
|
if not action_item:
|
||||||
return f"You do not have the {item} item in your inventory."
|
raise ActionError(f"You do not have the {item} item in your inventory.")
|
||||||
|
|
||||||
broadcast(f"{action_actor.name} drops the {item} item")
|
broadcast(f"{action_actor.name} drops the {item} item")
|
||||||
action_actor.items.remove(action_item)
|
action_actor.items.remove(action_item)
|
||||||
|
|
|
@ -13,6 +13,7 @@ from adventure.context import (
|
||||||
set_dungeon_master,
|
set_dungeon_master,
|
||||||
world_context,
|
world_context,
|
||||||
)
|
)
|
||||||
|
from adventure.errors import ActionError
|
||||||
from adventure.generate import generate_item, generate_room, link_rooms
|
from adventure.generate import generate_item, generate_room, link_rooms
|
||||||
from adventure.utils.effect import apply_effects
|
from adventure.utils.effect import apply_effects
|
||||||
from adventure.utils.search import find_actor_in_room
|
from adventure.utils.search import find_actor_in_room
|
||||||
|
@ -47,7 +48,10 @@ def action_explore(direction: str) -> str:
|
||||||
|
|
||||||
if direction in action_room.portals:
|
if direction in action_room.portals:
|
||||||
dest_room = action_room.portals[direction]
|
dest_room = action_room.portals[direction]
|
||||||
return f"You cannot explore {direction} from here, that direction already leads to {dest_room}. Please use the move action to go there."
|
raise ActionError(
|
||||||
|
f"You cannot explore {direction} from here, that direction already leads to {dest_room}. "
|
||||||
|
"Please use the move action to go there."
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
systems = get_game_systems()
|
systems = get_game_systems()
|
||||||
|
@ -118,7 +122,7 @@ def action_use(item: str, target: str) -> str:
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
if not action_item:
|
if not action_item:
|
||||||
return f"The {item} item is not available to use."
|
raise ActionError(f"The {item} item is not available to use.")
|
||||||
|
|
||||||
if target == "self":
|
if target == "self":
|
||||||
target_actor = action_actor
|
target_actor = action_actor
|
||||||
|
@ -148,13 +152,15 @@ def action_use(item: str, target: str) -> str:
|
||||||
)
|
)
|
||||||
if not chosen_effect:
|
if not chosen_effect:
|
||||||
# TODO: should retry the question if the effect is not found
|
# TODO: should retry the question if the effect is not found
|
||||||
return f"The {chosen_name} effect is not available to apply."
|
raise ValueError(f"The {chosen_name} effect is not available to apply.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
apply_effects(target_actor, [chosen_effect])
|
apply_effects(target_actor, [chosen_effect])
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error applying effect: %s", chosen_effect)
|
logger.exception("error applying effect: %s", chosen_effect)
|
||||||
return f"There was a problem applying the {chosen_name} effect."
|
raise ValueError(
|
||||||
|
f"There was a problem applying the {chosen_name} effect while using the {item} item."
|
||||||
|
)
|
||||||
|
|
||||||
broadcast(
|
broadcast(
|
||||||
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}"
|
f"{action_actor.name} uses the {chosen_name} effect of {item} on {target}"
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class ActionError(Exception):
|
||||||
|
pass
|
|
@ -1,5 +1,5 @@
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from random import choice, randint
|
from random import choice
|
||||||
from typing import List, Tuple
|
from typing import List, Tuple
|
||||||
|
|
||||||
from packit.agent import Agent
|
from packit.agent import Agent
|
||||||
|
@ -19,6 +19,7 @@ from adventure.models.effect import (
|
||||||
from adventure.models.entity import Actor, Item, Portal, Room, World, WorldEntity
|
from adventure.models.entity import Actor, Item, Portal, Room, World, WorldEntity
|
||||||
from adventure.models.event import GenerateEvent
|
from adventure.models.event import GenerateEvent
|
||||||
from adventure.utils import try_parse_float, try_parse_int
|
from adventure.utils import try_parse_float, try_parse_int
|
||||||
|
from adventure.utils.effect import resolve_int_range
|
||||||
from adventure.utils.search import (
|
from adventure.utils.search import (
|
||||||
list_actors,
|
list_actors,
|
||||||
list_actors_in_room,
|
list_actors_in_room,
|
||||||
|
@ -108,9 +109,7 @@ def generate_room(
|
||||||
actions = {}
|
actions = {}
|
||||||
room = Room(name=name, description=desc, items=[], actors=[], actions=actions)
|
room = Room(name=name, description=desc, items=[], actors=[], actions=actions)
|
||||||
|
|
||||||
item_count = randint(
|
item_count = resolve_int_range(world_config.size.room_items) or 0
|
||||||
world_config.size.room_items.min, world_config.size.room_items.max
|
|
||||||
)
|
|
||||||
broadcast_generated(f"Generating {item_count} items for room: {name}")
|
broadcast_generated(f"Generating {item_count} items for room: {name}")
|
||||||
|
|
||||||
for _ in range(item_count):
|
for _ in range(item_count):
|
||||||
|
@ -127,9 +126,7 @@ def generate_room(
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error generating item")
|
logger.exception("error generating item")
|
||||||
|
|
||||||
actor_count = randint(
|
actor_count = resolve_int_range(world_config.size.room_actors) or 0
|
||||||
world_config.size.room_actors.min, world_config.size.room_actors.max
|
|
||||||
)
|
|
||||||
broadcast_generated(message=f"Generating {actor_count} actors for room: {name}")
|
broadcast_generated(message=f"Generating {actor_count} actors for room: {name}")
|
||||||
|
|
||||||
for _ in range(actor_count):
|
for _ in range(actor_count):
|
||||||
|
@ -265,9 +262,7 @@ def generate_item(
|
||||||
item = Item(name=name, description=desc, actions=actions)
|
item = Item(name=name, description=desc, actions=actions)
|
||||||
generate_system_attributes(agent, world, item, systems)
|
generate_system_attributes(agent, world, item, systems)
|
||||||
|
|
||||||
effect_count = randint(
|
effect_count = resolve_int_range(world_config.size.item_effects) or 0
|
||||||
world_config.size.item_effects.min, world_config.size.item_effects.max
|
|
||||||
)
|
|
||||||
broadcast_generated(message=f"Generating {effect_count} effects for item: {name}")
|
broadcast_generated(message=f"Generating {effect_count} effects for item: {name}")
|
||||||
|
|
||||||
for _ in range(effect_count):
|
for _ in range(effect_count):
|
||||||
|
@ -326,9 +321,7 @@ def generate_actor(
|
||||||
generate_system_attributes(agent, world, actor, systems)
|
generate_system_attributes(agent, world, actor, systems)
|
||||||
|
|
||||||
# generate the actor's inventory
|
# generate the actor's inventory
|
||||||
item_count = randint(
|
item_count = resolve_int_range(world_config.size.actor_items) or 0
|
||||||
world_config.size.actor_items.min, world_config.size.actor_items.max
|
|
||||||
)
|
|
||||||
broadcast_generated(f"Generating {item_count} items for actor {name}")
|
broadcast_generated(f"Generating {item_count} items for actor {name}")
|
||||||
|
|
||||||
for k in range(item_count):
|
for k in range(item_count):
|
||||||
|
@ -470,9 +463,7 @@ def link_rooms(
|
||||||
rooms = rooms or world.rooms
|
rooms = rooms or world.rooms
|
||||||
|
|
||||||
for room in rooms:
|
for room in rooms:
|
||||||
num_portals = randint(
|
num_portals = resolve_int_range(world_config.size.portals) or 0
|
||||||
world_config.size.portals.min, world_config.size.portals.max
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(room.portals) >= num_portals:
|
if len(room.portals) >= num_portals:
|
||||||
logger.info(f"room {room.name} already has enough portals")
|
logger.info(f"room {room.name} already has enough portals")
|
||||||
|
@ -517,9 +508,7 @@ def generate_world(
|
||||||
systems: List[GameSystem],
|
systems: List[GameSystem],
|
||||||
room_count: int | None = None,
|
room_count: int | None = None,
|
||||||
) -> World:
|
) -> World:
|
||||||
room_count = room_count or randint(
|
room_count = room_count or resolve_int_range(world_config.size.rooms) or 0
|
||||||
world_config.size.rooms.min, world_config.size.rooms.max
|
|
||||||
)
|
|
||||||
|
|
||||||
broadcast_generated(message=f"Generating a {theme} with {room_count} rooms")
|
broadcast_generated(message=f"Generating a {theme} with {room_count} rooms")
|
||||||
world = World(name=name, rooms=[], theme=theme, order=[])
|
world = World(name=name, rooms=[], theme=theme, order=[])
|
||||||
|
|
|
@ -22,11 +22,11 @@ class BotConfig:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class RenderConfig:
|
class RenderConfig:
|
||||||
cfg: IntRange
|
cfg: int | IntRange
|
||||||
checkpoints: List[str]
|
checkpoints: List[str]
|
||||||
path: str
|
path: str
|
||||||
sizes: Dict[str, Size]
|
sizes: Dict[str, Size]
|
||||||
steps: IntRange
|
steps: int | IntRange
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -47,12 +47,12 @@ class WorldActorConfig:
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class WorldSizeConfig:
|
class WorldSizeConfig:
|
||||||
actor_items: IntRange
|
actor_items: int | IntRange
|
||||||
item_effects: IntRange
|
item_effects: int | IntRange
|
||||||
portals: IntRange
|
portals: int | IntRange
|
||||||
room_actors: IntRange
|
room_actors: int | IntRange
|
||||||
room_items: IntRange
|
room_items: int | IntRange
|
||||||
rooms: IntRange
|
rooms: int | IntRange
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -87,11 +87,11 @@ DEFAULT_CONFIG = Config(
|
||||||
server=ServerConfig(websocket=WebsocketServerConfig(host="localhost", port=8001)),
|
server=ServerConfig(websocket=WebsocketServerConfig(host="localhost", port=8001)),
|
||||||
world=WorldConfig(
|
world=WorldConfig(
|
||||||
actor=WorldActorConfig(
|
actor=WorldActorConfig(
|
||||||
conversation_limit=3,
|
conversation_limit=2,
|
||||||
),
|
),
|
||||||
size=WorldSizeConfig(
|
size=WorldSizeConfig(
|
||||||
actor_items=IntRange(min=0, max=2),
|
actor_items=IntRange(min=0, max=2),
|
||||||
item_effects=IntRange(min=1, max=2),
|
item_effects=IntRange(min=1, max=1),
|
||||||
portals=IntRange(min=1, max=3),
|
portals=IntRange(min=1, max=3),
|
||||||
rooms=IntRange(min=3, max=6),
|
rooms=IntRange(min=3, max=6),
|
||||||
room_actors=IntRange(min=1, max=3),
|
room_actors=IntRange(min=1, max=3),
|
||||||
|
|
|
@ -28,6 +28,7 @@ from adventure.models.event import (
|
||||||
ResultEvent,
|
ResultEvent,
|
||||||
StatusEvent,
|
StatusEvent,
|
||||||
)
|
)
|
||||||
|
from adventure.utils.random import resolve_int_range
|
||||||
|
|
||||||
from .prompt import prompt_from_entity, prompt_from_event
|
from .prompt import prompt_from_entity, prompt_from_event
|
||||||
|
|
||||||
|
@ -44,17 +45,11 @@ render_thread: Thread | None = None
|
||||||
|
|
||||||
|
|
||||||
def generate_cfg():
|
def generate_cfg():
|
||||||
if render_config.cfg.min == render_config.cfg.max:
|
return resolve_int_range(render_config.cfg)
|
||||||
return render_config.cfg.min
|
|
||||||
|
|
||||||
return randint(render_config.cfg.min, render_config.cfg.max)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_steps():
|
def generate_steps():
|
||||||
if render_config.steps.min == render_config.steps.max:
|
return resolve_int_range(render_config.steps)
|
||||||
return render_config.steps.min
|
|
||||||
|
|
||||||
return randint(render_config.steps.min, render_config.steps.max)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_batches(
|
def generate_batches(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
from json import loads
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from math import inf
|
from math import inf
|
||||||
from typing import Callable, Sequence
|
from typing import Callable, Sequence
|
||||||
|
@ -91,6 +92,22 @@ def prompt_actor_action(
|
||||||
if not room or not actor:
|
if not room or not actor:
|
||||||
raise ValueError("Room and actor must be set before parsing results")
|
raise ValueError("Room and actor must be set before parsing results")
|
||||||
|
|
||||||
|
# trim suffixes that are used elsewhere
|
||||||
|
value = value.removesuffix("END").strip()
|
||||||
|
|
||||||
|
# fix unbalanced curly braces
|
||||||
|
if value.startswith("{") and not value.endswith("}"):
|
||||||
|
open_count = value.count("{")
|
||||||
|
close_count = value.count("}")
|
||||||
|
|
||||||
|
if open_count > close_count:
|
||||||
|
fixed_value = value + ("}" * (open_count - close_count))
|
||||||
|
try:
|
||||||
|
loads(fixed_value)
|
||||||
|
value = fixed_value
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
if could_be_json(value):
|
if could_be_json(value):
|
||||||
event = ActionEvent.from_json(value, room, actor)
|
event = ActionEvent.from_json(value, room, actor)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -11,6 +11,7 @@ from packit.utils import could_be_json
|
||||||
from adventure.context import broadcast
|
from adventure.context import broadcast
|
||||||
from adventure.models.config import DEFAULT_CONFIG
|
from adventure.models.config import DEFAULT_CONFIG
|
||||||
from adventure.models.entity import Actor, Room
|
from adventure.models.entity import Actor, Room
|
||||||
|
from adventure.models.event import ReplyEvent
|
||||||
|
|
||||||
from .string import normalize_name
|
from .string import normalize_name
|
||||||
|
|
||||||
|
@ -125,10 +126,12 @@ def loop_conversation(
|
||||||
if len(actors) != len(agents):
|
if len(actors) != len(agents):
|
||||||
raise ValueError("The number of actors and agents must match.")
|
raise ValueError("The number of actors and agents must match.")
|
||||||
|
|
||||||
|
# set up the keyword or length-limit compound condition
|
||||||
_, condition_end, parse_end = make_keyword_condition(end_message)
|
_, condition_end, parse_end = make_keyword_condition(end_message)
|
||||||
stop_length = partial(condition_threshold, max=max_length)
|
stop_length = partial(condition_threshold, max=max_length)
|
||||||
stop_condition = condition_or(condition_end, stop_length)
|
stop_condition = condition_or(condition_end, stop_length)
|
||||||
|
|
||||||
|
# prepare a result parser looking for the echo function
|
||||||
def result_parser(value: str, **kwargs) -> str:
|
def result_parser(value: str, **kwargs) -> str:
|
||||||
value = parse_end(value, **kwargs)
|
value = parse_end(value, **kwargs)
|
||||||
|
|
||||||
|
@ -140,6 +143,7 @@ def loop_conversation(
|
||||||
|
|
||||||
return value.strip()
|
return value.strip()
|
||||||
|
|
||||||
|
# prepare the loop state
|
||||||
i = 0
|
i = 0
|
||||||
last_actor = first_actor
|
last_actor = first_actor
|
||||||
response = first_message
|
response = first_message
|
||||||
|
@ -163,8 +167,9 @@ def loop_conversation(
|
||||||
)
|
)
|
||||||
response = result_parser(response)
|
response = result_parser(response)
|
||||||
|
|
||||||
logger.info(f"{actor.name} response: {response}")
|
logger.info(f"{actor.name} responds: {response}")
|
||||||
broadcast(f"{actor.name} responds to {last_actor.name}: {response}")
|
reply_event = ReplyEvent.from_text(response, room, actor)
|
||||||
|
broadcast(reply_event)
|
||||||
|
|
||||||
# increment the step counter
|
# increment the step counter
|
||||||
i += 1
|
i += 1
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import random
|
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from adventure.models.base import FloatRange, IntRange
|
|
||||||
from adventure.models.effect import (
|
from adventure.models.effect import (
|
||||||
BooleanEffectPattern,
|
BooleanEffectPattern,
|
||||||
BooleanEffectResult,
|
BooleanEffectResult,
|
||||||
|
@ -23,6 +21,8 @@ from adventure.utils.attribute import (
|
||||||
prepend_value,
|
prepend_value,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .random import resolve_float_range, resolve_int_range, resolve_string_list
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -124,50 +124,6 @@ def effective_attributes(
|
||||||
return attributes
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
def resolve_float_range(range: float | FloatRange | None) -> float | None:
|
|
||||||
"""
|
|
||||||
Resolve a float range to a single value.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if range is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if isinstance(
|
|
||||||
range, (float, int)
|
|
||||||
): # int is not really necessary here, but mypy complains without it
|
|
||||||
return range
|
|
||||||
|
|
||||||
return random.uniform(range.min, range.max)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_int_range(range: int | IntRange | None) -> int | None:
|
|
||||||
"""
|
|
||||||
Resolve an integer range to a single value.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if range is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if isinstance(range, int):
|
|
||||||
return range
|
|
||||||
|
|
||||||
return random.randint(range.min, range.max)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_string_list(result: str | List[str] | None) -> str | None:
|
|
||||||
"""
|
|
||||||
Resolve a string result to a single value.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if result is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if isinstance(result, str):
|
|
||||||
return result
|
|
||||||
|
|
||||||
return random.choice(result)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_boolean_effect(effect: BooleanEffectPattern) -> BooleanEffectResult:
|
def resolve_boolean_effect(effect: BooleanEffectPattern) -> BooleanEffectResult:
|
||||||
"""
|
"""
|
||||||
Apply a boolean effect pattern to a set of attributes.
|
Apply a boolean effect pattern to a set of attributes.
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import random
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from adventure.models.base import FloatRange, IntRange
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_float_range(range: float | FloatRange | None) -> float | None:
|
||||||
|
"""
|
||||||
|
Resolve a float range to a single value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if range is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(
|
||||||
|
range, (float, int)
|
||||||
|
): # int is not really necessary here, but mypy complains without it
|
||||||
|
return range
|
||||||
|
|
||||||
|
return random.uniform(range.min, range.max)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_int_range(range: int | IntRange | None) -> int | None:
|
||||||
|
"""
|
||||||
|
Resolve an integer range to a single value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if range is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(range, int):
|
||||||
|
return range
|
||||||
|
|
||||||
|
return random.randint(range.min, range.max)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_string_list(result: str | List[str] | None) -> str | None:
|
||||||
|
"""
|
||||||
|
Resolve a string result to a single value.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if isinstance(result, str):
|
||||||
|
return result
|
||||||
|
|
||||||
|
return random.choice(result)
|
Loading…
Reference in New Issue