-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCellularAutomata.cpp
More file actions
149 lines (132 loc) · 5.55 KB
/
CellularAutomata.cpp
File metadata and controls
149 lines (132 loc) · 5.55 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <iostream>
#include <vector>
#include <thread>
#include <chrono>
#include <fstream>
#include <string>
#include <set>
using namespace std;
int applyRule(int left, int center, int right, int rule) {
'''
The rule is an 8-bit number where each bit represents the new state of the center cell based on the combination of left, center, and right states.
The neighborhood is represented as a 3-bit number where:
'''
int neighborhood = (left << 2) | (center << 1) | right;
int newState = (rule >> neighborhood) & 1;
return newState;
//consider finding a way to have a cell have three states, on, off, and 'turned off' (a cell that was on but is now off, and may be part of a feature). This would allow us to track features more easily, as we could mark cells that have changed state in the current step.
}
struct features{
set<feature> Features;
};
struct feature {
set<int> states; //List of cells that are part of the feature. Useful for ensures 'lines' of empty cells are correctly identified as features.
int startIndex; //From left most index of the feature
int size;
int state; //state of the feature, may be useful for classification.
//int max_width; //largest width of the feature at any point in time, may be useful for classification.
//int length; //number of steps the feature exists for, may be useful for classification.
}
int findFeatures(vector<int>& current, vector<int>& previous, vector<feature>& features) {
// Need to differentiate between features in white (1) and black (0) cells
//features are contiguous blocks of same state cells.
//Takes as input a previous state of the automaton and identifies features in the current state.
//We do not need to care if we are categorizing that are 'background color'
//We can later go through the list of features and remove those of the 'background color'
for (size_t i = 0; i < current.size(); ++i) {
if (current[i] == previous[i]) {
//if previous[i] is part of a feature then so is curren[i] and we need to update the feature
}
if (current[i] != previous[i]) {
feature newFeature;
newFeature.startIndex = i;
newFeature.state = current[i];
newFeature.size = 0;
for(int j = i; j < current.size() && current[j] == current[i]; j++) {
newFeature.size++;
newFeature.states.insert(j);
}
features.insert(newFeature);
}
}
return 0;
}
int imageSimulator(int width, int steps, int rule, vector<int> start, string outputFile = "") {
const int BLOCK_SIZE = 32;
//Two arrays may impact how we measure the 'holes' in the rules
//may need a different approach.
vector<int> current(width, 0);
vector<int> next(width, 0);
bool saveToFile = false;
bool imageCreated = false;
bool sizeExceeded = false;
if ((width > 2000 || steps > 2000) && outputFile != "") {
cout << "Output size too large, skipping file output." << endl;
sizeExceeded = true;
}
std::ofstream img;
if (outputFile != "" && !sizeExceeded) {
if(outputFile.substr(outputFile.find_last_of(".") + 1) == "ppm") {
img.open(outputFile);
img << "P3\n";
img << width << " " << steps << "\n";
img << "255\n";
imageCreated = true;
} else {
img.open(outputFile + ".txt");
}
saveToFile = true;
}
// Initial condition: single active cell in the center
current = start;
for (int t = 0; t < steps; ++t) {
// Display
if (saveToFile == false) {
break;
} else if (imageCreated == false) {
for (int cell : current) {
img << (cell ? '#' : ' ');
}
img << '\n';
} else {
for (int cell : current) {
img << (cell ? "255 255 255 " : "0 0 0 ");
}
img << "\n";
}
// Compute next state
#pragma omp parallel for
for (int i_step = 0; i_step < width; i_step += BLOCK_SIZE) {
for (int i = i_step; i < i_step + BLOCK_SIZE && i < width; ++i) {
int left = current[(i - 1 + width) % width];
int center = current[i];
int right = current[(i + 1) % width];
next[i] = applyRule(left, center, right, rule);
}
}
current = next;
img << "\n";
}
if (saveToFile) {
img.close();
}
return 0;
}
int main() {
//consider stopping the simulation early if we hit the boundary of the width size
const int width = 960;
const int steps = 540;
//for(int rule = 1; rule <= 128; rule++) {
const int rule = 110; // Try 90, 110, 184, etc.
vector<int> start(width, 0);
start[width / 2] = 1;
//might compare features and feature distribution for center in the middle v.s randomly generated starting conditions.
//Need options to run the program without visuals.
auto start_time = std::chrono::high_resolution_clock::now();
imageSimulator(width, steps, rule, start, "cellular_automaton_output_" + to_string(rule) + ".ppm");
auto end_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = end_time - start_time;
std::cout << "Elapsed time: " << elapsed.count() << " seconds\n";
//}
return 0;
}