feat: start of tile grammar
This commit is contained in:
parent
d410fa377a
commit
e7e161c5b0
7 changed files with 239 additions and 37 deletions
|
|
@ -4,7 +4,7 @@
|
|||
#include "macros.h"
|
||||
|
||||
void Generator::_bind_methods() {
|
||||
BIND_HPROPERTY(Variant::OBJECT, state, PROPERTY_HINT_RESOURCE_TYPE, "Symbol");
|
||||
BIND_HPROPERTY(Variant::OBJECT, state, PROPERTY_HINT_RESOURCE_TYPE, "Sentence");
|
||||
}
|
||||
|
||||
void Generator::ready() {
|
||||
|
|
@ -19,6 +19,13 @@ void Generator::_notification(int what) {
|
|||
return;
|
||||
case NOTIFICATION_READY:
|
||||
ready();
|
||||
case NOTIFICATION_CHILD_ORDER_CHANGED:
|
||||
for (Variant child : get_children()) {
|
||||
if (Rule * rule{ cast_to<Rule>(child) }) {
|
||||
this->rule = rule;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "generative_grammar/symbol.h"
|
||||
#include "generative_grammar/grammar.h"
|
||||
#include "scene/3d/node_3d.h"
|
||||
|
||||
class Generator : public Node3D {
|
||||
|
|
@ -12,8 +12,9 @@ protected:
|
|||
void _notification(int what);
|
||||
|
||||
public:
|
||||
Ref<Symbol> state{};
|
||||
Ref<Sentence> state{};
|
||||
Rule *rule{ nullptr };
|
||||
|
||||
private:
|
||||
GET_SET_FNS(Ref<Symbol>, state);
|
||||
GET_SET_FNS(Ref<Sentence>, state);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,25 +1,156 @@
|
|||
#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_PROPERTY(Variant::STRING, symbols_string);
|
||||
BIND_PROPERTY(Variant::INT, width);
|
||||
BIND_HPROPERTY(Variant::STRING, symbols_string, PROPERTY_HINT_MULTILINE_TEXT);
|
||||
BIND_PROPERTY(Variant::VECTOR2I, size);
|
||||
}
|
||||
|
||||
bool Sentence::is_terminal() const {
|
||||
for (char32_t c : this->symbols) {
|
||||
if (String::char_uppercase(c)) {
|
||||
for (Symbol c : this->symbols) {
|
||||
if (!symbol_is_terminal(c)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; // no non-terminals in sentence
|
||||
}
|
||||
|
||||
void Rule::_bind_methods() {
|
||||
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 Rule::try_apply(Ref<Sentence> sentence) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "core/io/resource.h"
|
||||
#include "macros.h"
|
||||
#include "scene/main/node.h"
|
||||
|
||||
typedef char32_t Symbol;
|
||||
|
||||
namespace NonTerminals {
|
||||
enum : Symbol {
|
||||
Start = 'S',
|
||||
Blocked = 'B',
|
||||
Invalid = '!',
|
||||
};
|
||||
};
|
||||
|
||||
namespace Terminals {
|
||||
enum : Symbol {
|
||||
Invalid = '!',
|
||||
};
|
||||
};
|
||||
|
||||
class Sentence : public Resource {
|
||||
GDCLASS(Sentence, Resource);
|
||||
|
|
@ -9,24 +25,54 @@ class Sentence : public Resource {
|
|||
|
||||
public:
|
||||
bool is_terminal() const;
|
||||
Symbol get_at(Vector2i coord) const;
|
||||
void set_at(Vector2i coord, Symbol symbol);
|
||||
bool check_match_at(Vector2i at, Ref<Sentence> pattern);
|
||||
void write_subsentence(Vector2i at, Ref<Sentence> sentence);
|
||||
static bool symbol_is_terminal(Symbol symbol) {
|
||||
return String::char_lowercase(symbol) == symbol;
|
||||
}
|
||||
|
||||
private:
|
||||
Vector<char32_t> symbols{};
|
||||
int width{ 0 };
|
||||
public:
|
||||
Vector<Symbol> symbols{};
|
||||
Vector2i size{ 0, 0 };
|
||||
|
||||
public:
|
||||
void set_symbols_string(String value);
|
||||
String get_symbols_string() const;
|
||||
void set_width(int width);
|
||||
int get_width() const;
|
||||
void set_size(Vector2i width);
|
||||
Vector2i get_size() const;
|
||||
};
|
||||
|
||||
struct Rule : public Resource {
|
||||
GDCLASS(Rule, Resource);
|
||||
class Rule : public Node {
|
||||
GDCLASS(Rule, Node);
|
||||
static void _bind_methods() {}
|
||||
|
||||
public:
|
||||
virtual void try_apply(Ref<Sentence> graph) {}
|
||||
};
|
||||
|
||||
class CompositeRule : public Rule {
|
||||
GDCLASS(CompositeRule, Rule);
|
||||
static void _bind_methods() {}
|
||||
void order_changed();
|
||||
|
||||
protected:
|
||||
void _notification(int what);
|
||||
|
||||
public:
|
||||
void try_apply(Ref<Sentence> graph) override;
|
||||
|
||||
protected:
|
||||
Vector<Rule *> rules{};
|
||||
};
|
||||
|
||||
class ReplaceRule : public Rule {
|
||||
GDCLASS(ReplaceRule, Rule);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void try_apply(Ref<Sentence> graph);
|
||||
void try_apply(Ref<Sentence> graph) override;
|
||||
|
||||
private:
|
||||
Ref<Sentence> pattern{};
|
||||
|
|
@ -36,3 +82,23 @@ public:
|
|||
GET_SET_FNS(Ref<Sentence>, pattern);
|
||||
GET_SET_FNS(Ref<Sentence>, result);
|
||||
};
|
||||
|
||||
class ResizeRule : public CompositeRule {
|
||||
GDCLASS(ResizeRule, Rule);
|
||||
static void _bind_methods();
|
||||
void fill_area(Ref<Sentence> data, Vector2i coord, Symbol tile);
|
||||
|
||||
public:
|
||||
void try_apply(Ref<Sentence> sentence) override;
|
||||
|
||||
private:
|
||||
int factor{ 10 };
|
||||
|
||||
public:
|
||||
GET_SET_FNS(int, factor);
|
||||
};
|
||||
|
||||
class PickRandomRule : public CompositeRule {
|
||||
GDCLASS(PickRandomRule, CompositeRule);
|
||||
static void _bind_methods() {}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
#include "generative_grammar/register_types.h"
|
||||
#include "core/object/class_db.h"
|
||||
#include "generative_grammar/generator.h"
|
||||
#include "generative_grammar/symbol.h"
|
||||
#include "generative_grammar/grammar.h"
|
||||
|
||||
void initialize_generative_grammar_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
|
||||
return;
|
||||
}
|
||||
ClassDB::register_class<Symbol>();
|
||||
ClassDB::register_class<Rule>();
|
||||
ClassDB::register_abstract_class<Rule>();
|
||||
ClassDB::register_class<Sentence>();
|
||||
ClassDB::register_class<CompositeRule>();
|
||||
ClassDB::register_class<ReplaceRule>();
|
||||
ClassDB::register_class<Generator>();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
[gd_resource type="Rule" format=3 uid="uid://cb5x75r5umlh5"]
|
||||
|
||||
[sub_resource type="Symbol" id="Symbol_26ujg"]
|
||||
|
||||
[sub_resource type="Symbol" id="Symbol_titn7"]
|
||||
type = 1
|
||||
|
||||
[sub_resource type="Symbol" id="Symbol_kwj57"]
|
||||
children_array = Array[Object]([SubResource("Symbol_titn7")])
|
||||
|
||||
[resource]
|
||||
match = SubResource("Symbol_26ujg")
|
||||
result = SubResource("Symbol_kwj57")
|
||||
|
|
@ -1,8 +1,16 @@
|
|||
[gd_scene format=3 uid="uid://cak2tf2adjv8j"]
|
||||
|
||||
[sub_resource type="Symbol" id="Symbol_bb2w7"]
|
||||
[sub_resource type="Sentence" id="Sentence_bb2w7"]
|
||||
symbols_string = "BBBBB
|
||||
BBBBB
|
||||
BBBBB
|
||||
BBBBB
|
||||
BBBBB"
|
||||
size = Vector2i(5, 5)
|
||||
|
||||
[node name="Dungeon" type="Node3D" unique_id=719313039]
|
||||
|
||||
[node name="Generator" type="Generator" parent="." unique_id=1532743122]
|
||||
state = SubResource("Symbol_bb2w7")
|
||||
state = SubResource("Sentence_bb2w7")
|
||||
|
||||
[node name="ReplaceRule" type="ReplaceRule" parent="Generator" unique_id=1787373751]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue