Skip to content

feat(labs): implement Water Jug and Equation Crafting lab features - #131

Merged
jgupta05072003-code merged 6 commits into
vicharanashala:mainfrom
Krishna009-pro:feat/water-jug-and-equation-crafting-labs
Sep 7, 2026
Merged

jgupta05072003-code merged 6 commits into
vicharanashala:mainfrom
Krishna009-pro:feat/water-jug-and-equation-crafting-labs

Conversation

@Krishna009-pro

@Krishna009-pro Krishna009-pro commented Jul 31, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Tenali is a larger learning platform with a number of interactive STEM modules already in place. This PR covers my contribution to it: a rework of two specific modules, the Water Jug Lab and the Equation Crafting Lab.

The main goal was to make both labs easier for younger kids (5+) to actually learn from, without dumbing down the underlying math. That meant breaking the Water Jug problem into a much gentler difficulty curve and cleaning up a bunch of UI clutter in Equation Crafting.

Neither lab is a small, cosmetic touch-up — the Water Jug Lab in particular went from a single undifferentiated difficulty setting to an actual 22-step curriculum with a real onboarding path. Equation Crafting was more of a layout and polish pass, but it fixes a genuinely annoying scrolling problem and removes duplicate heading text that had built up over a few iterations.

Why

Water Jug Lab is really teaching linear Diophantine equations ($ax + by = c$), where a solution only exists if $c$ is a multiple of $\gcd(a, b)$. That's a genuinely subtle number theory idea, and the old version had no middle ground between trivial one-pour problems and puzzles requiring five or more non-obvious steps, with no scaffolding in between.

This PR adds a 13-level curve (Level 0–12) plus a slower 22-step "intuition journey" so kids can build a feel for remainders, GCD limits, and unsolvable cases before tackling the real puzzles. The intuition journey is meant to be almost impossible to fail — it's less a puzzle and more a guided tour of the mechanic, so that by the time a kid hits the actual leveled puzzles they already understand what pouring and emptying do.

Equation Crafting Lab teaches expression-building, brackets, and operator precedence via a "mixing pot" metaphor. The old layout had a duplicate title, an oversized crucible container with a lot of dead space, and enough vertical padding stacked up across cards that the whole thing didn't fit on a laptop screen without scrolling. This PR tightens that up into one screen that fits on anything 1366×768 or larger.

What changed

Water Jug Lab

13-level difficulty progression (Level 0–12)

image

The levels are grouped into four tiers, verified directly from WaterJugLab.jsx:

  • Level 0 (first win): 1L & 2L jugs, target 1L. Solvable in one move. Exists purely to give a first-time player an immediate win with zero friction.
  • Levels 1–6 (co-prime warm-up): Jug pairs with GCD = 1, meaning everything is technically reachable, but each target requires a few careful pours: (2,3)→1, (3,5)→3, (3,5)→2, (3,5)→4, (4,7)→3, (5,8)→2. BFS-based hints are available if a kid gets stuck.
  • Levels 7–9 (multi-step, non-trivial GCD): (4,6)→2 with GCD=2, (6,9)→3 with GCD=3, and (7,11)→4 with GCD=1. These require multiple drain-and-fill cycles rather than a single pour sequence. This is where the underlying algorithm really has to click.
  • Levels 10–11 (unsolvable traps): (4,6)→3 — GCD is 2, and 3 is not divisible by 2, so this is genuinely impossible. (6,9)→5 — GCD is 3, and 5 is not divisible by 3, also impossible. Kids can pour and empty as much as they like and eventually conclude for themselves that no sequence of moves reaches the target.
  • Level 12 (grandmaster): (9,13)→7 — GCD = 1, fully solvable but requires up to 16 moves. A fitting final challenge.
image

On Levels 10 and 11 specifically, the Pour/Empty buttons are no longer disabled the way they were at lower difficulties. Previously a difficulty < 11 guard was preventing interaction at the wrong point. Now kids can keep interacting with the jugs indefinitely on the unsolvable levels, which matters pedagogically: the "aha" moment is supposed to come from exhausting the possibilities themselves, not from the UI telling them it's impossible.

The level selector itself was simplified too — instead of verbose difficulty descriptions, it's now just clean "Level 0" through "Level 12" badges in a row.

Intuition journey (steps 1–22)

image

This is a separate, gentler track that sits before the main leveled puzzles — think of it as a tutorial mode rather than a difficulty tier.

The 22 steps build concepts one at a time in a deliberate order:

