gm-assistant/gm_assistant/oracle/object_generator.py

100 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPDX-FileCopyrightText: 2025 2025
# SPDX-FileContributor: Gergely Polonkai
#
# SPDX-License-Identifier: GPL-3.0-or-later
"""Object Generator Oracle"""
from typing import Any
from ..dice import Die
from .base import Oracle
class ObjectGeneratorProperty: # pylint: disable=too-few-public-methods
"""Property for the Object Generator Oracle"""
def __init__(self, property_data: dict[str, Any]) -> None:
self.question: str | None = None
self.die: Die | None = None
self.results: dict[int, str] = {}
self.parse_and_validate(property_data)
def parse_and_validate(self, property_data: dict[str, Any]) -> None:
"""Parse and validate property data"""
if "question" not in property_data:
raise ValueError("Missing question value")
self.question = property_data["question"]
if "roll-type" not in property_data:
raise ValueError("Missing roll-type value")
self.die = Die(property_data["roll-type"])
if "results" not in property_data:
raise ValueError("Missing results value")
new_results: dict[int, str | None] = {
roll_value: None for roll_value in range(self.die.minimum, self.die.maximum + 1)
}
for idx, result_data in enumerate(property_data["results"]):
if "min-roll" not in result_data:
raise ValueError(f"Missing min-roll value in result {idx}")
min_roll = int(result_data["min-roll"])
max_roll = int(result_data.get("max-roll", min_roll))
if max_roll < min_roll:
raise ValueError(f"max-roll cannot be smaller than min-roll in result {idx}")
if "value" not in result_data:
raise ValueError(f"Missing value value in result {idx}")
for roll_value in range(min_roll, max_roll + 1):
if new_results[roll_value] is not None:
raise ValueError(f"Roll value {roll_value} is already registered in result {idx}")
new_results[roll_value] = result_data["value"]
if any(value is None for value in new_results.values()):
raise ValueError(f"Not all roll values yield a result for property {self.question}")
# We just checked that every member of new_results is a string, so its safe to ignore type checking here
self.results = new_results # type: ignore[assignment]
def generate(self) -> str:
"""Generate a random value for this property"""
assert self.die
value = self.die.roll()
answer = self.results[value]
return f"""*{self.question}*
{answer}"""
class ObjectGeneratorOracle(Oracle):
"""Oracle that can generate objects"""
TYPE_MARKER = "object-generator"
def __init__(self, oracle_data: dict[str, Any]) -> None:
self.properties: list[ObjectGeneratorProperty] = []
super().__init__(oracle_data)
def parse_and_validate(self, oracle_data: dict[str, Any]) -> None:
super().parse_and_validate(oracle_data)
if "properties" not in oracle_data:
raise KeyError("properties")
self.properties = [ObjectGeneratorProperty(prop_data) for prop_data in oracle_data["properties"]]
def generate(self) -> str:
return "\n\n".join(prop.generate() for prop in self.properties)