GameAI / explainers /explainer_probability.py
j-js's picture
Update explainers/explainer_probability.py
85467de verified
import re
from .explainer_types import ExplainerResult, ExplainerScaffold
def explain_probability_question(text: str):
low = (text or "").lower()
probability_signals = [
"probability",
"chance",
"odds",
"random",
"chosen at random",
"selected at random",
"picked at random",
"drawn",
"without replacement",
"with replacement",
]
if not any(signal in low for signal in probability_signals):
return None
subtype = "dependent_events" if "without replacement" in low else "independent_events" if "with replacement" in low else "general_probability"
result = ExplainerResult(
understood=True,
topic="probability",
summary="This is a probability problem. The goal is to compare favorable outcomes to total possible outcomes.",
asks_for="the probability of a specific event or combination of events",
plain_english="Probability questions become much easier once you define exactly what counts as success and what the full sample space is.",
)
scaffold = ExplainerScaffold(
concept="Probability compares favorable outcomes to total outcomes.",
ask="Identify what counts as a successful outcome and what the total possible outcomes are.",
target="Set up favorable ÷ total before calculating.",
answer_hidden=True,
solution_path_type=subtype,
)
numbers = re.findall(r"\b\d+\b", low)
if numbers:
result.givens.append(f"Numbers mentioned: {', '.join(numbers[:5])}")
else:
result.givens.append("A situation involving possible outcomes is described.")
result.relationships = [
"Probability = favorable outcomes ÷ total possible outcomes",
]
result.needed_concepts = [
"favorable vs total outcomes",
"independent vs dependent events",
"complement rule",
]
result.trap_notes = [
"Make sure you correctly identify the total number of possible outcomes.",
"Check whether events are independent or dependent.",
"Watch for 'at least' — often easier to use the complement.",
"Be careful not to double count outcomes.",
]
result.strategy_hint = "Start by identifying all possible outcomes, then count how many satisfy the condition."
if "without replacement" in low:
result.constraints.append("The total number of items decreases after each draw.")
result.relationships.append("The probabilities change after each selection.")
if "with replacement" in low:
result.relationships.append("The probabilities stay the same for each selection.")
if "at least" in low:
result.relationships.append("This may be easier through the complement: 1 − probability of the opposite event.")
if "both" in low or "and" in low:
result.relationships.append("You may need to combine probabilities of multiple events occurring together.")
if "or" in low:
result.relationships.append("You may need to consider multiple favorable cases without double counting.")
scaffold.setup_actions = [
"Define what counts as a favorable outcome.",
"Count or describe the total possible outcomes.",
"Check whether the events are independent or dependent.",
]
scaffold.intermediate_steps = [
"If multiple events occur together, decide whether to multiply or add probabilities.",
"If the wording says 'at least', consider using the complement.",
"Check whether order matters.",
]
scaffold.first_move = "Start by identifying the total number of outcomes."
scaffold.next_hint = "Then count how many outcomes match the condition."
scaffold.common_traps = [
"Forgetting the total outcomes.",
"Mixing up 'and' vs 'or'.",
"Ignoring replacement vs no replacement.",
]
scaffold.key_operations = [
"count the sample space",
"count favorable cases",
"choose multiply/add/complement appropriately",
]
scaffold.hint_ladder = [
"What counts as success here?",
"What is the full sample space?",
"Do you need multiplication, addition, or the complement rule?",
]
result.teaching_points = [
"Probability = favorable / total.",
"Use multiplication for linked 'and' events when appropriate.",
"Use the complement when 'at least' is easier to solve indirectly.",
]
result.scaffold = scaffold
result.meta = {
"intent": "explain_question",
"bridge_ready": True,
"hint_style": "step_ready",
"subtype": subtype,
}
return result