From 1d4feabf7e553a883c942b265ec589ce35e22817 Mon Sep 17 00:00:00 2001 From: Sara Date: Thu, 26 Mar 2026 00:32:09 +0100 Subject: [PATCH] feat: started generative grammar --- modules/generative_grammar/SCsub | 3 + modules/generative_grammar/config.py | 5 ++ modules/generative_grammar/generator.cpp | 24 ++++++ modules/generative_grammar/generator.h | 19 +++++ modules/generative_grammar/register_types.cpp | 19 +++++ modules/generative_grammar/register_types.h | 9 +++ modules/generative_grammar/symbol.cpp | 80 +++++++++++++++++++ modules/generative_grammar/symbol.h | 49 ++++++++++++ project/data/rules/test_rule.tres | 13 +++ project/scenes/levels/dungeon.tscn | 8 ++ 10 files changed, 229 insertions(+) create mode 100644 modules/generative_grammar/SCsub create mode 100644 modules/generative_grammar/config.py create mode 100644 modules/generative_grammar/generator.cpp create mode 100644 modules/generative_grammar/generator.h create mode 100644 modules/generative_grammar/register_types.cpp create mode 100644 modules/generative_grammar/register_types.h create mode 100644 modules/generative_grammar/symbol.cpp create mode 100644 modules/generative_grammar/symbol.h create mode 100644 project/data/rules/test_rule.tres create mode 100644 project/scenes/levels/dungeon.tscn diff --git a/modules/generative_grammar/SCsub b/modules/generative_grammar/SCsub new file mode 100644 index 0000000000..2760ab7d3d --- /dev/null +++ b/modules/generative_grammar/SCsub @@ -0,0 +1,3 @@ +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/generative_grammar/config.py b/modules/generative_grammar/config.py new file mode 100644 index 0000000000..58c88bf17a --- /dev/null +++ b/modules/generative_grammar/config.py @@ -0,0 +1,5 @@ +def can_build(env, platform): + return True; + +def configure(env): + pass; diff --git a/modules/generative_grammar/generator.cpp b/modules/generative_grammar/generator.cpp new file mode 100644 index 0000000000..caed0fc05f --- /dev/null +++ b/modules/generative_grammar/generator.cpp @@ -0,0 +1,24 @@ +#include "generator.h" +#include "core/config/engine.h" +#include "core/object/class_db.h" +#include "macros.h" + +void Generator::_bind_methods() { + BIND_HPROPERTY(Variant::OBJECT, state, PROPERTY_HINT_RESOURCE_TYPE, "Symbol"); +} + +void Generator::ready() { +} + +void Generator::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + default: + return; + case NOTIFICATION_READY: + ready(); + return; + } +} diff --git a/modules/generative_grammar/generator.h b/modules/generative_grammar/generator.h new file mode 100644 index 0000000000..e0bca38510 --- /dev/null +++ b/modules/generative_grammar/generator.h @@ -0,0 +1,19 @@ +#pragma once + +#include "generative_grammar/symbol.h" +#include "scene/3d/node_3d.h" + +class Generator : public Node3D { + GDCLASS(Generator, Node3D); + static void _bind_methods(); + void ready(); + +protected: + void _notification(int what); + +public: + Ref state{}; + +private: + GET_SET_FNS(Ref, state); +}; diff --git a/modules/generative_grammar/register_types.cpp b/modules/generative_grammar/register_types.cpp new file mode 100644 index 0000000000..62f15d8de6 --- /dev/null +++ b/modules/generative_grammar/register_types.cpp @@ -0,0 +1,19 @@ +#include "generative_grammar/register_types.h" +#include "core/object/class_db.h" +#include "generative_grammar/generator.h" +#include "generative_grammar/symbol.h" + +void initialize_generative_grammar_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); +} + +void uninitialize_generative_grammar_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } +} diff --git a/modules/generative_grammar/register_types.h b/modules/generative_grammar/register_types.h new file mode 100644 index 0000000000..c3de3365d9 --- /dev/null +++ b/modules/generative_grammar/register_types.h @@ -0,0 +1,9 @@ +#ifndef GENERATIVE_GRAMMAR_REGISTER_TYPES_H +#define GENERATIVE_GRAMMAR_REGISTER_TYPES_H + +#include "modules/register_module_types.h" + +void initialize_generative_grammar_module(ModuleInitializationLevel p_level); +void uninitialize_generative_grammar_module(ModuleInitializationLevel p_level); + +#endif // !GENERATIVE_GRAMMAR_REGISTER_TYPES_H diff --git a/modules/generative_grammar/symbol.cpp b/modules/generative_grammar/symbol.cpp new file mode 100644 index 0000000000..3101579e61 --- /dev/null +++ b/modules/generative_grammar/symbol.cpp @@ -0,0 +1,80 @@ +#include "generative_grammar/symbol.h" +#include "core/error/error_macros.h" +#include "core/object/class_db.h" + +void Symbol::_bind_methods() { + BIND_HPROPERTY(Variant::INT, symbol, PROPERTY_HINT_ENUM, Type_hint()); + BIND_HPROPERTY(Variant::ARRAY, children_array, PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "Symbol")); +} + +bool Symbol::is_terminal() const { + switch (this->type) { + default: + return false; + } +} + +void Symbol::set_children_array(TypedArray> array) { + for (Ref symbol : this->children) { + if (symbol.is_valid()) { + symbol->set_parent(nullptr); + } + } + this->children.clear(); + for (Variant var : array) { + Ref child{ var }; + this->children.insert(var); + if (child.is_valid()) { + child->set_parent(this); + } + } +} + +TypedArray> Symbol::get_children_array() const { + TypedArray> array{}; + for (Ref symbol : this->children) { + array.push_back(symbol); + } + return array; +} + +void Rule::_bind_methods() { + BIND_HPROPERTY(Variant::OBJECT, match, PROPERTY_HINT_RESOURCE_TYPE, "Symbol"); + BIND_HPROPERTY(Variant::OBJECT, result, PROPERTY_HINT_RESOURCE_TYPE, "Symbol"); +} + +bool Rule::symbols_match(Ref match, Ref pattern) { + if (match == pattern) { + return true; // reference match or null match + } + ERR_FAIL_COND_V_EDMSG(match.is_valid() == false, false, "Attempt to match null symbol, is not possible"); + if (pattern.is_null()) { + return false; // null matches nothing + } + if (match->get_symbol() != pattern->get_symbol()) { + return false; // symbol type mismatch + } + if (match->get_children().size() < pattern->get_children().size()) { + return false; // too little children to be able to match + } + if ((match->get_parent() == nullptr) != (pattern->get_parent() == nullptr)) { + return false; // mismatch in existence of incoming edge + } + // check for matching outgoing edges + for (Ref pattern_symbol : pattern->get_children()) { + bool match_found{ false }; + for (Ref match_symbol : match->get_children()) { + if (symbols_match(match_symbol, pattern_symbol)) { + match_found = true; + break; + } + } + if (!match_found) { + return false; + } + } + return true; +} + +bool Rule::matches(Ref graph) { +} diff --git a/modules/generative_grammar/symbol.h b/modules/generative_grammar/symbol.h new file mode 100644 index 0000000000..e9450906cc --- /dev/null +++ b/modules/generative_grammar/symbol.h @@ -0,0 +1,49 @@ +#pragma once + +#include "core/io/resource.h" +#include "core/templates/hash_set.h" +#include "core/variant/type_info.h" +#include "core/variant/typed_array.h" +#include "macros.h" + +class Symbol : public Resource { + GDCLASS(Symbol, Resource); + static void _bind_methods(); + +public: + GDENUM(Type, Start, Goal, Challenge, Reward); + bool is_terminal() const; + +private: + Type symbol{ Start }; + HashSet> children{}; + Symbol *parent{ nullptr }; + +public: + GET_SET_FNS(Type, symbol); + GET_SET_FNS(Symbol *, parent); + GET_SET_REF_FNS(HashSet>, children); + void set_children_array(TypedArray> array); + TypedArray> get_children_array() const; +}; + +MAKE_TYPE_INFO(Symbol::Type, Variant::INT); + +struct Rule : public Resource { + GDCLASS(Rule, Resource); + static void _bind_methods(); + bool symbols_match(Ref match, Ref pattern); + +public: + bool matches(Ref graph); + void apply(Ref graph); + bool try_apply(Ref graph); + +private: + Ref match{}; + Ref result{}; + +public: + GET_SET_FNS(Ref, match); + GET_SET_FNS(Ref, result); +}; diff --git a/project/data/rules/test_rule.tres b/project/data/rules/test_rule.tres new file mode 100644 index 0000000000..8f5fcf78b9 --- /dev/null +++ b/project/data/rules/test_rule.tres @@ -0,0 +1,13 @@ +[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") diff --git a/project/scenes/levels/dungeon.tscn b/project/scenes/levels/dungeon.tscn new file mode 100644 index 0000000000..269690c8c0 --- /dev/null +++ b/project/scenes/levels/dungeon.tscn @@ -0,0 +1,8 @@ +[gd_scene format=3 uid="uid://cak2tf2adjv8j"] + +[sub_resource type="Symbol" id="Symbol_bb2w7"] + +[node name="Dungeon" type="Node3D" unique_id=719313039] + +[node name="Generator" type="Generator" parent="." unique_id=1532743122] +state = SubResource("Symbol_bb2w7")