Added a new way to generate unit names, using rules of a context-free grammar.
This commit is contained in:
parent
83dd965f9e
commit
f84e8f4795
7 changed files with 371 additions and 3 deletions
File diff suppressed because one or more lines are too long
|
@ -980,6 +980,7 @@ set(wesnoth-main_SRC
|
|||
units/map.cpp
|
||||
units/types.cpp
|
||||
utils/sha1.cpp
|
||||
utils/context_free_grammar_generator.cpp
|
||||
variable.cpp
|
||||
variable_info.cpp
|
||||
whiteboard/action.cpp
|
||||
|
|
|
@ -555,6 +555,7 @@ wesnoth_sources = Split("""
|
|||
units/udisplay.cpp
|
||||
units/unit.cpp
|
||||
utils/sha1.cpp
|
||||
utils/context_free_grammar_generator.cpp
|
||||
variable_info.cpp
|
||||
variable.cpp
|
||||
whiteboard/action.cpp
|
||||
|
|
18
src/race.cpp
18
src/race.cpp
|
@ -191,6 +191,21 @@ unit_race::unit_race(const config& cfg) :
|
|||
name_[FEMALE] = (cfg["name"]);
|
||||
}
|
||||
|
||||
if (cfg.has_attribute("male_name_generator")) {
|
||||
name_generator_[MALE].constructFromString(cfg["male_name_generator"]);
|
||||
}
|
||||
if (cfg.has_attribute("female_name_generator")) {
|
||||
name_generator_[FEMALE].constructFromString(cfg["female_name_generator"]);
|
||||
}
|
||||
if (cfg.has_attribute("name_generator")) {
|
||||
if (!name_generator_[MALE].is_initialized()) {
|
||||
name_generator_[MALE].constructFromString(cfg["name_generator"]);
|
||||
}
|
||||
if (!name_generator_[FEMALE].is_initialized()) {
|
||||
name_generator_[FEMALE].constructFromString(cfg["name_generator"]);
|
||||
}
|
||||
}
|
||||
|
||||
if(chain_size_ <= 0)
|
||||
chain_size_ = 2;
|
||||
|
||||
|
@ -202,6 +217,9 @@ unit_race::unit_race(const config& cfg) :
|
|||
std::string unit_race::generate_name(
|
||||
unit_race::GENDER gender) const
|
||||
{
|
||||
if (name_generator_[gender].is_initialized()) {
|
||||
return name_generator_[gender].generate();
|
||||
}
|
||||
return unicode_cast<utf8::string>(
|
||||
markov_generate_name(next_[gender], chain_size_, 12));
|
||||
}
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
#include "config.hpp"
|
||||
#include "serialization/unicode_types.hpp"
|
||||
|
||||
|
||||
#include "utils/context_free_grammar_generator.hpp"
|
||||
|
||||
typedef std::map<ucs4::string, ucs4::string > markov_prefix_map;
|
||||
|
||||
|
@ -63,6 +62,7 @@ private:
|
|||
unsigned int ntraits_;
|
||||
markov_prefix_map next_[NUM_GENDERS];
|
||||
int chain_size_;
|
||||
context_free_grammar_generator name_generator_[NUM_GENDERS];
|
||||
|
||||
config::const_child_itors traits_;
|
||||
config::const_child_itors topics_;
|
||||
|
|
123
src/utils/context_free_grammar_generator.cpp
Normal file
123
src/utils/context_free_grammar_generator.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
Copyright (C) 2016 by Ján Dugáček
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Algorithm to generate names using a context-free grammar, which allows more control
|
||||
* than the usual Markov chain generator
|
||||
*/
|
||||
|
||||
#include "context_free_grammar_generator.hpp"
|
||||
#include "../log.hpp"
|
||||
#include "../random_new.hpp"
|
||||
|
||||
context_free_grammar_generator::context_free_grammar_generator() :
|
||||
initialized_(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
context_free_grammar_generator::~context_free_grammar_generator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool context_free_grammar_generator::constructFromString(const std::string &source) {
|
||||
const char* reading = source.c_str();
|
||||
nonterminal* current = nullptr;
|
||||
std::vector<std::string>* filled = nullptr;
|
||||
std::string buf;
|
||||
|
||||
while (*reading != 0) {
|
||||
if (*reading == '=') {
|
||||
current = &nonterminals_[buf];
|
||||
current->possibilities_.push_back(std::vector<std::string>());
|
||||
filled = ¤t->possibilities_.back();
|
||||
buf.clear();
|
||||
} else if (*reading == '\n') {
|
||||
if (filled) filled->push_back(buf);
|
||||
filled = nullptr;
|
||||
current = nullptr;
|
||||
buf.clear();
|
||||
} else if (*reading == '|') {
|
||||
if (!filled || !current) {
|
||||
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced | symbol";
|
||||
return false;
|
||||
}
|
||||
filled->push_back(buf);
|
||||
current->possibilities_.push_back(std::vector<std::string>());
|
||||
filled = ¤t->possibilities_.back();
|
||||
buf.clear();
|
||||
} else if (*reading == '\\' && reading[1] == 'n') {
|
||||
reading++;
|
||||
buf.push_back('\n');
|
||||
} else if (*reading == '\\' && reading[1] == 't') {
|
||||
reading++;
|
||||
buf.push_back('\t');
|
||||
} else {
|
||||
if (*reading == '{') {
|
||||
if (!filled) {
|
||||
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced { symbol";
|
||||
return false;
|
||||
}
|
||||
filled->push_back(buf);
|
||||
buf.clear();
|
||||
}
|
||||
if (*reading == '}') {
|
||||
if (!filled) {
|
||||
lg::wml_error() << "[context_free_grammar_generator] Parsing error: misplaced } symbol";
|
||||
return false;
|
||||
}
|
||||
filled->push_back(buf);
|
||||
buf.clear();
|
||||
} else buf.push_back(*reading);
|
||||
}
|
||||
reading++;
|
||||
}
|
||||
if (filled) filled->push_back(buf);
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string context_free_grammar_generator::print_nonterminal(const std::string& name, uint32_t* seed, short seed_pos) const {
|
||||
std::string result;
|
||||
std::map<std::string, nonterminal>::const_iterator found = nonterminals_.find(name);
|
||||
if (found == nonterminals_.end()) {
|
||||
lg::wml_error() << "[context_free_grammar_generator] Warning: needed nonterminal " << name << " not defined";
|
||||
return "!" + name;
|
||||
}
|
||||
const context_free_grammar_generator::nonterminal& got = found->second;
|
||||
unsigned int picked = seed[seed_pos++] % got.possibilities_.size();
|
||||
if (seed_pos >= seed_size) seed_pos = 0;
|
||||
if (picked == got.last_) {
|
||||
picked = seed[seed_pos++] % got.possibilities_.size();
|
||||
if (seed_pos >= seed_size) seed_pos = 0;
|
||||
}
|
||||
const_cast<unsigned int&>(got.last_) = picked; /* The variable last_ can change, the rest must stay const */
|
||||
const std::vector<std::string>& used = got.possibilities_[picked];
|
||||
for (unsigned int i = 0; i < used.size(); i++) {
|
||||
if (used[i][0] == '{') result += print_nonterminal(used[i].substr(1), seed, seed_pos);
|
||||
else result += used[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string context_free_grammar_generator::generate() const {
|
||||
uint32_t seed[seed_size];
|
||||
for (unsigned short int i = 0; i < seed_size; i++) {
|
||||
seed[i] = random_new::generator->next_random();
|
||||
}
|
||||
return print_nonterminal("main", seed, 0);
|
||||
}
|
61
src/utils/context_free_grammar_generator.hpp
Normal file
61
src/utils/context_free_grammar_generator.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright (C) 2016 by Ján Dugáček
|
||||
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef CONTEXT_FREE_GRAMMAR_GENERATOR_INCLUDED
|
||||
#define CONTEXT_FREE_GRAMMAR_GENERATOR_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
class context_free_grammar_generator
|
||||
{
|
||||
private:
|
||||
|
||||
struct nonterminal {
|
||||
nonterminal() : last_(1) {}
|
||||
std::vector<std::vector<std::string> > possibilities_;
|
||||
unsigned int last_;
|
||||
};
|
||||
|
||||
std::map<std::string, nonterminal> nonterminals_;
|
||||
bool initialized_;
|
||||
std::string print_nonterminal(const std::string& name, uint32_t* seed, short int seed_pos) const;
|
||||
static const short unsigned int seed_size = 20;
|
||||
|
||||
public:
|
||||
/** Default constructor */
|
||||
context_free_grammar_generator();
|
||||
|
||||
/** Initialisation
|
||||
* @param source the definition of the context-free grammar to use
|
||||
* @returns if the operation was successful
|
||||
*/
|
||||
bool constructFromString(const std::string& source);
|
||||
|
||||
/** Generates a possible word in the grammar set before
|
||||
* @returns the word
|
||||
*/
|
||||
std::string generate() const;
|
||||
|
||||
~context_free_grammar_generator();
|
||||
|
||||
/** Checks if the object is initialized
|
||||
* @returns if it is initialized
|
||||
*/
|
||||
bool is_initialized() const {return initialized_; }
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue