An interactive web-based exercise generator for students, covering Math and Physics problems with real-time scoring.
Open index.html in a browser to see the dashboard. Choose Mathe-Übungen or Physik-Übungen. A random problem appears — type your answer, press Enter (or click Prüfen) to check it, then press Enter again for the next problem. Your score updates after each attempt.
python logic.pyThe original Python version is kept for reference. Press Enter for the next problem, type q to quit.
| Type | Description | Example |
|---|---|---|
add |
4-digit addition | 3456 + 7891 = |
sub |
Subtraction (positive results) | 12000 − 3500 = |
mul |
3-digit × single digit | 456 × 7 = |
div |
Division (whole number results) | 1440 ÷ 12 = |
dec_add |
Decimal addition | 12.45 + 8.32 = |
dec_sub |
Decimal subtraction | 25.67 − 4.23 = |
dec_mul |
Decimal multiplication | 5.5 × 3.2 = |
dec_div |
Decimal division | 45 ÷ 2.5 = |
frac_add |
Fraction addition | 3/4 + 2/5 = |
frac_sub |
Fraction subtraction | 7/3 − 2/5 = |
dreisatz |
Direct proportion (Rule of Three) | 5 Äpfel kosten 15€. Was kosten 8 Äpfel? |
dreisatz_inv |
Inverse proportion | 4 Arbeiter brauchen 6 Tage. Wie viele Tage brauchen 3 Arbeiter? |
umrechnung |
Unit conversion (length, weight, volume, time) | Rechne 3500 g in kg um. |
| Type | Given | Find | Example |
|---|---|---|---|
ohm_u |
R (Ω), I (A) | U (V) | R = 47 Ω, I = 2,5 A. Berechne U. |
ohm_r |
U (V), I (A) | R (Ω) | U = 120 V, I = 3 A. Berechne R. |
ohm_i |
U (V), R (Ω) | I (A) | U = 230 V, R = 46 Ω. Berechne I. |
code-math/
├── index.html # Dashboard (landing page)
├── math.html # Math exercises page
├── physics.html # Physics exercises page
├── style.css # App styling (shared)
├── js/
│ ├── problems.js # Math problem generation, answer checking, formatting
│ ├── app.js # Math UI event handling and state management
│ ├── physics-problems.js # Physics problem generation, answer checking, formatting
│ └── physics-app.js # Physics UI event handling and state management
├── tests.html # Browser test page
├── run-tests.js # Server-side test runner (Node.js)
├── logic.py # Legacy Python CLI version
└── package.json # NPM config
js/problems.js exports three functions via the MathProblems global:
Returns an object with:
| Field | Type | Description |
|---|---|---|
display |
string |
Problem text ending with = or ? (word problems) |
hint |
string |
Hint for fraction and Dreisatz problems (empty string otherwise) |
type |
string |
One of the 13 problem types listed above |
answer |
number or {num, den} |
Correct answer (fraction object for frac_* types) |
isFraction |
boolean |
true for frac_add / frac_sub |
answer |
number or {num, den} |
For Dreisatz: always a whole number |
Returns boolean — whether the user's input is correct.
| Parameter | Type | Description |
|---|---|---|
answer |
number or {num, den} |
The correct answer from generateTask() |
isFraction |
boolean |
Whether the problem is a fraction type |
userStr |
string |
The user's raw input |
Key behaviors:
- Decimal inputs: both
,and.are accepted as decimal separators - Numeric tolerance: answers within ±0.005 of the correct value are accepted
- Fraction equivalence: checked via cross-multiplication (
uNum * answer.den === uDen * answer.num), so any equivalent fraction is accepted (e.g.6/8for3/4) - Rejects empty/whitespace-only input, non-numeric text, and zero denominators
Returns string — the answer formatted for display.
- Numbers: converted to string directly
- Fractions: reduced to lowest terms using GCD (e.g.
{num:6, den:4}→"3/2")
- Dashboard landing page with subject selection (Math / Physics)
- German-language UI
- Real-time scoring (Richtig/Falsch counter)
- Keyboard support — Enter to submit answer or advance to next problem
- Comma and dot decimal separators accepted
- Fraction equivalence checking (any equivalent fraction is correct)
- Hints for fraction problems ("Antwort als Bruch"), Dreisatz problems ("Antwort in €" / "Antwort in Tagen"), unit conversions ("Antwort in km" etc.), and physics problems ("Antwort in V" / "Antwort in Ω" / "Antwort in A")
- Answers auto-reduced to lowest terms on display
- Back navigation from exercise pages to dashboard
Subtraction: The first operand (5000–15000) is always larger than the second (1000–4999), preventing negative results.
Division: The dividend is adjusted with a = a - (a % b) to guarantee whole number results.
Fraction Subtraction: Operands are compared and swapped if necessary to ensure non-negative results:
if (num1/den1 < num2/den2) {
// swap (num1,den1) with (num2,den2)
}js/physics-problems.js exports three functions via the PhysicsProblems global, following the same interface as MathProblems:
Returns an object with display, hint, type, answer, isFraction (always false).
Generation ensures clean answers:
ohm_u: R and I are generated directly, U = R × Iohm_r: R (integer) is chosen first, I is generated, U = R × I is computed. Given U and I, the student solves for R (always a whole number)ohm_i: I (1 decimal) is chosen first, R is generated, U = R × I is computed. Given U and R, the student solves for I (at most 1 decimal place)
Same tolerance-based approach as MathProblems (±0.005), accepts both comma and dot decimal separators.
Returns the answer as a plain string.
Generated by computing the answer first, then deriving the dividend:
var ans = randFloat(2, 20, 1); // Clean answer: 2.0–20.0
var b = randFloat(0.5, 5, 1); // Divisor: 0.5–5.0
var a = parseFloat((ans * b).toFixed(2)); // Dividend = answer × divisorThis guarantees every problem has an exact, mentally-computable answer (e.g., 15.5 ÷ 2.5 = 6.2).
Direct proportion (dreisatz): Generates word problems where more items cost proportionally more. The price per item is generated first, ensuring whole number answers:
var pricePerItem = randInt(2, 10);
var b = a * pricePerItem; // Total cost is always a multiple
answer = c * pricePerItem; // Answer is always wholeInverse proportion (dreisatz_inv): Generates word problems where more workers means fewer days. Total work-days is computed first, and the target worker count is chosen to divide evenly. The initial worker count b must differ from a to guarantee a valid c exists:
var bVal = randInt(2, 8);
while (bVal === a) bVal = randInt(2, 8); // Ensure b ≠ a
var totalWork = a * bVal;
while (totalWork % c !== 0 || c === a) c = randInt(2, 8); // Ensure clean division
answer = totalWork / c;node run-tests.js71 tests across 9 suites. See TESTS.md for details.