webdevpuneet.com

Regex Match Game — Free HTML CSS JS Snippet

Regex Match Game · Games · Plain HTML, CSS & JS · Live preview

CategoryGames

For supporters

Source Code

Want the source code?

Play the game for free. Become a WebDevPuneet supporter to unlock the complete HTML, CSS and JavaScript source code, the exports (HTML, React, Vue, Angular, Tailwind) and Fork.

Play freeSource code: Patreon

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Levels specified purely as positive and negative string sets, with no stored reference pattern to guess
Any pattern satisfying the specification is accepted, so equivalent formulations all pass
new RegExp() compiled inside try/catch, distinguishing empty, invalid and usable pattern states
Live per-keystroke retesting of every string in both columns via RegExp.test()
Raw verdict printed next to each string alongside a pass/fail colour for the desired outcome
Too-strict vs too-loose diagnosis with counts, naming the over-broad-pattern failure mode explicitly
Adversarial negative lists that force anchors, escaping and precise quantifiers to be discovered
Seven levels covering literals, character classes, +, ^, \d, alternation, escaped dots and $

About this UI Snippet

Regex Match Game — Live RegExp.test Grading, Positive and Negative String Sets & Too-Loose Diagnosis

Screenshot of the Regex Match Game snippet rendered live

The hard part of regular expressions is never writing something that matches — it is writing something that matches the right things and nothing else. A pattern that passes your three examples and quietly also matches half your production data is the classic regex bug, and it is invisible if you only ever test the positive cases. This snippet is a playable trainer built around that exact tension: every level gives a list of strings the pattern must match and a second list it must reject, and a level is only cleared when both lists are fully satisfied.

Grading against string sets, not a stored answer

There is no reference pattern anywhere in the level data. Each level is defined purely by its yes and no arrays, and grading runs the player's compiled RegExp against every string in both lists with re.test(s), requiring true for every positive and false for every negative. That means any pattern satisfying the specification passes — on the file-extension level, \.(png|jpg)$, \.(jpe?g|png)$ and other equivalent formulations are all accepted. Because the specification *is* the test set, the game teaches the habit of thinking about a regex in terms of what it accepts and rejects rather than in terms of a memorised incantation.

Compiling user input without letting it break the page

