行动决定的模式

时间:2017-02-25 13:58:39

标签: c algorithm

我正在编写迷宫生成器,在某些时候我必须选择一个单元格的随机未访问的邻居。第一个想法是枚举邻居,例如left = 0,top = 1,right = 2,bottom = 3,并使用rand()%4生成随机数并选择合适的单元格。但是,并非所有单元都有4个邻居,因此我必须编写以下代码:

Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell) {

int randomNumb = rand() % 4;

int timer = 1;

while(timer > 0) {
    if (randomNumb == 0 && currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited) 
        return &maze->map[currentCell->y][currentCell->x+1];
    if (randomNumb == 1 && currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited) 
        return &maze->map[currentCell->y][currentCell->x-1];
    if (randomNumb == 2 && currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited) 
        return &maze->map[currentCell->y+1][currentCell->x];
    if (randomNumb == 3 && currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited) 
        return &maze->map[currentCell->y-1][currentCell->x];

    timer--;
    randomNumb = rand() % 4;
}


if (currentCell->x < maze->width-1 && maze->map[currentCell->y][currentCell->x+1].isUnvisited) 
    return &maze->map[currentCell->y][currentCell->x+1];
if (currentCell->x > 0 && maze->map[currentCell->y][currentCell->x-1].isUnvisited) 
    return &maze->map[currentCell->y][currentCell->x-1];
if (currentCell->y < maze->height-1 && maze->map[currentCell->y+1][currentCell->x].isUnvisited) 
    return &maze->map[currentCell->y+1][currentCell->x];
if (currentCell->y > 0 && maze->map[currentCell->y-1][currentCell->x].isUnvisited) 
    return &maze->map[currentCell->y-1][currentCell->x];

return NULL;
}

因此,如果经过10次迭代后没有选择正确的决定,那么它将被蛮力挑选。这种方法似乎很好,因为变量 timer 会改变迷宫的复杂性:计时器越少,迷宫越简单。然而,如果我唯一的目的是生成完全随机的迷宫,那么它需要大量的执行时间并且看起来有点难看。是否有任何模式(使用C语言)或重构方式可以使我在没有长开关和大量if-else结构的情况下处理这种情况?

2 个答案:

答案 0 :(得分:1)

建议@pat@Ivan Gritsenko,您可以将随机选择限制为仅有效的单元格,如下所示:

Cell* getRandomNeighbour(const Maze* const maze, const Cell* const currentCell)
{
    Cell *neighbours[4] = {NULL};
    int count = 0;

    // first select the valid neighbours
    if (    currentCell->x  <  maze->width - 1 
         && maze->map[currentCell->y][currentCell->x + 1].isUnvisited ) { 
        neighbours[count++] = &maze->map[currentCell->y][currentCell->x + 1];
    }
    if (    currentCell->x  >  0 
         && maze->map[currentCell->y][currentCell->x - 1].isUnvisited ) {
        neighbours[count++] = &maze->map[currentCell->y][currentCell->x - 1];
    }
    if (    currentCell->y  <  maze->height - 1 
         && maze->map[currentCell->y + 1][currentCell->x].isUnvisited ) {
        neighbours[count++] = &maze->map[currentCell->y + 1][currentCell->x];
    }
    if (    currentCell->y  >  0 
         && maze->map[currentCell->y - 1][currentCell->x].isUnvisited ) {
        neighbours[count++] = &maze->map[currentCell->y - 1][currentCell->x];
    }

    // then choose one of them (if any)
    int chosen = 0;
    if ( count > 1 )    
    {
        int divisor = RAND_MAX / count;
        do { 
            chosen = rand() / divisor;
        } while (chosen >= count);
    }
    return neighbours[chosen];
}

随机数生成部分背后的基本原理(与更常见的rand() % count相反)在this answer中得到了很好的解释。

答案 1 :(得分:0)

考虑重复的代码,并采用更有纪律的方式来选择尝试的方向顺序:

// in_maze returns whether x, y is a valid maze coodinate.
int in_maze(const Maze* const maze, int x, int y) {
    return 0 <= x && x < maze->width && 0 <= y && y < maze->height;
}

Cell *get_random_neighbour(const Maze* const maze, const Cell* const c) {
    int dirs[] = {0, 1, 2, 3};
    // Randomly shuffle dirs.
    for (int i = 0; i < 4; i++) {
        int r = i + rand() % (4 - i);
        int t = dirs[i];
        dirs[i] = dirs[r];
        dirs[r] = t;
    }
    // Iterate through the shuffled dirs, returning the first one that's valid.
    for (int trial=0; trial<4; trial++) {
        int dx = (dirs[trial] == 0) - (dirs[trial] == 2);
        int dy = (dirs[trial] == 1) - (dirs[trial] == 3);
        if (in_maze(maze, c->x + dx, c->y + dy)) {
            const Cell * const ret = &maze->map[c->y + dy][c->x + dx];
            if (ret->isUnvisited) return ret;
        }
    }
    return NULL;
}

(免责声明:未经测试 - 它可能有一些小问题,例如const正确性。)