1 module unexpected.rando.names;
2 
3 import reversineer;
4 import std.random : Random, uniform;
5 
6 import unexpected.rando.common;
7 
8 static immutable string[string] genTable;
9 static immutable string[string] genTableStarts;
10 
11 enum minNameLength = 3;
12 
13 shared static this() {
14 	import std.uni : isUpper;
15 	string[string] tableUpper;
16 	string[string] tableLower;
17 	genTableStarts = createTable!true(import("nametable.txt"));
18 	genTable = createTable!false(import("nametable.txt"));
19 	import std.stdio;
20 }
21 
22 string[string] createTable(bool upper)(string data) @safe {
23 	import std.algorithm.iteration : splitter;
24 	import std.array : front;
25 	import std.string : lineSplitter;
26 	import std.uni : isUpper, toLower;
27 	string[string] result;
28 	foreach (line; data.lineSplitter()) {
29 		auto split = line.splitter(" ");
30 		string key = split.front;
31 		if (upper) {
32 			if (!key.front.isUpper) {
33 				continue;
34 			}
35 		} else {
36 			key = key.toLower();
37 		}
38 		split.popFront();
39 		result[key] = split.front;
40 	}
41 	return result;
42 }
43 
44 string generateName(size_t length, uint seed) @safe
45 	in(length >= minNameLength, "Length must be at least "~('0'+minNameLength))
46 {
47 	import std.random : Random, uniform;
48 	import std.uni : toLower;
49 	string pickRandomKey(immutable string[string] assoc, ref Random rnd) @trusted {
50 		return assoc.keys[uniform(0, assoc.length, rnd)];
51 	}
52 
53 	auto rand = Random(seed);
54 	string result = pickRandomKey(genTableStarts, rand);
55 	result.reserve(length);
56 	while (result.length < length) {
57 		auto lastChars = result[$-3 .. $].toLower();
58 		if (lastChars !in genTable) {
59 			if (result.length < length - 4) {
60 				result ~= ' '~pickRandomKey(genTableStarts, rand);
61 			} else {
62 				return result;
63 			}
64 		} else {
65 			result ~= genTable[lastChars][uniform(0, genTable[lastChars].length, rand)];
66 		}
67 	}
68 	return result;
69 }
70 
71 
72 void randomizeNames(Name CTOptions, T)(ref T field, ref Random rng, ref uint seed, const Options options) {
73 	seed =  rng.uniform!uint;
74 	field = generateName(field.length, seed);
75 }