j-js commited on
Commit
82ffe01
·
verified ·
1 Parent(s): 0711dc8

Update explainers/explainer_percent.py

Browse files
Files changed (1) hide show
  1. explainers/explainer_percent.py +188 -151
explainers/explainer_percent.py CHANGED
@@ -1,229 +1,264 @@
 
 
1
  import re
2
  from .explainer_types import ExplainerResult, ExplainerScaffold
3
 
4
 
5
- _PERCENT_PATTERNS = [
6
- r"\bpercent\b",
7
- r"\bpercentage\b",
8
- r"\bwhat percent\b",
9
- r"\bpercent of\b",
10
- r"\bpercent greater than\b",
11
- r"\bpercent less than\b",
12
- r"\bincrease(?:d)? by \d+ ?%\b",
13
- r"\bdecrease(?:d)? by \d+ ?%\b",
14
- r"\bdiscount\b",
15
- r"\bmarkup\b",
16
- r"\binterest\b",
17
- r"\btax\b",
18
- r"\btip\b",
19
- r"\bprofit margin\b",
20
- r"\bcommission\b",
21
- r"\bpart of\b",
22
- r"\bof a number\b",
23
  ]
24
 
25
 
26
- def _looks_like_percent_question(text: str) -> bool:
27
  low = (text or "").lower()
28
- if "%" in low:
 
29
  return True
30
- return any(re.search(p, low) for p in _PERCENT_PATTERNS)
 
 
31
 
32
 
33
- def _infer_percent_subtype(text: str) -> str:
34
  low = (text or "").lower()
35
 
36
- if any(k in low for k in ["increase", "decrease", "increased", "decreased", "percent change", "changed by"]):
37
- return "percent_change"
38
- if any(k in low for k in ["discount", "sale", "off", "markdown"]):
39
- return "discount"
40
- if any(k in low for k in ["interest", "simple interest", "compound interest"]):
41
- return "interest"
42
- if any(k in low for k in ["tax", "vat", "tip", "gratuity"]):
43
- return "tax_tip"
44
- if any(k in low for k in ["what percent", "is what percent of", "as a percent of"]):
45
- return "percent_of_percent"
46
- if any(k in low for k in ["percent of", "of a number", "% of"]):
47
- return "basic_percent"
48
- return "generic_percent"
49
-
50
-
51
- def explain_percent_question(text: str):
52
- if not _looks_like_percent_question(text):
53
  return None
54
 
55
- subtype = _infer_percent_subtype(text)
56
 
57
  result = ExplainerResult(
58
  understood=True,
59
- topic="percent",
60
- 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."
 
 
 
61
  )
62
 
63
  scaffold = ExplainerScaffold(
64
- concept="Percent problems are built around a base amount, a compared amount, and a rate per hundred.",
65
- 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.",
66
- target="Translate the wording into the correct percent relationship before doing any arithmetic.",
67
  answer_hidden=True,
 
 
68
  )
69
 
70
  teaching_points = [
71
- "Percent means per hundred, so it should usually be converted to a decimal or fraction before setting up the relationship.",
72
- "Most percent questions reduce to one of a small number of templates, so identifying the template matters more than rushing into calculation.",
73
- "The biggest source of error is choosing the wrong base quantity."
74
  ]
75
 
76
- if subtype == "basic_percent":
77
  scaffold.setup_actions = [
78
- "Label the known quantities as part, whole, and percent.",
79
- "Convert the percent to a decimal or fraction.",
80
- "Match the statement to the relationship: part = percent × whole."
81
  ]
82
  scaffold.intermediate_steps = [
83
- "If the whole is unknown, assign it a variable.",
84
- "Substitute the known values into the relationship.",
85
- "Solve only after the structure is correct."
86
  ]
87
- scaffold.first_move = "Start by identifying the whole and the part."
88
- scaffold.next_hint = "Once those roles are clear, write one equation connecting part, percent, and whole."
89
  scaffold.variables_to_define = [
90
- "Let x represent the unknown quantity if either the part or whole is missing."
91
  ]
92
  scaffold.equations_to_form = [
93
- "part = percent × whole"
94
  ]
95
  scaffold.common_traps = [
96
- "Using the wrong quantity as the whole.",
97
- "Using 40 instead of 0.40.",
98
- "Treating the wording as a percent change problem when it is only a basic percent-of problem."
99
  ]
100
-
101
- elif subtype == "percent_of_percent":
102
- scaffold.setup_actions = [
103
- "Identify the comparison being made.",
104
- "Place the compared quantity over the base quantity.",
105
- "Convert the resulting fraction to a percent."
106
  ]
