100 lines
3.3 KiB
Python
100 lines
3.3 KiB
Python
# 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)
|