Неопределенное поведение с детерминированной процедурой

В настоящее время я пытаюсь реализовать «генерацию пещеры» в виде 2D-массива, следуя идеям «Игры жизни». Идея заключается в следующем:

У меня есть двумерный вектор из 0 и 1 (которые соответственно представляют воздух и блок), случайно сгенерированный с помощью uniform_real_distribution с density (здесь 0,45, поэтому 45% массива будет 1).

После этого мы повторяем массив Икс раз. Итерация выглядит следующим образом:

  1. Сначала копируем массив на новый.
  2. Во-вторых, мы повторяем массив Старый следующим образом: мы смотрим на количество блоков в окрестности блока, в котором мы находимся, и в зависимости от двух вещей мы делаем это: ЕСЛИ текущая плитка воздушная и имеет более 4 блоков по соседству (-1,-1) до (1,1), исключая себя, замените ее на блок в НОВОМ МАССИВЕ. ЕСЛИ текущая плитка является блоком и имеет менее 3 блоков по соседству, измените ее на воздух в НОВОМ МАССИВЕ.
  3. Скопируйте новый массив в старый массив

Проблема в том, что ДАЖЕ, когда я засею свой однородный закон детерминистским начальным числом, иногда (1 раз из 3) карта будет полностью заполнена блоками после двух или трех итераций. У меня буквально 0 идей, почему после многочасового просмотра моего кода, и именно поэтому я здесь. Есть код:

cavefactory.h

#ifndef CAVEFACTORY_H_
#define CAVEFACTORY_H_

#include <vector>

namespace cavegenerator {

// define cave_t as a 2d vector of integers
using cave_t = std::vector<std::vector<int>>;

// constants
namespace DEFAULT {

constexpr unsigned short int WIDTH = 64;
constexpr unsigned short int HEIGHT = 64;
constexpr float DENSITY = 0.45;
constexpr unsigned short int BIRTH_LIMIT = 4;
constexpr unsigned short int DEATH_LIMIT = 3;

} // namespace DEFAULT


class CaveFactory {
    public:
        CaveFactory(unsigned short int width = DEFAULT::WIDTH,
                                unsigned short int height = DEFAULT::HEIGHT,
                                float density = DEFAULT::DENSITY);

        // makes a cave with the desired number of iterations and parameters
    static cave_t MakeCave(unsigned short int width = DEFAULT::WIDTH,
                                                    unsigned short int height = DEFAULT::HEIGHT,
                                                    float density = DEFAULT::DENSITY,
                                                    int iterations = 3,
                                                    unsigned short int bl = DEFAULT::BIRTH_LIMIT,
                                                    unsigned short int dl = DEFAULT::DEATH_LIMIT);

        // implemented in case of generalization of cave(more than two blocks)
        bool isSolid(int i, int j);

        cave_t getCave();

        void Print();
        void Iterate( unsigned short int bl = DEFAULT::BIRTH_LIMIT,
                                    unsigned short int dl = DEFAULT::DEATH_LIMIT );

    private:
    cave_t cave_;

    int NumberOfNeighbours(int i, int j);

    void Initialize(float density = DEFAULT::DENSITY);


};

} // namespace cavegenerator

#endif // CAVEFACTORY_H_

cavefactory.cc

#include "cavefactory.h"
#include <random>
#include <iostream>
#include <ctime>
#include <algorithm>


namespace cavegenerator {


CaveFactory::CaveFactory(unsigned short int width, unsigned short int height, float density) {
    cave_.resize(width);
  for (auto &i : cave_) {
    i.resize(height);
  }
  Initialize(density);
}

bool CaveFactory::isSolid(int i, int j) {
    return (cave_[i][j] == 1);
}

int CaveFactory::NumberOfNeighbours(int x, int y) {
    int num = 0;

  for (int i = -1; i < 2; i++) {
        for (int j = -1; j < 2; j++) {
      if ( i == 0 && j == 0 ) continue; // we don't want to count ourselve

      // if out of bounds, add a solid neighbour
      if ( x + i >= (int)cave_.size() || x + i < 0 || y + j >= (int)cave_[i].size() || y + j < 0) {
                ++num;
            } else if (isSolid(x+i, y+j)) {
                ++num;
            }
        }
    }

    return num;
}

cave_t CaveFactory::getCave() {
    return cave_;
}

void CaveFactory::Print() {
    for (auto &i : cave_) {
        for (auto &j : i) {
            std::cout << ((j==1) ? "x" : " ");
        }
        std::cout << "\n";
  }
  return;
}

cave_t CaveFactory::MakeCave(unsigned short int width,
                                unsigned short int height,
                                float density,
                                int iterations,
                                unsigned short int bl,
                                unsigned short int dl)
{

    CaveFactory cave(width, height, density);
  for (int i = 0; i < iterations; i++) {
        cave.Iterate(bl, dl);
  }

  return cave.getCave();
}


// Initlialize the cave with the specified density
void CaveFactory::Initialize(float density) {
  std::mt19937 rd(4);
  std::uniform_real_distribution<float> roll(0, 1);

  for (auto &i : cave_) {
        for (auto &j : i) {
            if (roll(rd) < density) {
                j = 1;
            } else {
                j = 0;
            }
        }
  }
}

// for each cell in the original cave, if the cell is solid:
// if the number of solid neighbours is under the death limit, we kill the block
// if the cell is air, if the number of solid blocks is above the birth limit we place a block
void CaveFactory::Iterate(unsigned short int bl, unsigned short int dl) {
  cave_t new_cave = cave_;

  for (int i = 0; i < (int)cave_.size(); i++) {
        for (int j = 0; j < (int)cave_[0].size(); j++) {

      int number_of_neighbours = NumberOfNeighbours(i, j);
      if (isSolid(i, j) && number_of_neighbours < dl) {
                new_cave[i][j] = 0;
      } else if (!isSolid(i,j) && number_of_neighbours > bl) {
                new_cave[i][j] = 1;
      }
      }
  }

  std::copy(new_cave.begin(), new_cave.end(), cave_.begin());
}



} // namespace cavegenerator

main.cc

#include <iostream>
#include <vector>
#include <random>
#include <ctime>
#include <windows.h>

#include "cavefactory.h"



int main() {

  cavegenerator::CaveFactory caveEE;
  caveEE.Print();


for(int i = 0; i < 15; i++) {
        caveEE.Iterate();
        Sleep(600);
        system("cls");
        caveEE.Print();
  }
  return 0;
}

Я знаю, что windows.h — плохая привычка, я просто использовал ее для отладки.

Я надеюсь, что кто-то может заставить меня понять, может быть, это просто нормальное поведение, о котором я не знаю?

Спасибо большое.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
40
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

(int)cave_[i].size() в NumberOfNeighbours неверно, должно быть (int)cave_[x+i].size() (или (int)cave_[0].size(), поскольку все строки и столбцы имеют одинаковый размер). Когда i равно -1, у вас есть доступ к вектору за пределами границ и поведение undefined.

Что ж, теперь я чувствую себя действительно тупым. Наверное, я пропустил это 10 раз, когда рецензировал. Спасибо, я оставлю это на ночь, чтобы убедиться, что вы увидите мой ответ, но я удалю пост после того, как это тривиально

Tommy-Xavier Robillard 02.03.2019 00:22

Другие вопросы по теме