107
- scaffold.intermediate_steps = [
108
- "Make sure the denominator is the reference/base amount.",
109
- "Simplify the fraction if helpful.",
110
- "Only convert to percent at the end."
111
- ]
112
- scaffold.first_move = "Ask: percent of what?"
113
- scaffold.next_hint = "Use the base quantity as the denominator in the fraction before converting."
114
- scaffold.equations_to_form = [
115
- "percent = (part / whole) × 100"
116
- ]
117
- scaffold.common_traps = [
118
- "Reversing numerator and denominator.",
119
- "Using the larger number automatically as the denominator instead of the stated base.",
120
- "Forgetting to multiply by 100 at the end if the question asks for a percent."
121
  ]
122
 
123
- elif subtype == "percent_change":
124
  scaffold.setup_actions = [
125
- "Identify the original value and the new value.",
126
- "Find the amount of change first.",
127
- "Use the original value as the base in the percent-change formula."
128
  ]
129
  scaffold.intermediate_steps = [
130
- "Compute change = new original.",
131
- "Place that change over the original value.",
132
- "Convert the result to a percent."
133
  ]
134
- scaffold.first_move = "Find the amount of increase or decrease before thinking about the percent."
135
- scaffold.next_hint = "After that, divide by the original amount, not the new amount."
136
  scaffold.equations_to_form = [
137
- "percent change = (new original) / original × 100"
138
  ]
139
  scaffold.common_traps = [
140
- "Using the new amount as the denominator.",
141
- "Skipping the change step and comparing the wrong two quantities.",
142
- "Losing track of whether the result should be described as an increase or decrease."
 
 
 
 
 
 
 
 
 
 
 
143
  ]
144
 
145
- elif subtype == "discount":
146
  scaffold.setup_actions = [
147
- "Identify the original price and the discount rate.",
148
- "Find the discount amount from the original price.",
149
- "Subtract the discount from the original price if the question asks for the sale price."
150
  ]
151
  scaffold.intermediate_steps = [
152
- "Treat the listed price as the base.",
153
- "Compute the discount amount separately from the final price.",
154
- "Check whether the question asks for discount amount or discounted price."
155
- ]
156
- scaffold.first_move = "Use the original price as the percent base."
157
- scaffold.next_hint = "Find the discount amount first, then decide whether to stop or subtract."
158
- scaffold.equations_to_form = [
159
- "discount = rate × original price",
160
- "sale price = original price − discount"
161
  ]
 
 
162
  scaffold.common_traps = [
163
- "Applying the discount to the wrong quantity.",
164
- "Giving the discount amount when the question asks for the final price.",
165
- "Treating multiple discounts as one simple combined subtraction without checking the structure."
 
 
 
 
 
 
 
 
 
 
 
166
  ]
167
 
168
- elif subtype == "interest":
169
  scaffold.setup_actions = [
170
- "Identify principal, rate, and time.",
171
- "Determine whether the question is simple interest or compound interest.",
172
- "Set up the matching formula structure."
173
  ]
174
  scaffold.intermediate_steps = [
175
- "For simple interest, keep the principal fixed.",
176
- "For compound interest, recognize that the base changes from period to period.",
177
- "Check whether the question asks for interest earned or final amount."
178
  ]
179
- scaffold.first_move = "Work out whether the interest is simple or compound."
180
- scaffold.next_hint = "Then identify which quantity stays fixed and which quantity grows."
181
  scaffold.common_traps = [
182
- "Using a simple-interest structure on a compound-interest question.",
183
- "Confusing interest earned with total amount.",
184
- "Ignoring the time unit matching between rate and period."
 
 
 
 
 
 
 
 
 
 
 
185
  ]
186
 
187
- elif subtype == "tax_tip":
188
  scaffold.setup_actions = [
189
- "Identify the pre-tax or pre-tip amount.",
190
- "Use that original amount as the base.",
191
- "Compute the tax or tip amount before adding if a total is required."
192
  ]
193
  scaffold.intermediate_steps = [
194
- "Separate the added charge from the final amount.",
195
- "Check whether multiple percentages are applied independently or sequentially.",
196
- "Make sure the question wants the fee amount or the full total."
197
- ]
198
- scaffold.first_move = "Use the original listed amount as the base unless the wording clearly says otherwise."
199
- scaffold.next_hint = "Find the added percent amount first, then combine only if the question asks for the total."
200
- scaffold.equations_to_form = [
201
- "added amount = rate × base amount",
202
- "total = base amount + added amount"
203
  ]
 
 
