Я пытаюсь кодировать Cellular Automaton, используя C ++, по какой-то причине, однако, когда программа запускается, мои правила, кажется, применяются неправильно или не применяются вообще. Любой совет будет принят во внимание.
Основная функция симуляции:
/* Simulate Array */
int beginSimulation(char parentArray[],char childArray[],int width, int ruleSet[]){
//get the amount of generations the program produces
int generationNum;
cout << "Please enter how many generations you would like to simulate" << endl;
cin >> generationNum;
for(int times=0; times< generationNum; times++){
//loop for applying ruleset to each cell in array
for(int i=0; i< width; i ++){
char left = parentArray[i-1];
char middle = parentArray[i];
char right = parentArray[i+1];// if statement that compares the current cells and its neighbours
// with the rules in the ruleset to define the current generation.
if(left == 'X' && middle == 'X' && right == 'X'){
if(ruleSet[7] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[7];
}
else if(left == 'X' && middle == 'X' && middle == '~'){
if(ruleSet[6] == 1){ //do this for each rule should work
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[6];
}
else if (left == 'X' && middle == '~' && middle == 'X'){
if(ruleSet[5] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[5];
}
else if (left == 'X' && middle == '~' && middle == '~'){
if(ruleSet[4] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[4];
}
else if (left == '~' && middle == 'X' && middle == 'X'){
if(ruleSet[3] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[3];
}else if (left == '~' && middle == 'X' && middle == '~'){
if(ruleSet[2] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[2];
}
else if (left == '~' && middle == '~' && middle == 'x'){
if(ruleSet[1] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[1];
}
else if (left == '~' && middle == '~' && middle == '~'){
childArray[i] = ruleSet[0];
if(ruleSet[0] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[0];
}
}//for loop that iterates through the array and display all its elements
for(int i = 0; i < width; i++)
{
cout << childArray[i];
}
cout<< endl;
// loop to make the current generation the past generation for the next
//iteration of the code
for(int c=0; c< width; c ++){
parentArray[c] = childArray[c];
}
}
}
Функция, которая использует beginSimulation:
/* Initialize Array */
int initializeArrays(char parentArray[],char childArray[],int width, int ruleSet[]){
//cout << "Please enter the size of the array" << endl;
//cin >> width;
cout << "Please enter the rule you would like to simulate" << endl;
int userInput = 0; //initialises userInput variable to be passed
cin >> userInput; //into the insertItem function
for(int x=0; x<width; x++){
if(x==(width/2)){
parentArray[(width/2)] = 'X';
continue;
}
cout << "";
parentArray[x] = '~'; /* or whatever number you want */
}
/* parentArray[0..width-1] = "~~...~~X~...~~"* ^
* \- at width/2
*/
cout << parentArray << endl;
for(int i=0; i<width; i++){
childArray[i] = '~'; /* or whatever number you want */
cout << "";
}
/* childArray[0...width - 1] = "~~...~~" */
cout << childArray << endl;
/* User input is bit mask to activate rules 0..7
* e.g. input = 10 = 0x0A = 0b1010 => rule 1 and 3 activated */
for (int z=7; z>(-1); z --){
ruleSet[z] = userInput % 2;
userInput = userInput/2;
}cout << ruleSet[0] << endl;
cout << ruleSet[1] << endl;
cout << ruleSet[2] << endl;
cout << ruleSet[3] << endl;
cout << ruleSet[4] << endl;
cout << ruleSet[5] << endl;
cout << ruleSet[6] << endl;
cout << ruleSet[7] << endl;
beginSimulation(parentArray, childArray, width, ruleSet);
return 0;
}
Во-первых, когда вы оцениваете клеточные автоматы, вам нужно правильно обрабатывать граничные условия. Когда вы пишете: вы переходите от обоих концов массивов к неопределенному поведению:
char left = parentArray[i-1];
char middle = parentArray[i];
char right = parentArray[i+1];
Так как i
идет от 0
в width-1
, Поэтому, когда i
является 0
, i-1
будет индекс -1
в ваш массив и получить доступ назад до начала массива. Индекс i+1
аналогично получит доступ вперед за конец массива, когда i
равно width-1
,
Во-вторых, ваша попытка использовать ваши правила для получения следующего значения ячеек на основе текущих значений безоговорочно перезаписывается следующим оператором. У вас есть такие утверждения, как:
if(ruleSet[7] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[7];
Неважно, ruleSet[7]
содержит 1
или нет, потому что вы сразу перезаписать childAray[i]
с тем, что в ruleSet[7]
, Вы делаете это для всей вашей другой обработки правил. Так что да, ваши правила никак не влияют на результат.
Похоже, это алгоритм или симуляция в Game-of-Life, где пользователь может определить ширину игрового поля и какие правила активны для изучения результатов в течение нескольких поколений.
Игровое поле представляет собой массив [width], инициализированный в «~» (пустой?) С одним «X» (активным) в width / 2
, Правила в ruleSet будут действовать на этот массив для создания следующего поколения после одного шага симуляции.
Во-первых, вы явно не запускали этот код и редактировали его для этого сайта, см. Строку № 13:
for(int i=0; i< width; i ++){
char left = parentArray[i-1]; // <-- What will happen for i == 0?
char middle = parentArray[i];
char right = parentArray[i+1];
...
}
У вас, вероятно, есть какое-то особое условие для обработки i == 0
, наверное
if (i == 0)
left = '~';
Так что я приму это.
Во-вторых, вы опечатали правило № 1 вокруг строки № 84:
else if (left == '~' && middle == '~' && middle == 'x'){ // <-- small Latin x
Я не уверен, что должно произойти, так как все правила, кроме правила № 0, представляют собой один и тот же код, и все правила делают то же самое. Таким образом, вы не должны были включать так много кода в свой оригинальный пост.
Во всяком случае, глядя на любое правило:
childArray[i] = ruleSet[0]; // this line in ruleSet[0] only
if(ruleSet[0] == 1){
childArray[i] = 'X';
} else {
childArray[i] = '~';
}
childArray[i] = ruleSet[0]; // overwrite childArray[i]
Ваши массивы представляют собой последовательности ‘X’ и ‘~’ — но вы храните целые числа 0 и 1, которые будут символами ‘\ x00’ и ‘\ x01’ соответственно после того, как вы закончите.
Может быть, это было только для отладки, но вы оставили это в? Или, может быть, вы ошиблись?
Так как все условия if соответствуют только комбинациям ‘~’ и ‘X’ (и ‘x’), ваша симуляция остановится после первого шага и всегда даст тот же результат, когда любое правило включено.
Я предлагаю вам улучшить ваши структуры данных, чтобы с ними было легче работать. Правила имеют две модели: search
, который будет заменен на replace
когда нашел.
typedef struct rule {
char search[3];
char replace[3];
rule(char* searchPattern, char* replacePattern) {
memcpy(&search, searchPattern, sizeof(search));
memcpy(&replace, replacePattern, sizeof(replace));
}
} rule_t;
Игровое поле представляет собой структуру, которая содержит его ширину и фактические данные.
Обычай operator=
создает глубокую копию всего поля для нас.
typedef struct field {
size_t width;
char* data;
field(size_t field_width) {
width = field_width;
data = new char[width + 1];
memset(data, '~', width);
data[width] = '\0';
/* catch out of memory exception here */
}
~field() {
width = 0;
delete[] data;
}
field& operator=(const field& rhs) {
/* prevent self-assignment */
if (this == &rhs)
return *this;
this->width = rhs.width;
delete[] data;
this->data = new char[width + 1];
/* catch out of memory exception here */
memcpy(this->data, rhs.data, width + 1);
return *this;
}
} field_t;
Основной алгоритм
for (int generation = 0; generation < numGenerations; generation++) {
myFieldAfter = myFieldBefore; /* deep copy of field */
/* sliding window pointers */
char *windowBefore = myFieldBefore.data;
char *windowAfter = myFieldAfter.data;
/* we have to stop sizeof(rule_t.search) - 1 = 3 - 1 = 2 bytes before end */
for (size_t x = 0; x < myFieldBefore.width - 2; x++) {
/* apply rules hierarchically */
for (auto it = activeRules.begin(); it != activeRules.end(); it++) {
if (!(memcmp(it->search, windowBefore, sizeof(it->search)))) {
memcpy(windowAfter, it->replace, sizeof(it->replace));
break; /* only apply first matching rule */
}
}
/* move windows */
windowBefore++;
windowAfter++;
}
std::cout << "Generation " << generation << ": " << myFieldBefore.data << "\n";
myFieldBefore = myFieldAfter; /* deep copy back */
}
Я предлагаю вам разобрать аргументы из командной строки, например, вызов может выглядеть myCellAutomaton 32 5 "1,3"
чтобы получить поле шириной 32, смоделируйте 5 поколений и используйте правила № 1 и № 3.
Посмотрите на Boost.Program_options. Примерный набросок финальной программы выглядит так:
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
/* definitions from above */
int main(int argc, char** argv) {
int numGenerations;
int width;
std::vector<rule_t> activeRules;
/* parse command line arguments */
numGenerations = 5;
width = 32;
/* example rule */
rule_t myRule("X~~", "XX~");
/* vector of active rules */
activeRules.push_back(myRule);
field_t myFieldBefore(width);
myFieldBefore.data[width / 2] = 'X';
field_t myFieldAfter(width);
/* algorithm here */
}
Вывод следующий:
Generation 0: ~~~~~~~~~~~~~~~~X~~~~~~~~~~~~~~~
Generation 1: ~~~~~~~~~~~~~~~~XX~~~~~~~~~~~~~~
Generation 2: ~~~~~~~~~~~~~~~~XXX~~~~~~~~~~~~~
Generation 3: ~~~~~~~~~~~~~~~~XXXX~~~~~~~~~~~~
Generation 4: ~~~~~~~~~~~~~~~~XXXXX~~~~~~~~~~~
Я надеюсь, что этот ответ дал вам общее представление о том, что делать. Я использовал простые структуры и стандартные вызовы, чтобы сделать его простым, хотя это делает всю программу скорее C-ish, чем чистым C ++.