feat: Create the Object Generator Oracle
This commit is contained in:
99
gm_assistant/oracle/object_generator.py
Normal file
99
gm_assistant/oracle/object_generator.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# 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 it’s 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)
|
Reference in New Issue
Block a user