204
  scaffold.common_traps = [
205
- "Using the final total as the percent base.",
206
- "Stopping at the tax or tip amount when the question asks for the total bill.",
207
- "Blending separate percentages together without checking the wording."
 
 
 
 
 
 
 
 
 
 
 
208
  ]
209
 
210
  else:
211
  scaffold.setup_actions = [
212
- "Identify the base quantity.",
213
- "Identify the compared quantity or changed amount.",
214
- "Match the wording to a standard percent relationship."
215
  ]
216
  scaffold.intermediate_steps = [
217
- "Convert the percent appropriately.",
218
- "Build one clear relationship before solving.",
219
- "Check whether the question asks for a value, a rate, or a final amount."
220
  ]
221
- scaffold.first_move = "Work out what the percent is being taken of."
222
- scaffold.next_hint = "Once the base quantity is clear, write the relationship before calculating."
223
  scaffold.common_traps = [
224
- "Choosing the wrong base.",
225
- "Not converting the percent properly.",
226
- "Using the right numbers in the wrong relationship."
 
 
 
 
 
 
 
 
 
 
 
227
  ]
228
 
229
  result.teaching_points = teaching_points
@@ -231,7 +266,9 @@ def explain_percent_question(text: str):
231
  result.meta = {
232
  "intent": "explain_question",
233
  "bridge_ready": True,
234
- "hint_style": "step_ready",
235
  "subtype": subtype,
 
 
236
  }
237
  return result
 
1
+ from __future__ import annotations
2
+
3
  import re
4
  from .explainer_types import ExplainerResult, ExplainerScaffold
5
 
6
 
7
+ _ALGEBRA_PATTERNS = [
8
+ r"=",
9
+ r"\bsolve\b",
10
+ r"\bequation\b",
11
+ r"\bexpression\b",
12
+ r"\bvalue of\b",
13
+ r"\bwhat is x\b",
14
+ r"\bwhat is y\b",
15
+ r"\bvariable\b",
 
 
 
 
 
 
 
 
 
16
  ]
17
 
18
 
19
+ def _looks_like_algebra_question(text: str) -> bool:
20
  low = (text or "").lower()
21
+
22
+ if re.search(r"\b[xyzab]\b", low) and "=" in low:
23
  return True
24
+ if any(re.search(p, low) for p in _ALGEBRA_PATTERNS):
25
+ return True
26
+ return False
27
 
28
 
29
+ def _infer_algebra_subtype(text: str) -> str:
30
  low = (text or "").lower()
31
 
32
+ if any(k in low for k in ["system", "simultaneous", "x and y", "two equations"]):
33
+ return "system"
34
+ if any(k in low for k in ["inequality", "<", ">", "at least", "at most", "no more than"]):
35
+ return "inequality"
36
+ if any(k in low for k in ["quadratic", "squared", "^2", "x2", "root", "factor"]):
37
+ return "quadratic"
38
+ if any(k in low for k in ["expression", "value of 2x", "value of x +", "in terms of"]):
39
+ return "expression_evaluation"
40
+ if "=" in low:
41
+ return "linear_equation"
42
+ return "generic_algebra"
43
+
44
+
45
+ def explain_algebra_question(text: str):
46
+ if not _looks_like_algebra_question(text):
 
 
47
  return None
48
 
49
+ subtype = _infer_algebra_subtype(text)
50
 
51
  result = ExplainerResult(
52
  understood=True,
53
+ topic="algebra",
54
+ summary=(
55
+ "This is an algebra problem. The main goal is to translate the wording into a clean "
56
+ "symbolic relationship and isolate the requested quantity step by step."
57
+ ),
58
  )
59
 
60
  scaffold = ExplainerScaffold(
61
+ concept="Algebra represents unknown quantities symbolically, then uses valid transformations to isolate or compare them.",
62
+ ask="Identify the unknown, identify the governing relationship, and check whether the question wants the variable itself or an expression built from it.",
63
+ target="Set up the simplest correct equation or relation before manipulating it.",
64
  answer_hidden=True,
65
+ solution_path_type=subtype,
66
+ reveal_threshold=3,
67
  )
68
 
69
  teaching_points = [
70
+ "Most algebra errors happen before the solving starts: either the variable is misdefined, or the equation is set up incorrectly.",
71
+ "A clean equation makes the solving steps much easier.",
72
+ "You should always check whether the question asks for x itself or for something derived from x.",
73
  ]
