# memory-game is a Kotlin rebuild of a kids match game with generated vector art

> memory-game is an Android matching game for children aged four and over, revived as a Kotlin rewrite with four themes, mini-games, a friends album and twenty one languages, whose artwork is regenerated from originals by a Gradle task. It is a case study in rebuilding a shipped app without adding a single permission.

**sromku/memory-game** — Simple and beautiful memory game for kids (since 2014)

- Repository: https://github.com/sromku/memory-game
- Website: https://play.google.com/store/apps/details?id=com.snatik.matches
- Stars: 597 · Forks: 416
- Language: Kotlin
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/sromku-memory-game

## A matching game for four year olds, and nothing else

The project describes itself as a memory game for kids aged 4 and over, with no ads, no accounts and nothing collected. The play is simple to state. You press Play, walk a road of rounds, find the pairs before the clock runs out, and collect stars. That description is the whole design brief, and everything else in the repository exists to serve it. The content structure gives the game its shape. There are four themes, Animals, Monsters, Emojis and Ocean, each with its own progress, and six roads per theme ranging from 3x2 up to 10x5 cards, with 100 rounds in each road, and the next road opening as you play. Every fifth round is not a matching round at all but one of seven mini-games: Who was here, Follow the song, What changed, Shadow match, Odd one out, Shopping list and Peek and find. Round completion brings stars, confetti and hopping friends, and the menu shows a friend of the day. A friends album collects a character every ten rounds, and tapping one meets it by name. It works on phones and tablets, in horizontal orientation, and offline.

## The child safety rules are enforced by the build, not just documented

The rules for changes are the most distinctive part of this repository. The Play listing declares the game for children and certifies COPPA and GDPR compliance, and any change must keep it free of data collection, network access, permissions, ads, purchases and third-party SDKs. That is a hard constraint on a project's architecture, and it is not just a note in the documentation. CLAUDE.md spells it out, and the checkChildSafety task fails the build when it can tell. So the constraint is mechanically enforced rather than trusted, which is the difference between a policy and a guard rail. It also explains several design choices that would otherwise look odd. The game works offline, with no accounts and nothing collected, because anything that would require a network call or a permission would fail the check. A parents' corner sits behind a hold-to-open gate, with the privacy policy and a start-over option, which is the pattern used to keep a settings area out of reach of a four year old's thumb. The privacy policy itself is published from a separate site repository rather than from this one, and the signing keys for releases never live in this repository either.

## From Java to Kotlin, and a twelve level jump in the meantime

The project history is told in three acts, and the middle one explains the shape of the current codebase. From 2014 to 2019 the game was written in Java, released on Google Play as Memory Game (Free), and published here as open source, with the last release at version 1.01.001007 in February 2019. From 2019 to 2026 maintenance stopped, Google Play removed the app in November 2019 for not keeping up with the developer policies, and the code sat still while Android moved on by twelve API levels. The third act is the revival. The whole app was converted from Java to Kotlin, restructured, debugged, tested and prepared for release purely with AI, using Claude Code, with the original artwork and gameplay kept intact. Then it grew, from a single round per difficulty to roads of a hundred rounds per theme, the seven mini-games arrived, the friends album filled up, the menu came alive, and the game learned twenty new languages. The current codebase is a standard Android Studio project with Kotlin, AndroidX and the Gradle Kotlin DSL, targeting SDK 36 with a minimum of SDK 24, and it is on its way back to the store.

## Gradle tasks are the build interface, and one of them builds the art

The documented build commands cover the whole loop from a debug build to a signed release artefact, and the comments tell you what each is for:

```bash
./gradlew :app:assembleDebug        # debug build, installs next to the store version
./gradlew :app:testDebugUnitTest    # game-logic unit tests
./gradlew :app:lintDebug            # lint, warnings are errors
./gradlew checkKeystore             # verify the signing keystore configuration
scripts/verify-release.sh           # signed release APK + AAB with pre-upload checks
```

The lint configuration treating warnings as errors is a detail worth noticing, because it is the same philosophy as the child safety check. The build is a gate, not just a compiler. The signed release path goes through a separate script rather than a Gradle task, and it runs pre-upload checks before producing the APK and the AAB, with the signing and Play Store checklist in docs/RELEASE.md. The unit test task is scoped to the game logic, which is the part that is worth testing, since the rest of the app is drawing. The repository layout is the standard Android one, with an app/ module, an art/ directory, a docs/ directory, a tools/ directory, a scripts/ directory, the Gradle Kotlin DSL build files and a CLAUDE.md at the root.

## The artwork is generated, and the words are drawn at runtime

