![]() |
Nickname Generator 5.2.2
Gamer-style nickname generation for C++23
|
This guide covers every feature of the nickname-generator library in detail. For a quick overview, see the README. For the full API reference, run doxygen Doxyfile from the repository root and open doc/api/html/index.html.
dasmig/nicknamegen.hpp, dasmig/core.hpp and the dasmig/core/ directory into your include path, keeping them side by side.resources/ folder (lite/ with the .words files) so it is accessible at runtime, or compile the lists in (see Compiled-in Data).-std=c++23.The library ships one tier, resources/lite/; dataset::full loads the same lists.
On first access the singleton (nng::instance()) loads it. It uses the compiled-in copy when built with DASMIG_NICKNAME_EMBED, and otherwise looks for lite/animals.words in these directories, relative to the working directory:
resources../resourcesnickname-generator/resources../nickname-generator/resourcesload(dataset::lite) searches the same way and returns false when it finds nothing. If your word lists are elsewhere, load the directory explicitly:
load(path) reads every .words and .tsv file below the directory, in path order, and replaces all lists and layers loaded before. A path that is not a directory is ignored; a malformed file throws dasmig::core::tsv_error and leaves the lists unchanged. To add to what is loaded, use extend() (see Extending Data).
Both generation overloads require a dasmig::nickname_request: get_nickname(request) uses the instance's normal random engine, and get_nickname(request, seed) is deterministic and const.
| Request field | Type | Default | Meaning |
|---|---|---|---|
name | std::wstring | Empty | Optional full name; empty or only spaces selects from word lists |
The request owns its name and can be reused. Word lists, resource paths and data layers are instance setup, not request fields. Decoration and case probabilities retain the existing model described below.
When a name is provided there is a 25 % chance the nickname will be derived from it (using initials, mixing name parts, etc.). Otherwise a random word from the loaded lists is used. Without word lists, the name is always used.
Each generated nickname passes through two stages:
The exact probabilities are in Data and Method.
With probability one half the nickname is decorated: half of the time with one of reverse, duovowel, oneleet or allleet (retried while the text does not change), otherwise with one of reverse, xfy, yfy, numify, tracefy or ingify.
| Transform | Example |
|---|---|
| reverse | emankcin |
| duovowel | nicknamee |
| oneleet | n1ckname |
| allleet | n1cknam3 |
| xfy | XnicknameX |
| yfy | nicknamy |
| numify | nickname2000 |
| tracefy | nickname- |
| ingify | nicknaming |
A case format is randomly selected with weighted distribution:
| Format | Weight | Example |
|---|---|---|
| lower_case | 8 | nickname |
| sentence_case | 5 | Nickname |
| upper_case | 4 | NICKNAME |
| bathtub_case | 3 | NicknamE |
| title_case (kept as is) | 2 | NickName |
| camel_case (first letter lower) | 2 | nickName |
| reverse_sentence_case | 2 | nicknamE |
| winding_case | 1 | nIcKnAmE |
| random_case | 1 | niCKnaMe |
| random_single_case | 1 | nicknaMe |
There is also a 1 % chance of snake_case being applied first (Nick_Name).
The library ships nine plain word lists and 43 theme lists in resources/lite/ (see Data and Method); every shipped plain list has at least 150 words and every theme at least 24:
| File | Contents |
|---|---|
adjectives.words | English adjectives |
animals.words | Animal names |
japanese.words | Japanese-themed words |
colors.words, food.words, nature.words, space.words, myth.words, gems.words | Colours, food, weather and plants, astronomy, creatures and gods, gemstones |
tag_<tag>.words | A theme, drawn from when a request lists the tag (below) |
A .words file has one word per line; the list's name is the file name. Lists of your own are added with extend() (see Extending Data). A list is chosen with a chance proportional to the square root of its word count, then a word from it uniformly, so a long list is drawn more often than a short one, but a 100-times longer list only 10 times as often.
nickname_request.style picks the kind of nickname:
| Style | Result |
|---|---|
nickname_style::gaming (default) | A gaming handle, decorated and cased freely: xXNioiXx, dR4g0n, iLoveCake |
nickname_style::username | A username: john.smith, jsmith92, swiftfox, quiet_otter_07 |
A username is lowercase ASCII letters and digits, at most 24 characters, with . or _ between parts and never at the end. From a name it takes the shapes people use (john.smith, johnsmith, jsmith, smith_john, ...), with a middle name (john.m.smith, jmsmith) when there is one, and leaves out particles (da, von); a double surname gives either name. About one in thirteen gets a prefix or a suffix (real, the_, _official, hq). Digits are often a year: pass birth_year and the two- or four-digit year is the most likely ending (john.smith92, jsmith1992); without it they are a made-up year, a small or lucky number or a pattern. Accents are folded (é is e) and characters with no ASCII form are dropped, so a name in another script gives word-based usernames. tags work as for a gaming handle: a themed word is the first word. plain() is the name, or the words joined by a space. The default style is unchanged. See Data and Method for the shapes and their chances.
A request can carry tags, plain strings that name themes, so a character's nickname can suit the character. They are the tags that trait-generator and occupation-generator put on traits and jobs ("combat", "leadership", "manual", ...); the library does not depend on either, and any strings work.
With at least one tag that has a theme (tag_<tag>.words), one nickname in three is a word of a theme: a theme is chosen uniformly among the request's themes (a repeated tag counts once), then a word from it. The decorations and cases apply as to any other word. The rest of the time the nickname is drawn as without tags. A tag without a theme is ignored, so a request with only unknown tags gives exactly the nickname of the same request without tags. When the request also has a name, the name takes its quarter first, then the themes their third of the rest.
Themes are lists like the others: extend() adds words to a shipped theme, and a file named tag_pirate.words adds a new one. Theme words are never drawn without their tag. The tags are not part of the replay signature, so keep them with the seed.
Each nng instance is independent. The static instance() singleton uses a local static for safe initialization.
| Operation | Thread-safe? |
|---|---|
instance() | Yes (static local) |
get_nickname(request) on different instances | Yes |
get_nickname(request) on the same instance | No — requires external synchronization |
get_nickname(request, seed) on the same instance | Yes, if word lists are not changed concurrently |
load(), extend() | No — must not be called concurrently with get_nickname() on the same instance |
Load and extend once during initialization before spawning threads. For concurrent generation, give each thread its own nng instance; the seeded overload get_nickname(request, seed) is const and does not touch the engine.
Pass an explicit seed to get_nickname() to produce the same nickname every time:
Every nickname records its seed. Retrieve it with nickname::seed() and pass it back with the same request and word lists to reproduce the exact same result:
Supply a seed for each call to produce a reproducible sequence. There are no generator-level seeding controls:
Explicitly seeded calls do not advance the instance's random engine. Calls without a seed continue normal random generation.
Use has_wordlists() before generating to verify resources loaded, and list_names() to see which lists are in use:
In addition to the singleton instance(), you can construct independent nng objects. Each instance owns its own word lists and random engine, so they operate without shared state — ideal for embedding inside other generators or running multiple configurations side by side.
An explicit seed affects only that call, not another instance or later calls:
Instances can be moved (but not copied), which lets you store them in containers or transfer ownership:
The shipped lists are a base layer. Your own files are loaded on top of them to add lists, add words or remove words, without editing the shipped files. A list's words are its keys.
dasmig::core::tsv_error with the file, line and column, and the lists stay as they were; a directory is applied all or nothing.layers("animals") lists what is loaded for one list: source, mode, words added, replaced and removed, and warnings such as unknown columns..words nor .tsv is refused with std::invalid_argument.load(...) replaces the base and drops every layer.Every result records the seed and the data it came from:
to_json() gives the result as JSON (UTF-8), the form the WebAssembly layer and the game consume:
Seeds are strings because JavaScript numbers cannot hold every 64-bit value. to_json(writer) writes into an existing dasmig::core::json_writer, for composing larger documents.
Define DASMIG_NICKNAME_EMBED before including the header to compile the word lists into the program (about 50 KB). load(dataset::lite) and instance() then use them and need no files, which is what a WebAssembly build wants. The compiled-in lists have the same data_hash() as the files, so seeds replay the same either way.
dasmig/nicknamegen_data.hpp is generated from resources/lite by make embed; CI checks it is up to date.
Version 5 draws the list in proportion to the square root of its size instead of uniformly, adds 84 animals, and makes the lists added in version 4 (and the themes) several times longer. The same seed gives a different nickname than in v4. The API is unchanged. Update any known answers; the data_hash() changes with the word lists.
Version 4 adds six plain word lists, so the same seed gives a different nickname than in v3 (a list is chosen uniformly among nine instead of three). The API only grows: nickname_request has a new tags field. Update any known answers; the data_hash() changes with the word lists.
Generation is now request-first, with only get_nickname(request) and get_nickname(request, seed). Replace no-argument calls with get_nickname({}), and string arguments with get_nickname({.name = L"Player Name"}) (or a reusable nickname_request). For deterministic calls, the seed remains the second argument.
Generator-level seed() and unseed() are removed. To generate deterministic sequences, pass an explicit seed on each call; to resume normal random generation, omit the seed. nickname::seed() remains available for replay. The request, loaded word lists and explicit seed reproduce the previous per-call seeded results unchanged. load(), extend() and resource queries remain instance APIs.
Version 2 moves the library onto dasmig-core.
std::towupper and std::towlower, so whether accented letters changed case depended on the C locale and the platform. v2 maps Basic Latin, Latin-1 Supplement and Latin Extended-A itself; other letters keep their case.resources/lite/, and repeated words were removed (38 in animals, 4 in japanese), so those words are no longer two or three times as likely. Lists are used in name order, not in the order the filesystem returns them.load(path) replaces** the lists instead of adding to them; use extend() to add. A malformed file now throws dasmig::core::tsv_error. load(dataset) is new.random.hpp with core.hpp and the core/ directory (see Installation).extend(), extend_from_string(), layers(), list_names(), data_hash(), nickname::signature(), nickname::to_json(), DASMIG_NICKNAME_EMBED, .tsv word lists.Every seeded getter has batch forms: get_nickname_batch return a std::vector, and for_each_nickname streams the items to a sink in index order without holding the batch.
i is exactly get_nickname(request, core::item_seed(batch_seed, first + i)). A range can be generated directly (first = 1000 starts at item 1000), two batches that follow each other equal one, and the output is the same for any threads (0 means the hardware count). Without a batch_seed one is drawn like the single getter draws its own.core::batch_options has count, first, threads and unique. The request describes one item; the options describe the batch..unique = core::unique_rule::by<nickname>(key) regenerates an item whose key already appeared in the batch, with a deterministic retry seed, and throws std::runtime_error naming the item when the attempts run out.core::jsonl_writer writes one to_json() per line, core::csv_writer flattens each result's JSON into dotted columns. Any callable (std::uint64_t index, nickname&) works.| Exception | Thrown by | Condition |
|---|---|---|
std::invalid_argument | nng::get_nickname() | Request name is empty or only spaces and no word lists loaded |
std::invalid_argument | nng::extend(path) | A file that is neither .words nor .tsv |
std::out_of_range | nng::layers(list) | No list with that name |
dasmig::core::tsv_error | load(), extend(), extend_from_string() | A malformed file or layer (message gives file, line and column); the lists are unchanged |