Steps Concepts introduced
1–4 Water as a liquid, containers, fill, empty
5–10 Capacity, overflow, amount, counting, comparing
11–14 Two containers together, pouring, water movement, total water
15–16 Goal-setting, multi-step puzzle (Fill A → Pour B → Fill A → 2L!)
17–18 Impossible goal (1L, 3L, 5L always fail), pattern spotting
19–22 Jump sizes (4&6 → jumps of 2), different pairs, hidden rule, GCD as the official name
image
  • Replaced static text-card explanations with actual glass jug visuals that show animated water height changes (transition: height 0.4s ease), so pouring and filling look like something physically happening.
  • Added capacity markers and labels like "Jug A (4L)" directly on the visual.
  • Controls were cut down to just two buttons on this track: Fill A and Pour A → B. The full multi-button control grid from the main game was overkill here.
  • A few contextual popups were added at key discovery moments — Level 16 pops up when the target amount gets isolated, Level 17 explicitly calls out that the puzzle is impossible rather than just letting it quietly fail forever.
  • Progress header is standardized across all 22 steps: Level {n} of 22 • {concept name}, flex-start aligned with an 8px gap.

Offline support

Added a local generateLevelData(difficulty) fallback generator (lines 582–612 in WaterJugLab.jsx), so the lab runs entirely without a working backend. Previously the lab depended on a /jug-api/question endpoint that doesn't reliably exist in all environments. Now it falls back silently.

Equation Crafting Lab

image
  • <QuizLayout> now receives title="" while phase === 'playing', which removes the large centered "Equation Crafting (Level 1)" heading that was crowding the top of the gameplay screen. The title is still shown on the setup screen.
  • The setup screen's header buttons were switched from absolute positioning (position: absolute; top: 24px; left: 24px) to a proper flex container (display: flex; justify-content: flex-start). width: 100% was added to .header-row in App.css so the "← Home" / "← Menu" back buttons sit flush against the top-left padding boundary.
  • The crucible container was shrunk: .crucible-inner min-height went from 150px to 90px, and .crucible-pot-wrapper padding went from 25px to 14px 20px. The section title was simplified to plain "Your Mixing Pot (Crucible)" text.
  • Vertical gaps were tightened: .gameplay-area gap went from 25px to 14px, and .alchemy-card padding was reduced to 14px 20px. Combined, the lab now fits on a standard 1080p or laptop screen without scrolling.
image

Solve:

image

Design tokens used

Nothing new — everything pulls from the existing token set:

Token Used for
var(--clr-bg) page background
var(--clr-card) setup/gameplay card surfaces
var(--clr-border) borders on buttons/cards
var(--clr-accent) target expression badge, active pills
var(--clr-text-soft) subtitles, hints, secondary text

Files touched

client/src/
├── App.css # width: 100% on .header-row for left-aligned nav
├── App.jsx # QuizLayout only renders title when non-empty
├── WaterJugLab.jsx # 13 levels, 22-step intuition journey, bug fixes
├── WaterJugLab.css # jug visuals, water animation, step badges
├── EquationCraftingLab.jsx # equation crafting page
└── EquationCraftingLab.css # equation crafting page css

Testing

npm run build
# ✓ built in 4.46s — 0 errors, 475 modules transformed

Manually checked:

  • All 13 level badges render correctly in the selector
  • Fill/pour/empty work across both solvable and unsolvable levels
  • Intuition journey water animations and 2-button controls work on steps 1–22
  • Level 16 popup triggers on target isolation; Level 17 shows impossibility message
  • Equation Crafting setup screen — back button sits top-left as expected
  • Equation Crafting gameplay — no more duplicate title, menu button aligned
  • Layout holds up at 1920×1080, 1366×768, and tablet sizes

@Krishna009-pro
Krishna009-pro force-pushed the feat/water-jug-and-equation-crafting-labs branch 2 times, most recently from e870c6d to 246a5b1 Compare July 31, 2026 14:01
…3-level difficulty progression, and UI layout refinements
@Krishna009-pro
Krishna009-pro force-pushed the feat/water-jug-and-equation-crafting-labs branch from 246a5b1 to 6d8c96f Compare July 31, 2026 14:16

@Vaibhav-sa30 Vaibhav-sa30 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update your PR after doing suggested changes