compile() wraps new RegExp(pattern) in try/catch and returns null on failure, because a half-typed pattern like [a- or (png| throws a SyntaxError on nearly every keystroke while the player is composing. Returning null lets callers distinguish three states cleanly — empty input, invalid syntax, and a usable expression — so the input can show a red border mid-typing without clearing the result marks or spilling errors into the console. The input is also length-capped in the markup, which keeps a pathological pattern from being pasted in wholesale.

Live per-string verdicts on both columns

paint() walks a list, tests each string, and applies a pass or fail class based on whether the actual result equals the *desired* result for that column — so in the left column a match is a pass, and in the right column a match is a failure. Each row also prints the raw verdict ("match" or "no") next to the string, which separates the two things a learner needs to see at once: what the regex did, and whether that was what the level wanted. Because both columns are repainted on every input event, a player watches strings flip between columns' pass states character by character as the pattern is built.

Failure messages that name the direction of the error

When a submission is not perfect, the status line compares the two column scores and reports the specific failure mode: "too strict" when every negative is correctly rejected but some positives are still unmatched, "too loose" when every positive matches but some negatives matched too, and a plain score when both columns have problems. Too-loose is the regex bug that matters most in real code, and naming it explicitly — rather than saying "wrong" — is what turns a wrong answer into a lesson about over-broad patterns.

Levels as a progression of concepts

The seven levels move through literal substrings, character classes, the + quantifier, the ^ start anchor, the \d digit shorthand, alternation with an escaped literal dot and the $ end anchor, and finally a both-ends-anchored email-shaped pattern. Each carries a one-line hint explaining the technique rather than giving the answer, and the negative lists are chosen adversarially — level four rejects "blog", "catalog" and "analogue" specifically so an unanchored log fails and the player has to discover ^.

Step by step

How to Use

  1. 1
    Read both columns before typingThe green column lists strings your pattern must match; the red column lists strings it must not. The negative list is chosen adversarially — on the "log" level it contains blog, catalog and analogue, so an unanchored pattern will visibly fail.
  2. 2
    Type a pattern between the slashesThe input sits between two literal slashes to mirror regex literal syntax. Every keystroke recompiles the pattern and retests all six strings, so you see the effect of adding a single character immediately.
  3. 3
    Watch the per-string verdictsEach row prints whether the regex matched it ("match" or "no") and turns green or red depending on whether that was the desired outcome for its column — a match in the right-hand column is a failure, not a success.
  4. 4
    Press Enter or click Test to submitA submission is only accepted when every string in both columns is correct. There is no reference answer to guess: any pattern that satisfies the specification clears the level.
  5. 5
    Read the direction of your missA failed submission tells you whether you were too strict (positives still unmatched) or too loose (negatives matched as well) with counts, which is the distinction that matters most when debugging a real regex.
  6. 6
    Use hints and work through all seven levelsShow hint explains the technique the level is about — character classes, the + quantifier, anchors, \d, alternation, escaping a literal dot — without giving the pattern away. Levels ramp from a literal substring to an anchored email-shaped expression.

Real-world uses

Common Use Cases

Teaching regular expressions in a course or team workshop
Specifying levels as accept/reject sets rather than answers builds the habit that actually prevents production regex bugs — reasoning about the full input space instead of the happy path. Pair it with a trie autocomplete visualizer when moving on to how string matching is implemented underneath.
Interactive docs for a product with user-authored patterns
Log search tools, redaction rules, routing configs and validation builders all expose regex to end users. A short trainer next to the docs lets people practise against sample data before pointing a pattern at anything that matters.
Developer marketing, careers pages, and conference booth screens
A regex challenge is instantly recognisable to a technical audience and takes under a minute to play. It sits well beside other learn-by-playing snippets such as the CSS Selector Challenge Game on a developer-focused page.
Reference for safely evaluating user-supplied expressions
The compile-in-try/catch pattern with a three-state return (empty, invalid, usable) is the correct shape for any input where users type an expression the app must execute — filter builders, search syntax fields, and rule editors all need exactly this handling.
Live validation-preview pattern for form builders
Form builders that let an author attach a validation regex to a field can borrow this UI directly: sample values on one side, live match verdicts on the other, so the author sees what their rule accepts before publishing the form.
Test-first thinking demonstrated in a UI
The game is effectively a tiny test runner — a specification of expected passes and failures, re-evaluated on every change. It works nicely as a visual explanation of test-driven development for people who have not seen a test suite before.
Related: Sorting Swap Puzzle Game
See the Sorting Swap Puzzle Game for a related games pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

No. Each level is defined only by its list of strings that must match and strings that must not; grading runs your compiled pattern against all of them with RegExp.test() and clears the level when every verdict is correct. On the image-extension level, \.(png|jpg)$ and \.(jpe?g|png)$ both pass because both satisfy the specification.

Incomplete patterns like [a-z or (png| are invalid regex and make new RegExp() throw a SyntaxError. compile() catches that and returns null, which the UI treats as "not valid yet" — the input border turns red, the string verdicts reset to a neutral dot, and nothing is logged as an error.

It means every string in the must-match column matched, but at least one string in the must-not-match column matched as well — your pattern is over-broad. That is the most common and most dangerous regex bug in real code, which is why the game names it specifically instead of just saying the answer was wrong.

Push an object onto LEVELS with four keys: goal (the instruction), yes (an array of strings the pattern must match), no (an array it must reject), and hint (a one-line explanation of the technique). No answer key is needed. Make the no array adversarial — near-misses of the yes strings are what force precise patterns.

Yes. Keep LEVELS and the compile() helper in a plain module, hold the pattern string and level index in component state, and derive the per-string verdicts during render instead of mutating classes — the whole grading step is a pure function of (pattern, level), which maps neatly onto useMemo in React, a computed property in Vue, or a computed signal in Angular. The only imperative bits are focusing the input and the advance timeout, which belong in useEffect/onMounted/ngAfterViewInit with clearTimeout on cleanup.