Files
2018-07-17 14:15:37 +02:00

208 lines
4.0 KiB
C++

#include "name_generator.h"
#include "compat/misc.h"
#include "str_util.h"
#include "util/random.h"
#include <fstream>
#ifndef NAMEGEN_MUTATE_END_PCT
#define NAMEGEN_MUTATE_END_PCT 0.1
#endif
NameGenerator::NameGenerator() {
clear();
}
void NameGenerator::clear() {
data.clear();
names.clear();
nameStarts.clear();
usedNames.clear();
preventDuplicates = false;
useGeneration = true;
mutationChance = 0;
}
void NameGenerator::read(const std::string& filename) {
std::ifstream file(filename);
skipBOM(file);
if(file.is_open()) {
while(true) {
std::string line;
std::getline(file, line);
if(file.fail())
break;
line = trim(line);
if(line.size() > 0)
addName(line);
}
}
}
void NameGenerator::write(const std::string& filename) {
std::ofstream file(filename);
foreach(it, names)
file << *it << "\n";
}
static int randomchar(bool start = false) {
if(start) {
return randomi((int)'A', (int)'Z');
}
else {
if(randomf() < NAMEGEN_MUTATE_END_PCT)
return 0;
return randomi((int)'a', (int)'z');
}
}
std::string NameGenerator::generate() {
if(names.size() == 0)
return "Error";
if(!useGeneration)
return names[randomi(0, names.size() - 1)];
std::string name;
while(true) {
std::pair<int, int> syl;
name = "";
//Add the start of a name
if(mutationChance != 0 && randomf() < mutationChance) {
syl.first = randomchar(true);
syl.second = randomchar();
u8append(name, syl.first);
u8append(name, syl.second);
}
else {
int chance = randomi(0, names.size() - 1);
int cum = 0;
foreach(it, nameStarts) {
cum += it->second;
if(chance < cum) {
syl.first = it->first.first;
syl.second = it->first.second;
u8append(name, syl.first);
u8append(name, syl.second);
break;
}
}
}
//Continue on
int next = 0;
do {
auto it = data.find(syl);
if(it == data.end())
break;
ProbData& pd = it->second;
if(mutationChance != 0 && randomf() < mutationChance) {
next = randomchar();
syl.first = syl.second;
syl.second = next;
if(next)
u8append(name, next);
}
else {
int chance = randomi(0, pd.occurances - 1);
int cum = 0;
foreach(it, pd.nextCount) {
cum += it->second;
if(chance < cum) {
next = it->first;
syl.first = syl.second;
syl.second = next;
if(next)
u8append(name, next);
break;
}
}
}
} while(next);
//Do duplicate prevention
if(!preventDuplicates)
return name;
if(usedNames.find(name) == usedNames.end()) {
usedNames.insert(name);
return name;
}
}
}
bool NameGenerator::hasName(const std::string& name) {
foreach(it, names)
if(*it == name)
return true;
return false;
}
unsigned NameGenerator::getNameCount() {
return names.size();
}
void NameGenerator::addName(const std::string& name) {
u8it it(name);
//Add start of word
std::pair<int,int> syl;
syl.first = it++;
if(!syl.first)
return;
syl.second = it++;
if(!syl.second)
return;
int next = it++;
if(!next)
return;
names.push_back(name);
auto f = nameStarts.find(syl);
if(f == nameStarts.end())
nameStarts[syl] = 1;
else
nameStarts[syl]++;
//Add association for lowercase
if(syl.first >= 'A' && syl.first <= 'Z')
addAssociation(syl.first-'A'+'a', syl.second, next);
//Add all the syllables
while(next) {
addAssociation(syl.first, syl.second, next);
syl.first = syl.second;
syl.second = next;
next = it++;
}
addAssociation(syl.first, syl.second, next);
}
void NameGenerator::addAssociation(int first, int second, int next) {
std::pair<int,int> syl;
syl.first = first;
syl.second = second;
ProbData* pd = 0;
auto it = data.find(syl);
if(it == data.end()) {
ProbData newData;
newData.occurances = 0;
data.insert(std::pair<std::pair<int,int>,ProbData>(syl, newData));
pd = &data[syl];
}
else {
pd = &it->second;
}
pd->occurances += 1;
auto f = pd->nextCount.find(next);
if(f == pd->nextCount.end())
pd->nextCount[next] = 1;
else
pd->nextCount[next]++;
}