具有确定性程序的未定义行为

时间:2019-03-01 22:33:17

标签: c++ arrays vector 2d procedural-generation

我目前正在尝试按照“生命游戏”的想法将“洞穴生成”实现为2D阵列。想法如下:

我有一个2s向量,分别为0s和1s(分别代表空气和块),这些向量是通过uniform_real_distributiondensity(此处为0.45,因此数组的45%为1)随机生成的。

此后,我们在数组上迭代 x 次。迭代如下:

  1. 首先,我们将数组复制到一个新数组上。
  2. 第二,我们迭代 old 数组,如下所示:我们查看所在块附近的块数,并根据两件事进行此操作: 如果当前图块是空气块,并且在其附近(-1,-1)到(1,1)周围有4个以上的区块(不包括他自己),请将其更改为NEW ARRAY 如果当前图块是一个块,并且邻域中少于3个块,则在“新阵列”中将其更改为air
  3. 将新阵列复制到旧阵列中

问题是,即使当我用确定性种子来播种我的统一定律时,有时(3遍中的1遍),在经过两到三次迭代后,地图也会完全被块填充。我看了好几个小时的代码后,对字面量的理解几乎为零,这就是为什么我在这里。有代码:

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是个坏习惯,我只是将它用于调试。

我希望有人能让我理解,也许这只是我不知道的正常行为?

非常感谢您。

1 个答案:

答案 0 :(得分:0)

(int)cave_[i].size()中的

NumberOfNeighbours不正确,应该为(int)cave_[x+i].size()(或(int)cave_[0].size(),因为所有行和列的大小均相等)。当我等于-1时,您将具有超出范围的向量访问权限和不确定的行为。