74
 
75
+ if subtype == "linear_equation":
76
  scaffold.setup_actions = [
77
+ "Identify the unknown and write the equation cleanly.",
78
+ "Simplify each side if needed.",
79
+ "Undo operations in a logical order to isolate the variable.",
80
  ]
81
  scaffold.intermediate_steps = [
82
+ "Combine like terms first when possible.",
83
+ "Move variable terms and constant terms carefully.",
84
+ "Check whether the final result should be the variable or a substituted expression.",
85
  ]
86
+ scaffold.first_move = "Rewrite the relationship as one clean equation if it is not already in that form."
87
+ scaffold.next_hint = "Simplify both sides before isolating the variable."
88
  scaffold.variables_to_define = [
89
+ "Let the unknown quantity be x if the question has not already named it.",
90
  ]
91
  scaffold.equations_to_form = [
92
+ "Build one equation from the stated relationship.",
93
  ]
94
  scaffold.common_traps = [
95
+ "Moving terms across the equals sign incorrectly.",
96
+ "Trying to isolate the variable before simplifying.",
97
+ "Finding x and forgetting the question asks for something like 2x or x + 3.",
98
  ]
99
+ scaffold.key_operations = [
100
+ "rewrite as equation",
101
+ "simplify both sides",
102
+ "use inverse operations",
 
 
103
  ]
104
+ scaffold.hint_ladder = [
105
+ "Start by spotting the equation you need to work with.",
106
+ "Simplify both sides before you try to isolate the variable.",
107
+ "Undo the operation attached to the variable on both sides.",
108
+ "Keep simplifying until only the variable remains.",
 
 
 
 
 
 
 
 
 
109
  ]
110
 
111
+ elif subtype == "system":
112
  scaffold.setup_actions = [
113
+ "Identify the separate equations and unknowns.",
114
+ "Decide whether substitution or elimination is the cleaner method.",
115
+ "Reduce the system to one variable before solving completely.",
116
  ]
117
  scaffold.intermediate_steps = [
118
+ "Make one variable easy to substitute, or align coefficients for elimination.",
119
+ "After finding one variable, substitute back carefully.",
120
+ "Check whether the question asks for one variable, both variables, or a combination of them.",
121
  ]
122
+ scaffold.first_move = "Choose one variable to eliminate or substitute."
123
+ scaffold.next_hint = "Turn the system into a single-variable equation before solving."
124
  scaffold.equations_to_form = [
125
+ "Use the two given equations together to reduce to one unknown.",
126
  ]
127
  scaffold.common_traps = [
128
+ "Mixing substitution and elimination without a clear plan.",
129
+ "Arithmetic mistakes when substituting back.",
130
+ "Stopping after finding one variable when the question asks for something else.",
131
+ ]
132
+ scaffold.key_operations = [
133
+ "choose elimination or substitution",
134
+ "reduce to one variable",
135
+ "substitute back",
136
+ ]
137
+ scaffold.hint_ladder = [
138
+ "Pick the variable that looks easiest to remove.",
139
+ "Use substitution or elimination to make a one-variable equation.",
140
+ "Solve that reduced equation first.",
141
+ "Then substitute back into the other equation.",
142
  ]
143
 
144
+ elif subtype == "inequality":
145
  scaffold.setup_actions = [
146
+ "Translate the condition into an inequality.",
147
+ "Manipulate it like an equation, but track the inequality direction carefully.",
148
+ "Reverse the sign only if multiplying or dividing by a negative number.",
149
  ]
150
  scaffold.intermediate_steps = [
151
+ "Simplify both sides first if possible.",
152
+ "Isolate the variable systematically.",
153
+ "Interpret the final solution set in the form the question wants.",
 
 
 
 
 
 
154
  ]
155
+ scaffold.first_move = "Set up the inequality carefully from the wording."
156
+ scaffold.next_hint = "Solve it step by step, watching for any operation that would reverse the sign."
157
  scaffold.common_traps = [
158
+ "Forgetting to reverse the inequality when dividing or multiplying by a negative.",
159
+ "Treating phrase-based conditions like at least or no more than incorrectly.",
160
+ "Reporting a single number when the solution is actually a range.",
161
+ ]
162
+ scaffold.key_operations = [
163
+ "translate wording",
164
+ "simplify",
165
+ "track sign reversal",
166
+ ]
167
+ scaffold.hint_ladder = [
168
+ "Translate the words into an inequality first.",
169
+ "Simplify before isolating the variable.",
170
+ "Watch carefully for multiplication or division by a negative.",
171
+ "Express the result as the correct range or set.",
172
  ]
