Compare commits

..

9 Commits

Author SHA1 Message Date
Maddie Busig
626486cadf Implement add_weighted_group 2025-04-29 06:14:14 -07:00
Maddie Busig
404faec921 Fix missing length check in add_group_characteristic 2025-04-29 06:13:52 -07:00
Maddie Busig
2d1b318d0f Add valid_group_characteristic 2025-04-29 06:13:36 -07:00
Maddie Busig
a67fcac5fc Implement add_group_characteristic 2025-04-29 06:05:20 -07:00
Maddie Busig
8e6413e9ec Move *_full to fractal.hpp and add valid token check 2025-04-29 06:04:55 -07:00
Maddie Busig
0f3e857586 Implement ruleset_t::add_token 2025-04-29 05:55:13 -07:00
Maddie Busig
f1b6870dfd Change ruleset constants to globals instead of templates 2025-04-29 05:50:11 -07:00
Maddie Busig
ee6daf36d7 Comment ruleset functions 2025-04-29 05:31:06 -07:00
Maddie Busig
00303ae3be Refactor fractal generator and fractal classes, begin rewrite
Separating fractal generation run from ruleset generation. After the
ruleset is processed, there is no reason for the group characteristics,
etc to still be used. Additionally, this will avoid the possibility of
the user attempting to use the fractal before ruleset is created and
processed.

Other changes:
Branch rules now use unioned weighted or unweighted group.
Rewrite ruleset creation usage
2025-04-29 04:59:38 -07:00
3 changed files with 260 additions and 79 deletions

View File