The art pipeline is the most unusual part of the project and it exists for a specific reason. The originals live in art/original, and everything the app draws is generated from them by one Gradle task, so a change to an original is a rerun and a commit. Cards are vector characters. Each picture is upscaled 4x with Real-ESRGAN, traced to paths with vtracer, and its parts, body, eyes, pupils and shadow, are tagged by geometry into character files under app/src/main/assets/characters. The app draws them on a Canvas, so they stay crisp at any size, and it animates them, breathing and blinking while face up and hopping when matched. Tagging mistakes are corrected in art/character-overrides.txt. The UI art, buttons, popups and theme cards, becomes one vector drawable per asset, with colours bled into the transparent area, the silhouette traced from the alpha channel and used as a clip path, and the soft drop shadow turned into one translucent path. Regenerating that art needs two tools, and the commands are given:

```bash
brew install webp
# download realesrgan-ncnn-vulkan and vtracer for macOS from their GitHub releases
./gradlew regenerateArt -Prealesrgan=/path/to/realesrgan-ncnn-vulkan -Pvtracer=/path/to/vtracer
```

The linguistic consequence is the point of all this. The pictures carry no words. The lettered originals are kept in art/original/lettered, tools/erase-lettering.py writes de-lettered copies, and the app draws the words at runtime in the language of the player. The title is a hatched texture that does not trace well and stays WebP, with English keeping the hand made original and tools/generate-titles.py drawing the other languages in the same style.

## Twenty one languages, and a font that cannot cover all of them

Localisation is handled as data rather than as resourced strings scattered through the code. English plus twenty languages, the translations are Python data in tools/i18n, one dictionary per language, and tools/i18n/generate.py writes the values-<locale> resources from them. The font situation is where the constraint bites. Grobold, the display font, has Latin letters only, so the game font resolves per language to Rubik, Baloo 2 or Mitr, all under the Open Font License, and Chinese, Japanese and Korean use the system font. That is a real engineering decision rather than a cosmetic one. A display face drawn as paths cannot be swapped per language, so the solution was to strip the lettering from the artwork and draw text at runtime, which then requires a font that can actually render the script in question. The design document for this lives in docs/design/localisation.md. The licence structure is also layered. The code is under the Apache License 2.0, while the artwork is licensed separately from its authors through four graphicriver listings, and the fonts carry the SIL Open Font License with the licences in docs/licenses. So the repository is open source in the code sense, and the pictures and typefaces have their own terms, which is the honest way to describe a project built on purchased assets.

## Conclusion

Adopt memory-game if you want a complete, child-safe Android game you can read end to end, and you are willing to work within hard constraints on data collection and dependencies. Do not adopt it as a starting point for a game with accounts, ads or in-app purchases, because the child safety rules are enforced by the build and would block that work. Before you modify it, read CLAUDE.md and the release checklist in docs/RELEASE.md, and note that the artwork pipeline needs Real-ESRGAN and vtracer locally if you change the original art.

## FAQ

### What is the memory-game app and who is it for?

It is a matching game for children aged four and over, with no ads, no accounts and nothing collected. You press Play, walk a road of rounds, find the pairs before the clock runs out and collect stars, and it works on phones and tablets, in horizontal orientation, offline.

### What constraints do changes to memory-game have to respect?

The Play listing declares the game for children and certifies COPPA and GDPR compliance, so any change must keep it free of data collection, network access, permissions, ads, purchases and third-party SDKs. CLAUDE.md spells this out and the checkChildSafety task fails the build when it can tell.

### How is the artwork in memory-game produced?

The originals live in art/original and everything the app draws is generated from them by one Gradle task. Cards are upscaled 4x with Real-ESRGAN and traced to paths with vtracer, UI art becomes one vector drawable per asset, and the letters are removed from the pictures with tools/erase-lettering.py so the app can draw the words at runtime in the player's language.

### How do I build the memory-game app?

It is a standard Android Studio project using Kotlin, AndroidX and the Gradle Kotlin DSL, with target SDK 36 and min SDK 24. The documented tasks are `./gradlew :app:assembleDebug` for a debug build, `./gradlew :app:testDebugUnitTest` for the game logic tests, `./gradlew :app:lintDebug` for lint where warnings are errors, and scripts/verify-release.sh for a signed release APK and AAB.

### What licence covers the memory-game code and artwork?

The code is under the Apache License 2.0. The artwork is licensed separately from its authors through graphicriver listings, and the fonts Rubik, Baloo 2 and Mitr are under the SIL Open Font License, with those licences kept in docs/licenses.

## Sources

- [Issues](https://github.com/sromku/memory-game/issues)
- [License: Apache-2.0](https://github.com/sromku/memory-game/blob/master/LICENSE)
- [Project website](https://play.google.com/store/apps/details?id=com.snatik.matches)
- [README](https://github.com/sromku/memory-game/blob/master/README.md)
- [sromku/memory-game on GitHub](https://github.com/sromku/memory-game)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/sromku-memory-game