173
 
174
+ elif subtype == "quadratic":
175
  scaffold.setup_actions = [
176
+ "Rewrite the equation so one side is zero if needed.",
177
+ "Look for factoring, structure, or another simplifying method.",
178
+ "Treat each factor or case carefully once the equation is structured properly.",
179
  ]
180
  scaffold.intermediate_steps = [
181
+ "Factor if the form allows it.",
182
+ "Otherwise identify another clean solving route.",
183
+ "Check whether all resulting values are allowed in the original context.",
184
  ]
185
+ scaffold.first_move = "Put the expression into a standard structured form before solving."
186
+ scaffold.next_hint = "Then look for a factorable pattern or another clean route."
187
  scaffold.common_traps = [
188
+ "Trying to factor before the expression is fully simplified.",
189
+ "Dropping one valid case.",
190
+ "Giving roots when the question asks for a derived expression instead.",
191
+ ]
192
+ scaffold.key_operations = [
193
+ "rewrite to zero",
194
+ "factor or restructure",
195
+ "check cases",
196
+ ]
197
+ scaffold.hint_ladder = [
198
+ "Rewrite the equation in a standard form first.",
199
+ "Look for a common factor or factorable pattern.",
200
+ "Use the structure to split into cases or solve the quadratic.",
201
+ "Check whether all resulting values fit the original problem.",
202
  ]
203
 
204
+ elif subtype == "expression_evaluation":
205
  scaffold.setup_actions = [
206
+ "Find the variable or relationship first.",
207
+ "Only then substitute into the requested expression.",
208
+ "Simplify the final expression carefully.",
209
  ]
210
  scaffold.intermediate_steps = [
211
+ "Do not stop when you find the variable unless that is exactly what the question asks.",
212
+ "Preserve parentheses during substitution.",
213
+ "Check whether there is a shortcut using the given relationship directly.",
 
 
 
 
 
 
214
  ]
215
+ scaffold.first_move = "Work out whether you need to solve for the variable first or can rewrite the target expression directly."
216
+ scaffold.next_hint = "Once the relationship is clear, substitute only into the exact expression the question asks for."
217
  scaffold.common_traps = [
218
+ "Stopping at x when the question asks for something built from x.",
219
+ "Substituting incorrectly into expressions with multiple terms.",
220
+ "Ignoring an easier algebraic simplification path.",
221
+ ]
222
+ scaffold.key_operations = [
223
+ "solve or rewrite relationship",
224
+ "substitute into target expression",
225
+ "simplify target",
226
+ ]
227
+ scaffold.hint_ladder = [
228
+ "Check whether the question wants x or something built from x.",
229
+ "Use the given relationship to find or rewrite the target expression.",
230
+ "Substitute only into the exact expression asked for.",
231
+ "Simplify the resulting expression carefully.",
232
  ]
233
 
234
  else:
235
  scaffold.setup_actions = [
236
+ "Define the unknown clearly.",
237
+ "Translate the wording into a symbolic relationship.",
238
+ "Manipulate the relationship only after the setup is clean.",
239
  ]
240
  scaffold.intermediate_steps = [
241
+ "Simplify before isolating.",
242
+ "Keep track of what the question actually asks for.",
243
+ "Check the final quantity against the prompt.",
244
  ]
245
+ scaffold.first_move = "Start by translating the words into one clean symbolic statement."
246
+ scaffold.next_hint = "Then simplify the structure before solving."
247
  scaffold.common_traps = [
248
+ "Poor variable definition.",
249
+ "Messy setup before solving.",
250
+ "Answering the wrong final quantity.",
251
+ ]
252
+ scaffold.key_operations = [
253
+ "define unknown",
254
+ "translate relationship",
255
+ "simplify and isolate",
256
+ ]
257
+ scaffold.hint_ladder = [
258
+ "Define the unknown clearly.",
259
+ "Translate the words into one symbolic relationship.",
260
+ "Simplify before solving.",
261
+ "Then isolate the target quantity.",
262
  ]
263
 
264
  result.teaching_points = teaching_points
 
266
  result.meta = {
267
  "intent": "explain_question",
268
  "bridge_ready": True,
269
+ "hint_style": "progressive",
270
  "subtype": subtype,
271
+ "solution_path_type": scaffold.solution_path_type,
272
+ "key_operations": list(scaffold.key_operations),
273
  }
274
  return result