@ -9,29 +9,34 @@
namespace fractal {
constexpr size_t g_max_tokens = 16;
constexpr size_t g_max_groups = 8;
constexpr size_t g_max_wgroups = 4;
constexpr size_t g_max_branch_rules = 8;
constexpr size_t g_max_mark_rules = 4;
constexpr size_t g_max_cgroup_tokens = 16;
constexpr size_t g_max_wgroup_weights = 8;
using token_id_t = uint32_t;
using group_id_t = uint32_t;
using weighted_group_id_t = uint32_t;
using branch_rule_basic_id_t = uint32_t;
using branch_rule_marking_id_t = uint32_t;
using branch_rule_id_t = uint32_t;
using mark_rule_id_t = uint32_t;
enum class token_type_e {
none,
empty,
walk,
rotate,
generate,
};
struct token_t {
token_type_e m_token = token_type_e::none;
token_type_e m_type = token_type_e::empty;
mtl::fixed m_value;
};
struct group_characteristic_t {
static constexpr size_t g_max_group_size = 32;
uint32_t m_factor; // Number of groups generated in succession
etl::vector<token_id_t, g_max_group_size> m_token_ids;
etl::vector<token_id_t, g_max_cgroup_tokens> m_token_ids;
};
struct marker_t {
@ -60,23 +65,174 @@ struct group_t {
};
struct weighted_group_t {
static constexpr size_t g_max_group_weights = 8;
etl::vector<group_id_t, g_max_group_weights> m_groups;
etl::vector<uint32_t, g_max_group_weights> m_weights;
uint32_t m_weight_total;
etl::vector<group_id_t, g_max_wgroup_weights> m_groups;
etl::vector<uint32_t, g_max_wgroup_weights> m_weights;
uint32_t m_weight_total; // Total needed for random selection
// TODO: Control weight additions so m_weight_total = sum(m_weights) is invariant
};
struct branch_rule_basic_t {
struct branch_rule_t {
token_id_t m_match;
weighted_group_id_t m_weighted_group;
bool m_weighted;
union {
group_id_t basic;
weighted_group_id_t weighted;
} m_group;
};
struct branch_rule_marking_t {
struct mark_rule_t {
token_id_t m_match;
uint32_t m_marker_id;
uint32_t m_tag;
};
class ruleset_t {
private:
etl::vector<token_t, g_max_tokens> m_tokens;
etl::vector<group_characteristic_t, g_max_groups> m_group_characteristics;
etl::vector<weighted_group_t, g_max_wgroups> m_weighted_groups;
etl::vector<branch_rule_t, g_max_branch_rules> m_branch_rules;
etl::vector<mark_rule_t, g_max_mark_rules> m_mark_rules;
public:
/**
* @brief Checks if the maximum number of tokens has been reached
*
* @ret @c true if the number of tokens in the ruleset equals @c g_max_tokens, @c false otherwise
*/
bool tokens_full() const {
return m_tokens.full();
}
bool valid_token(token_id_t tok) const {
return tok < m_tokens.size();
}
/**
* @brief Add new token variable that can be used in this ruleset
*
* @param type Type of the token to add
* @param value Value of the token, unused if @p type is @c token_type_e::empty
*
* @ret @c token_id_t of the newly added token, local to this ruleset
*
* @exception @c mtl::length_error if the maximum number of tokens is reached
*/
token_id_t add_token(token_type_e type, mtl::fixed value = 0);
/**
* @brief Checks if the maximum number of group characteristics has been reached
*
* @ret @c true if the number of group characteristics in the ruleset equals @c g_max_cgroups, @c false otherwise
*/
bool group_characteristics_full() const {
return m_group_characteristics.full();
}
bool valid_group_characteristic(group_id_t group) const {
return group < m_group_characteristics.size();
}
/**
* @brief Add new group characteristic
*
* @param tokens @c etl::ivector of @c token_id_t that should be generated inside this group
* @param factor The number of times the group's tokens are repeatedly generated
*
* @ret @c group_id_t of the newly added group characteristic, local to this ruleset
*
* @exception mtl::invalid_argument if an invalid token was encountered
* @exception mtl::length_error if the maximum number of groups is reached
*/
group_id_t add_group_characteristic(const etl::ivector<token_id_t>& tokens, uint32_t factor = 1);
/**
* @brief Checks if the maximum number of weighted groups has been reached
*
* @ret @c true if the number of weighted groups in the ruleset equals @c g_max_wgroups, @c false otherwise
*/
bool weighted_groups_full() const {
return m_weighted_groups.full();
}
/**
* @brief Add new weighted group
*
* Adds weighted group of groups, where each subgroup has a
* <tt>(weight) / (total weight)</tt> chance of being selected. Weights
* are represented as @c uint32_t.
*
* @param weights @c etl::ivector of @c etl::pair of @c group_id_t and their corresponding weights
*
* @ret @c weighted_group_id_t of the newly created group
*
* @exception @c mtl::invalid_argument if no groups were supplied, or an invalid group was encountered
* @exception @c mtl::length_error if the maximum number of groups is reached
*/
weighted_group_id_t add_weighted_group(const etl::ivector<etl::pair<group_id_t, uint32_t>>& weights);
/**
* @brief Checks if the maximum number of branch rules has been reached
*
* @ret @c true if the number of branch rules in the ruleset equals @c g_max_branch_rules, @c false otherwise
*/
bool branch_rules_full() const;
/**
* @brief Add a new unweighted branch rule to the ruleset
*
* Branch rules describe what groups can be generated given a token.
* Branch rules can use either plain groups or weighted groups as their output.
*
* @param match Token to match and generate the group in place of
* @param group Group to generate
*
* @ret @c branch_rule_id_t of the newly added branch rule
*
* @exception @c mtl::invalid_argument if an invalid token or group was supplied
* @exception @c mtl::length_error if the maximum number of branch rules was reached
*/
branch_rule_id_t add_branch_rule(token_id_t match, group_id_t group);
/**
* @brief Add a new weighted branch rule to the ruleset
*
* Branch rules describe what groups can be generated given a token.
* Branch rules can use either plain groups or weighted groups as their output.
*
* @param match Token to match and generate the group in place of
* @param wgroup Weighted group to select from, only one subgroup is generated
*
* @ret @c branch_rule_id_t of the newly added branch rule
*
* @exception @c mtl::invalid_argument if an invalid token or group was supplied
* @exception @c mtl::length_error if the maximum number of branch rules was reached
*/
branch_rule_id_t add_branch_rule_weighted(token_id_t match, weighted_group_id_t wgroup);
/**
* @brief Checks if the maximum number of mark rules has been reached
*
* @ret @c true if the number of mark rules in the ruleset equals @c g_max_mark_rules, @c false otherwise
*/
bool mark_rules_full() const;
/** @brief Add a new marking rule to the ruleset
*
* Mark rules describe what tokens should have their positions output
* each generation. If a token has a match, its position and a tag is output.
*
* @param match Token to match and mark
* @param tag Marking identifier, does not need to be unique
*
* @ret @c mark_rule_id_t of the newly added mark rule
*
* @exception @c mtl::invalid_argument if an invalid token was supplied
* @exception @c mtl::length_error if the maximum number of mark rules was reached
*/
mark_rule_id_t add_mark_rule(token_id_t match, uint32_t tag);
};
#if 0
class generator_t {
private:
static constexpr size_t g_max_groups = 32;
@ -291,6 +447,7 @@ public:
*/
bool step_generation(const etl::ivector<marker_t>& out_markers) noexcept;
};
#endif
} // namespace fractal

View File

@ -8,6 +8,65 @@ namespace log = mtl::log;
namespace fractal {
token_id_t ruleset_t::add_token(token_type_e type, mtl::fixed value) {
if (m_tokens.full()) {
throw mtl::length_error();
}
token_t tok { .m_type = type, .m_value = value };
m_tokens.push_back(tok);
return m_tokens.size() - 1;
}
group_id_t ruleset_t::add_group_characteristic(const etl::ivector<token_id_t>& tokens, uint32_t factor) {
if (m_group_characteristics.full()) {
throw mtl::length_error();
}
if (tokens.size() > g_max_cgroup_tokens) {
throw mtl::length_error();
}
m_group_characteristics.push_back(group_characteristic_t());
group_characteristic_t& chr = m_group_characteristics.back();
chr.m_factor = factor;
for (token_id_t tok : tokens) {
if (!valid_token(tok)) {
throw mtl::invalid_argument();
}
chr.m_token_ids.push_back(tok);
}
return m_group_characteristics.size() - 1;
}
weighted_group_id_t ruleset_t::add_weighted_group(const etl::ivector<etl::pair<group_id_t, uint32_t>>& weights) {
if (m_weighted_groups.full()) {
throw mtl::length_error();
}
if (weights.size() > g_max_wgroup_weights) {
throw mtl::length_error();
}
m_weighted_groups.push_back(weighted_group_t());
weighted_group_t& wgroup = m_weighted_groups.back();
for (const auto& weight : weights) {
if (!valid_group_characteristic(weight.first)) {
throw mtl::invalid_argument();
}
wgroup.m_groups.push_back(weight.first);
wgroup.m_weights.push_back(weight.second);
wgroup.m_weight_total += weight.second;
}
return m_weighted_groups.size() - 1;
}
#if 0
token_id_t generator_t::add_token(token_type_e type, mtl::fixed value) {
log::debug << "Adding token with type " << (int)type << endl;
@ -199,6 +258,7 @@ void generator_t::set_initial_orientation(mtl::fixed orientation) noexcept {
bool generator_t::step_generation(const etl::ivector<marker_t>& out_markers) noexcept {
}
#endif
} // namespace fractal

View File

@ -8,92 +8,56 @@
#include "fractal.hpp"
using namespace mtl;
using namespace fractal;
int main(void) {
REG_DISPCNT = DCNT_MODE3 | DCNT_BG2;
log::debug << "Hello world!" << mtl::endl;
fractal::generator_t gen;
ruleset_t rules;
try {
fractal::token_id_t walk_petal_length = gen.add_token(fractal::token_type_e::walk, 8);
fractal::token_id_t walk_petal_side = gen.add_token(fractal::token_type_e::walk, 4);
fixed branch_angle = M_PI_4;
fractal::token_id_t rotate_axiom = gen.add_token(fractal::token_type_e::rotate, M_PI_2);
fractal::token_id_t rotate_petal = gen.add_token(fractal::token_type_e::rotate, M_PI_2);
fractal::token_id_t rotate_branchr = gen.add_token(fractal::token_type_e::rotate, -M_PI_4);
fractal::token_id_t rotate_branchl = gen.add_token(fractal::token_type_e::rotate, +M_PI_4);
token_id_t walk_petal_length = rules.add_token(token_type_e::walk, 8);
token_id_t walk_petal_side = rules.add_token(token_type_e::walk, 4);
fractal::token_id_t generate_seed = gen.add_token(fractal::token_type_e::generate);
fractal::token_id_t generate_petal = gen.add_token(fractal::token_type_e::generate);
fractal::token_id_t generate_branchr = gen.add_token(fractal::token_type_e::generate);
fractal::token_id_t generate_branchl = gen.add_token(fractal::token_type_e::generate);
fractal::token_id_t generate_mark = gen.add_token(fractal::token_type_e::generate);
token_id_t rotate_axiom = rules.add_token(token_type_e::rotate, M_PI_2);
token_id_t rotate_petal = rules.add_token(token_type_e::rotate, M_PI_2);
token_id_t rotate_branchr = rules.add_token(token_type_e::rotate, branch_angle);
token_id_t rotate_branchl = rules.add_token(token_type_e::rotate, branch_angle * -2);
token_id_t branch = rules.add_token(token_type_e::empty);
token_id_t petal = rules.add_token(token_type_e::empty);
token_id_t mark = rules.add_token(token_type_e::empty);
log::info << "Added tokens" << endl;
constexpr uint32_t axiom_factor = 4;
constexpr uint32_t branchr_factor = 1;
constexpr uint32_t branchl_factor = 1;
uint32_t axiom_factor = 4;
etl::vector axiom_characteristic{generate_seed, rotate_axiom};
fractal::group_id_t group_axiom = gen.add_group_characteristic(axiom_factor, axiom_characteristic);
etl::vector axiom_chr{ branch, rotate_axiom };
group_id_t axiom_grp = rules.add_group_characteristic(axiom_chr, axiom_factor);
etl::vector seed_characteristic{generate_petal, generate_branchl, generate_branchr};
fractal::group_id_t group_seed = gen.add_group_characteristic(seed_characteristic);
etl::vector branch_chr{ petal, rotate_branchr, branch, rotate_branchl, branch };
group_id_t branch_grp = rules.add_group_characteristic(branch_chr);
etl::vector petal_characteristic{generate_mark, walk_petal_length, generate_mark, rotate_petal, walk_petal_side, generate_mark};
fractal::group_id_t group_petal = gen.add_group_characteristic(petal_characteristic);
etl::vector branchl_characteristic{rotate_branchl, generate_seed};
fractal::group_id_t group_branchl = gen.add_group_characteristic(branchl_factor, branchl_characteristic);
etl::vector branchr_characteristic{rotate_branchr, generate_seed};
fractal::group_id_t group_branchr = gen.add_group_characteristic(branchr_factor, branchr_characteristic);
etl::vector petal_chr{ mark, walk_petal_length, mark, rotate_petal, walk_petal_length, mark };
group_id_t petal_grp = rules.add_group_characteristic(petal_chr);
log::info << "Added group characteristics" << endl;
etl::vector<etl::pair<fractal::group_id_t, uint32_t>, 1> axiom_weights{{group_axiom, 1}};
fractal::weighted_group_id_t wgroup_axiom = gen.add_weighted_group(axiom_weights);
rules.add_branch_rule(branch, branch_grp);
rules.add_branch_rule(petal, petal_grp);
rules.add_mark_rule(mark, 1);
fractal::weighted_group_id_t wgroup_seed = gen.add_weighted_group(group_seed);
fractal::weighted_group_id_t wgroup_petal = gen.add_weighted_group(group_petal);
fractal::weighted_group_id_t wgroup_branchl = gen.add_weighted_group(group_branchl);
fractal::weighted_group_id_t wgroup_branchr = gen.add_weighted_group(group_branchr);
log::info << "Added weighted groups" << endl;
gen.add_basic_branch_rule(generate_seed, wgroup_seed);
gen.add_basic_branch_rule(generate_petal, wgroup_petal);
gen.add_basic_branch_rule(generate_branchl, wgroup_branchl);
gen.add_basic_branch_rule(generate_branchr, wgroup_branchr);
gen.add_marking_branch_rule(generate_mark, 1);
log::info << "Added branch rules" << endl;
gen.set_axiom(wgroup_axiom);
log::info << "Successfully added generator values" << endl;
log::info << "Added rules" << endl;
log::info << "Finished configuring ruleset" << endl;
} catch(const mtl::exception&) {
log::info << "Failed to create generator, caught exception" << endl;
log::info << "Failed to configure ruleset, caught exception" << endl;
return 0;
}
/*gen.preprocess();
constexpr uint32_t num_generations = 3;
constexpr size_t max_markers = 128;
etl::vector<fractal::marker_t, max_markers> markers;
for (size_t i = 0; i < num_generations; ++i) {
markers.clear();
gen.step_generation(markers);
//draw_triangles(markers);
}*/
while (true) {
vid_vsync();
}