forked from EthanPadden/cellular-automata-model
-
Notifications
You must be signed in to change notification settings - Fork 1
/
plugin.cpp
93 lines (76 loc) · 3.02 KB
/
plugin.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/**
* Copyright (c) 2018 - Marcos Cardinot <marcos@cardinot.net>
* Copyright (c) 2018 - Ethan Padden <e.padden1@nuigalway.ie>
*
* This source code is licensed under the MIT license found in
* the LICENSE file in the root directory of this source tree.
*/
#include "plugin.h"
namespace evoplex {
bool CellularAutomata1D::init()
{
// this model is only valid for `squareGrid` graph
if (graphId() != "squareGrid" || !graph()->attrExists("boundary") ||
!graph()->attrExists("width") || !graph()->attrExists("height")) {
return false;
}
// starts reading from row=0
m_currRow = 0;
// checks if the `squareGrid` was set with periodic bondary conditions (i.e., a toroid)
m_toroidal = graph()->attr("boundary") == "periodic";
// gets the `squareGrid` width
m_width = graph()->attr("width").toInt();
// gets the `squareGrid` height
m_height = graph()->attr("height").toInt();
if (m_height < 2) {
qWarning() << "the 'squareGrid' height must be >= 2";
return false;
}
// gets the id of the `state` node's attribute, which is the same for all nodes
m_stateAttrId = node(0).attrs().indexOf("state");
// determines which rule to use
m_binrule = std::bitset<8>(attr("rule").toInt());
return m_stateAttrId >= 0;
}
bool CellularAutomata1D::algorithmStep()
{
// 1. gets first node in the current row
Node first = node(linearIdx(m_currRow, 0));
// 2. for each node (starting from the second),
int lastColumn = m_width - 1;
for (int col = 1; col < lastColumn; ++col) {
Node central = node(first.id() + col);
// a. compute the next state based on its neighbours on the left and right
Value state = nextState(node(central.id()-1), central, node(central.id()+1));
// b. assign the next state to the node below the current node
node(central.id()+m_width).setAttr(m_stateAttrId, state);
}
// 3. edge case: if the graph is a toroid, we compute the state for the last column
if (m_toroidal) {
Node last = node(linearIdx(m_currRow, lastColumn));
// a. compute the next state based on its left neighbour and the first node in the row
Value state = nextState(node(last.id()-1), last, first);
// b. assign the next state to the first node of the next row
node(first.id()+m_width).setAttr(m_stateAttrId, state);
}
++m_currRow;
if (m_currRow == m_height-1) {
// all rows have been filled; return false to stop the simulation
return false;
}
return true;
}
Value CellularAutomata1D::nextState(const Node& leftNode, const Node& node, const Node& rightNode) const
{
bool left = leftNode.attr(m_stateAttrId).toBool();
bool center = node.attr(m_stateAttrId).toBool();
bool right = rightNode.attr(m_stateAttrId).toBool();
return Value(m_binrule[left*4 + center*2 + right]);
}
int CellularAutomata1D::linearIdx(int row, int col) const
{
return row * m_width + col;
}
} // evoplex
REGISTER_PLUGIN(CellularAutomata1D)
#include "plugin.moc"