Comment thread server/index.js
} catch (e) {
return NaN;
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input being not validated is a critical security issue. an attacker can send a crafted HTTP POST request containing arbitrary JavaScript to execute commands directly on the server. Sanitize userExpression (and target)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Comment thread server/index.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No /jug-api routes are defined in server/index.js or elsewhere on the server. Either implement the backend /jug-api routes to match the client's design, or remove the network request block and mark the Water Jug Lab as a client-side-only feature to prevent console error spam.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@Krishna009-pro

Copy link
Copy Markdown
Contributor Author

Hi @Vaibhav-sa30, thanks for the review! I've pushed the fixes:

Security: Added strict input regex sanitization on userExpression & target to block code execution vulnerabilities in /alchemy-api/check.
/jug-api: Implemented backend /jug-api/question and /jug-api/check endpoints in server/index.js and updated vite.config.js.
Please take a look when you have a moment!

@Krishna009-pro
Krishna009-pro force-pushed the feat/water-jug-and-equation-crafting-labs branch from a441ed9 to b645499 Compare September 1, 2026 13:38
@jgupta05072003-code

Copy link
Copy Markdown
Collaborator

Thank you for this work, and apologies for the delayed response.

We have now reviewed the security concern raised during the earlier review on 31 July, and it can be confirmed that the issue is addressed.

The validation added in this PR is:

const SAFE_MATH_REGEX = /^[\d\s\+\-\*\/\^\(\)\.xab]*$/;

Because this permits only digits, arithmetic operators, and the letters x, a and b, the bypass techniques described in your own write up are not reachable. Approaches such as this['pro'+'cess'] require square brackets and quote characters, neither of which is permitted, and identifiers such as constructor cannot be constructed from the allowed character set. The keyword list applied alongside the pattern is largely redundant as a result, though it does no harm.

The concern raised in review is therefore resolved. Responding to review feedback with a proper fix rather than a partial one is appreciated.

There are two follow up points we would like to address before merging.

1. It may be preferable to use a mathematical expression parser rather than new Function

Your onboarding document proposes using mathjs and its parse() function in place of new Function, and that recommendation appears sound.

It may be worth implementing that change now rather than deferring it. At present, main contains no occurrences of new Function or eval anywhere in server/index.js. This PR would introduce the first one.

The current validation is effective, but its effectiveness depends entirely on a single pattern. If that character class is later extended, for example to support an additional operator or variable name, the original risk would be reintroduced, and the person making that change would have no clear indication of the consequence.

An expression parser does not carry that risk, since the input is never executed as code. As the approach has already been outlined in your document, the change should be reasonably contained.

2. A clarification would be helpful in the onboarding document

The section titled "Gap 1" describes the evaluator as pre-existing:

In server/index.js, around lines 1529 to 1543, there's an evalMathExpr function used inside the /alchemy-api/check endpoint

On checking main, there is no evalMathExpr function, no /alchemy-api endpoint, and no use of new Function in server/index.js. That code is introduced by this PR rather than inherited from the existing codebase.

The analysis itself is genuinely useful, and the explanation of why keyword filtering is insufficient against arbitrary JavaScript is accurate and clearly written. It would simply be more accurate to frame it as a risk identified within the code being added, and the mitigation applied, rather than as an existing gap in the repository. Onboarding documents are frequently the first reference point for new contributors, and the current wording may lead someone to look for an issue on main that is not present.

Additional notes

  1. This PR contains two separate labs, Water Jug and Equation Crafting. Combining them makes review more difficult and prevents either from being reverted independently. This is not a blocking concern here, but separating such work in future would be helpful.
  2. The /jug-api routes are now present, so the second point raised in the earlier review is resolved.

The remainder of the implementation looks good, and the client side work is well structured. We would be happy to merge once the expression parser change is in place.

@jgupta05072003-code

Copy link
Copy Markdown
Collaborator

Thank you for this, Krishna. I ran both labs locally from b6454993 — they build clean and the core mechanics work. Before this can be merged, though, I went through it as a student would, and the learning experience needs work. Four things:

1. It is not clear what I am supposed to do.

In the Equation Crafting Lab I am shown a target of 12, four number tiles, and four operators — but nothing tells me why I am clicking numbers and operators, or what the goal of the exercise is. A child opening this will not know where to begin. The mechanics are implemented; the teaching around them is missing. Please add a short instruction or worked example on the first screen that says plainly what to do and why.

2. The Home button is in the centre of the screen.

In the Equation Crafting Lab, ← Home sits centred at the top. It should be in the top-left corner, which is where a user reaches for it. The Water Jug Lab already puts ← Menu on the left — please make the two consistent, with the left-corner placement.

3. I can skip the entire Water Jug module without learning anything.

I verified this: from Level 1, clicking Next Concept → moved me to Level 2 and then Level 3 without ever clicking "Touch the River Water!" or interacting with the concept at all. Nothing checks that the student engaged with the step. A child can click straight through all 22 levels and reach the end having learned nothing, while the progress bar shows completion.

Each step needs a gate: the student must complete the interaction before Next Concept → becomes available.

4. The instructions are not self-explanatory.

Lines like "Water is a liquid you can touch and splash!" and "Containers hold water safely inside." describe the object but never connect to the actual mathematics — this lab is meant to teach GCD discovery. A student cannot tell what the concept is, what they did wrong, or how to correct it. Please make each step state what is being taught and give feedback when the answer is wrong, not just when it is right.

One small inconsistency: the home tile describes this as a "13-level progression" but the lab itself shows "LEVEL 1 OF 22". Please make these agree.

The engineering is sound and the features are genuinely a good idea. What needs another pass is the pedagogy — a student should not be able to finish a module without understanding it. Please address these and I will re-review.

@Krishna009-pro

Copy link
Copy Markdown
Contributor Author

Hi @jgupta05072003-code and @Vaibhav-sa30 👋

Thank you so much for the detailed review and guidance! I have addressed all 4 points along with the mathjs security refactoring:

  1. 🎯 Equation Crafting Instructions & Worked Example: Added a short worked example on the setup screen explaining step-by-step how to select number tiles, pick an operator (+, -, ×, ÷), and fuse them into the target equation.
  2. ↖️ Top-Left Home Button: Moved the ← Home back button in EquationCraftingLab.jsx to the top-left corner so it matches WaterJugLab.jsx.
  3. 🔒 Step-Gating in Water Jug Lab: The Next Concept → button in the 22-Step Intuition Journey is now gated — students must interact with that step (splash water, fill cup, or pour jug) before they can proceed.
  4. 📐 Math Connection & Level Naming: Connected each step's description directly to the Greatest Common Divisor (GCD) and Euclid's Algorithm, and clarified the distinction between the 22-Step Intuition Journey (tutorial track) and 13-Level Leveled Puzzles.
  5. 🛡️ Replaced new Function with mathjs: Refactored server/index.js to use math.evaluate() from the mathjs library instead of new Function.

Detailed Implementation Overview

💧 1. Water Jug Lab: Component-by-Component Intuition Journey
As suggested, the 22-step Intuition Journey now introduces one small component at a time so kids build confidence progressively before tackling full leveled puzzles:

  • Single Water Splash & Cup (Steps 1–4): Introduces basic water mechanics with 1 cup.
  • Single Jug Filling (Steps 5–8): Teaches capacity, filling, and emptying a single 3L jug.
  • Two-Jug Interaction & Transfer (Steps 9–14): Teaches pouring between 3L and 5L jugs with a 🫗 Reset Jugs button from Level 12+ so state never gets stuck.
  • Target Measurement & GCD Discovery (Steps 15–22): Guides students to measure target amounts (e.g. 4L) and connects physical pouring to Greatest Common Divisor (GCD) and Euclid's Algorithm ($a \cdot x + b \cdot y = d$).
  • Step-Gating Added: The Next Concept → button is now gated until the child performs the interaction for that specific step, ensuring active hands-on learning.

🧪 2. Equation Crafting Lab: Minimal, Intuitive UI for Kids
Per @Vaibhav-sa30's minimal UI feedback and keeping the interface clean for children:

  • Minimalist Visual Layout: Kept text lightweight and clean so children can visually pick numbers, select operators (+, -, ×, ÷), and click Fuse Reagents without text overload.
  • Lightweight Quick Tip: Added a subtle, minimal hint indicator explaining tile selection without cluttering the screen.
  • Header Alignment: Aligned the ← Home back button to the top-left corner (justify-content: flex-start), keeping UI consistent with Water Jug Lab.

🛡️ 3. Security & Code Refactoring

  • Replaced new Function: Switched /alchemy-api/check in server/index.js to use math.evaluate() from the mathjs library, eliminating arbitrary JavaScript execution vectors.
  • Input Sanitization: Maintained regex validation (SAFE_MATH_REGEX) for mathematical payload validation.

All updates have been tested locally (node --check server/index.js and npm run build passed with 0 errors). Ready for merge! 🚀

@Krishna009-pro

Krishna009-pro commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Hi @jgupta05072003-code mam, just checking in to see if the recent updates in ae66425 look good on your end, or if any further adjustments are needed before merge order 1. Thank you!

@Krishna009-pro

Copy link
Copy Markdown
Contributor Author

Thank you, @jgupta05072003-code mam, for your support and mentorship throughout this contribution. The PR has been successfully merged. I really appreciate your guidance and support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants