behaviour-tree-test/modules/generative_grammar/grammar.cpp
2026-03-29 17:28:28 +02:00

156 lines
4.3 KiB
C++

#include "generative_grammar/grammar.h"
#include "core/object/class_db.h"
#include "core/variant/typed_array.h"
#include "macros.h"
void Sentence::_bind_methods() {
BIND_HPROPERTY(Variant::STRING, symbols_string, PROPERTY_HINT_MULTILINE_TEXT);
BIND_PROPERTY(Variant::VECTOR2I, size);
}
bool Sentence::is_terminal() const {
for (Symbol c : this->symbols) {
if (!symbol_is_terminal(c)) {
return false;
}
}
return true; // no non-terminals in sentence
}
Symbol Sentence::get_at(Vector2i coord) const {
int idx{ coord.x + (coord.y * this->size.x) };
ERR_FAIL_COND_V_EDMSG(idx < this->symbols.size(), NonTerminals::Invalid, "Sentence::get_at: invalid coordinate");
return this->symbols.get(idx);
}
void Sentence::set_at(Vector2i coord, Symbol symbol) {
int idx{ coord.x + (coord.y * this->size.x) };
ERR_FAIL_COND_EDMSG(idx < this->symbols.size(), "Sentence::set_at: invalid coordinate");
this->symbols.set(idx, symbol);
}
bool Sentence::check_match_at(Vector2i at, Ref<Sentence> pattern) {
Vector2i const pattern_size{ pattern->get_size() };
if (at.x + pattern_size.x > this->size.x || at.y + pattern_size.y > this->size.y) {
return false; // can't match, out of bounds
}
for (Vector2i itr{ 0, 0 }; itr.y < pattern_size.x; ++itr.y) {
for (; itr.x + at.x < pattern_size.x; ++itr.x) {
if (get_at(at + itr) != pattern->get_at(itr)) {
return false; // difference found
}
}
itr.x = at.x;
}
return true;
}
void Sentence::write_subsentence(Vector2i at, Ref<Sentence> sentence) {
for (Vector2i pos{ 0, 0 }; pos.y < sentence->get_size().y; ++pos.y) {
for (; pos.x < sentence->get_size().x; ++pos.x) {
set_at(at + pos, sentence->get_at(pos));
}
pos.x = 0;
}
}
void Sentence::set_symbols_string(String value) {
int size{ this->size.x * this->size.y };
int writer{ 0 };
for (int reader{ 0 }; writer < size; ++reader) {
char32_t c{ value.length() >= reader ? value[reader] : (char32_t)'B' };
if (c != '\n') {
this->symbols.set(writer, c);
++writer;
}
}
}
String Sentence::get_symbols_string() const {
String value{};
for (int i{ 0 }; i < this->symbols.size(); ++i) {
if (i != 0 && i % this->size.x == 0) {
value += L'\n';
}
value += this->symbols.get(i);
}
return value;
}
void Sentence::set_size(Vector2i value) {
this->size = value;
this->symbols.clear();
this->symbols.resize_initialized(this->size.x * this->size.y);
for (int i{ 0 }; i < this->symbols.size(); ++i) {
this->symbols.set(i, 'B');
}
}
Vector2i Sentence::get_size() const {
return this->size;
}
void CompositeRule::_notification(int what) {
switch (what) {
case NOTIFICATION_READY:
case NOTIFICATION_CHILD_ORDER_CHANGED:
this->rules.clear();
for (Variant child : get_children()) {
if (Rule * rule{ cast_to<Rule>(child) }) {
this->rules.push_back(rule);
}
}
return;
}
}
void CompositeRule::try_apply(Ref<Sentence> sentence) {
for (Rule *rule : this->rules) {
rule->try_apply(sentence);
}
}
void ReplaceRule::_bind_methods() {
BIND_HPROPERTY(Variant::OBJECT, pattern, PROPERTY_HINT_RESOURCE_TYPE, Sentence::get_class_static());
BIND_HPROPERTY(Variant::OBJECT, result, PROPERTY_HINT_RESOURCE_TYPE, Sentence::get_class_static());
}
void ReplaceRule::try_apply(Ref<Sentence> sentence) {
Vector2i max{ sentence->get_size() - this->pattern->get_size() };
if (max.x < 0 || max.y < 0) {
return; // cannot continue, no area in sentence big enough
}
for (Vector2i pos{ 0, 0 }; pos.y < max.y; ++pos.y) {
for (; pos.x < max.x; ++pos.x) {
if (sentence->check_match_at(pos, this->pattern)) {
sentence->write_subsentence(pos, this->result);
}
}
pos.x = 0;
}
}
void ResizeRule::_bind_methods() {
BIND_PROPERTY(Variant::INT, factor);
}
void ResizeRule::fill_area(Ref<Sentence> data, Vector2i coord, Symbol tile) {
for (Vector2i at{ 0, 0 }; at.y < this->factor; ++at.y) {
for (; at.x < this->factor; ++at.x) {
data->set_at(coord * this->factor + at, tile);
}
at.x = 0;
}
}
void ResizeRule::try_apply(Ref<Sentence> sentence) {
Vector2i size_before{ sentence->size };
Vector<Symbol> before{ sentence->symbols };
sentence->set_size(size_before * this->factor);
for (Vector2i at{ 0, 0 }; at.y < size_before.y; ++at.y) {
for (; at.x < size_before.x; ++at.x) {
fill_area(sentence, at, before.get(at.x + at.y * size_before.x));
}
at.x = 0;
}
}