GameAI / explainers /explainer_percent.py
j-js's picture
Update explainers/explainer_percent.py
37e4b67 verified
raw
history blame
10.6 kB
import re
from .explainer_types import ExplainerResult, ExplainerScaffold
_PERCENT_PATTERNS = [
r"\bpercent\b",
r"\bpercentage\b",
r"\bwhat percent\b",
r"\bpercent of\b",
r"\bpercent greater than\b",
r"\bpercent less than\b",
r"\bincrease(?:d)? by \d+ ?%\b",
r"\bdecrease(?:d)? by \d+ ?%\b",
r"\bdiscount\b",
r"\bmarkup\b",
r"\binterest\b",
r"\btax\b",
r"\btip\b",
r"\bprofit margin\b",
r"\bcommission\b",
r"\bpart of\b",
r"\bof a number\b",
]
def _looks_like_percent_question(text: str) -> bool:
low = (text or "").lower()
if "%" in low:
return True
return any(re.search(p, low) for p in _PERCENT_PATTERNS)
def _infer_percent_subtype(text: str) -> str:
low = (text or "").lower()
if any(k in low for k in ["increase", "decrease", "increased", "decreased", "percent change", "changed by"]):
return "percent_change"
if any(k in low for k in ["discount", "sale", "off", "markdown"]):
return "discount"
if any(k in low for k in ["interest", "simple interest", "compound interest"]):
return "interest"
if any(k in low for k in ["tax", "vat", "tip", "gratuity"]):
return "tax_tip"
if any(k in low for k in ["what percent", "is what percent of", "as a percent of"]):
return "percent_of_percent"
if any(k in low for k in ["percent of", "of a number", "% of"]):
return "basic_percent"
return "generic_percent"
def explain_percent_question(text: str):
if not _looks_like_percent_question(text):
return None
subtype = _infer_percent_subtype(text)
result = ExplainerResult(
understood=True,
topic="percent",
summary="This is a percent problem. The key is to identify which quantity is the base, which is the compared amount, and what role the percent is playing."
)
scaffold = ExplainerScaffold(
concept="Percent problems are built around a base amount, a compared amount, and a rate per hundred.",
ask="Decide which quantity is the whole/base, which quantity is the part or changed amount, and whether the question is asking for a percent, a value, or a new total.",
target="Translate the wording into the correct percent relationship before doing any arithmetic.",
answer_hidden=True,
)
teaching_points = [
"Percent means per hundred, so it should usually be converted to a decimal or fraction before setting up the relationship.",
"Most percent questions reduce to one of a small number of templates, so identifying the template matters more than rushing into calculation.",
"The biggest source of error is choosing the wrong base quantity."
]
if subtype == "basic_percent":
scaffold.setup_actions = [
"Label the known quantities as part, whole, and percent.",
"Convert the percent to a decimal or fraction.",
"Match the statement to the relationship: part = percent × whole."
]
scaffold.intermediate_steps = [
"If the whole is unknown, assign it a variable.",
"Substitute the known values into the relationship.",
"Solve only after the structure is correct."
]
scaffold.first_move = "Start by identifying the whole and the part."
scaffold.next_hint = "Once those roles are clear, write one equation connecting part, percent, and whole."
scaffold.variables_to_define = [
"Let x represent the unknown quantity if either the part or whole is missing."
]
scaffold.equations_to_form = [
"part = percent × whole"
]
scaffold.common_traps = [
"Using the wrong quantity as the whole.",
"Using 40 instead of 0.40.",
"Treating the wording as a percent change problem when it is only a basic percent-of problem."
]
elif subtype == "percent_of_percent":
scaffold.setup_actions = [
"Identify the comparison being made.",
"Place the compared quantity over the base quantity.",
"Convert the resulting fraction to a percent."
]
scaffold.intermediate_steps = [
"Make sure the denominator is the reference/base amount.",
"Simplify the fraction if helpful.",
"Only convert to percent at the end."
]
scaffold.first_move = "Ask: percent of what?"
scaffold.next_hint = "Use the base quantity as the denominator in the fraction before converting."
scaffold.equations_to_form = [
"percent = (part / whole) × 100"
]
scaffold.common_traps = [
"Reversing numerator and denominator.",
"Using the larger number automatically as the denominator instead of the stated base.",
"Forgetting to multiply by 100 at the end if the question asks for a percent."
]
elif subtype == "percent_change":
scaffold.setup_actions = [
"Identify the original value and the new value.",
"Find the amount of change first.",
"Use the original value as the base in the percent-change formula."
]
scaffold.intermediate_steps = [
"Compute change = new − original.",
"Place that change over the original value.",
"Convert the result to a percent."
]
scaffold.first_move = "Find the amount of increase or decrease before thinking about the percent."
scaffold.next_hint = "After that, divide by the original amount, not the new amount."
scaffold.equations_to_form = [
"percent change = (new − original) / original × 100"
]
scaffold.common_traps = [
"Using the new amount as the denominator.",
"Skipping the change step and comparing the wrong two quantities.",
"Losing track of whether the result should be described as an increase or decrease."
]
elif subtype == "discount":
scaffold.setup_actions = [
"Identify the original price and the discount rate.",
"Find the discount amount from the original price.",
"Subtract the discount from the original price if the question asks for the sale price."
]
scaffold.intermediate_steps = [
"Treat the listed price as the base.",
"Compute the discount amount separately from the final price.",
"Check whether the question asks for discount amount or discounted price."
]
scaffold.first_move = "Use the original price as the percent base."
scaffold.next_hint = "Find the discount amount first, then decide whether to stop or subtract."
scaffold.equations_to_form = [
"discount = rate × original price",
"sale price = original price − discount"
]
scaffold.common_traps = [
"Applying the discount to the wrong quantity.",
"Giving the discount amount when the question asks for the final price.",
"Treating multiple discounts as one simple combined subtraction without checking the structure."
]
elif subtype == "interest":
scaffold.setup_actions = [
"Identify principal, rate, and time.",
"Determine whether the question is simple interest or compound interest.",
"Set up the matching formula structure."
]
scaffold.intermediate_steps = [
"For simple interest, keep the principal fixed.",
"For compound interest, recognize that the base changes from period to period.",
"Check whether the question asks for interest earned or final amount."
]
scaffold.first_move = "Work out whether the interest is simple or compound."
scaffold.next_hint = "Then identify which quantity stays fixed and which quantity grows."
scaffold.common_traps = [
"Using a simple-interest structure on a compound-interest question.",
"Confusing interest earned with total amount.",
"Ignoring the time unit matching between rate and period."
]
elif subtype == "tax_tip":
scaffold.setup_actions = [
"Identify the pre-tax or pre-tip amount.",
"Use that original amount as the base.",
"Compute the tax or tip amount before adding if a total is required."
]
scaffold.intermediate_steps = [
"Separate the added charge from the final amount.",
"Check whether multiple percentages are applied independently or sequentially.",
"Make sure the question wants the fee amount or the full total."
]
scaffold.first_move = "Use the original listed amount as the base unless the wording clearly says otherwise."
scaffold.next_hint = "Find the added percent amount first, then combine only if the question asks for the total."
scaffold.equations_to_form = [
"added amount = rate × base amount",
"total = base amount + added amount"
]
scaffold.common_traps = [
"Using the final total as the percent base.",
"Stopping at the tax or tip amount when the question asks for the total bill.",
"Blending separate percentages together without checking the wording."
]
else:
scaffold.setup_actions = [
"Identify the base quantity.",
"Identify the compared quantity or changed amount.",
"Match the wording to a standard percent relationship."
]
scaffold.intermediate_steps = [
"Convert the percent appropriately.",
"Build one clear relationship before solving.",
"Check whether the question asks for a value, a rate, or a final amount."
]
scaffold.first_move = "Work out what the percent is being taken of."
scaffold.next_hint = "Once the base quantity is clear, write the relationship before calculating."
scaffold.common_traps = [
"Choosing the wrong base.",
"Not converting the percent properly.",
"Using the right numbers in the wrong relationship."
]
result.teaching_points = teaching_points
result.scaffold = scaffold
result.meta = {
"intent": "explain_question",
"bridge_ready": True,
"hint_style": "step_ready",
"subtype": subtype,
}
return result