OSDN Git Service

[Refactor] ダンジョンフロアの連結性判定で std::stack を使うようにした
authortaotao54321 <taotao54321@gmail.com>
Sat, 20 Feb 2021 14:20:58 +0000 (23:20 +0900)
committertaotao54321 <taotao54321@gmail.com>
Sat, 6 Mar 2021 21:22:03 +0000 (06:22 +0900)
src/floor/floor-generator.cpp

index 7467638..346f9f4 100644 (file)
@@ -9,7 +9,9 @@
  * 2014 Deskull rearranged comment for Doxygen. \n
  */
 
-#include <assert.h>
+#include <algorithm>
+#include <array>
+#include <stack>
 
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
 #include "wizard/wizard-messages.h"
 #include "world/world.h"
 
-//--------------------------------------------------------------------
-// 動的可変長配列
-//--------------------------------------------------------------------
-
-typedef struct Vec {
-    size_t len;
-    size_t cap;
-    int *data;
-} Vec;
-
-// 容量 cap の空 Vec を返す。
-static Vec vec_with_capacity(const size_t cap)
-{
-    assert(cap > 0);
-
-    Vec vec = { .len = 0, .cap = cap, .data = NULL };
-    vec.data = static_cast<int*>(malloc(sizeof(int) * cap));
-    if (!vec.data)
-        rpanic(cap);
-
-    return vec;
-}
-
-// サイズ len で、全要素が init で初期化された Vec を返す。
-static Vec vec_new(const size_t len, const int init)
-{
-    assert(len > 0);
-
-    const size_t cap = 2 * len;
-    Vec vec = vec_with_capacity(cap);
-
-    vec.len = len;
-    for (size_t i = 0; i < len; ++i)
-        vec.data[i] = init;
-
-    return vec;
-}
-
-static void vec_delete(Vec *const vec)
-{
-    free(vec->data);
-
-    vec->len = 0;
-    vec->cap = 0;
-    vec->data = NULL;
-}
-
-static size_t vec_size(const Vec *const vec) { return vec->len; }
-
-static bool vec_is_empty(const Vec *const vec) { return vec_size(vec) == 0; }
-
-static void vec_push_back(Vec *const vec, const int e)
-{
-    // 容量不足になったら容量を拡張する。
-    if (vec->len == vec->cap) {
-        vec->cap = vec->cap > 0 ? 2 * vec->cap : 1;
-        vec->data = static_cast<int*>(realloc(vec->data, sizeof(int) * vec->cap));
-        if (!vec->data)
-            rpanic(vec->cap);
-    }
-
-    vec->data[vec->len++] = e;
-}
-
-static int vec_pop_back(Vec *const vec)
-{
-    assert(!vec_is_empty(vec));
-
-    return vec->data[vec->len-- - 1];
-}
-
-//--------------------------------------------------------------------
-
 /*!
  * @brief 闘技場用のアリーナ地形を作成する / Builds the on_defeat_arena_monster after it is entered -KMW-
  * @param player_ptr プレーヤーへの参照ポインタ
@@ -448,8 +377,7 @@ static bool is_permanent_blocker(const floor_type *const floor_ptr, const int y,
     return has_flag(flags, FF_PERMANENT) && !has_flag(flags, FF_MOVE);
 }
 
-static void floor_is_connected_dfs(
-    const floor_type *const floor_ptr, const IsWallFunc is_wall, const int y_start, const int x_start, Vec *const stk, const Vec *const visited)
+static void floor_is_connected_dfs(const floor_type *const floor_ptr, const IsWallFunc is_wall, const int y_start, const int x_start, bool *const visited)
 {
     // clang-format off
     static const int DY[8] = { -1, -1, -1,  0, 0,  1, 1, 1 };
@@ -460,11 +388,16 @@ static void floor_is_connected_dfs(
     const int w = floor_ptr->width;
     const int start = w * y_start + x_start;
 
-    vec_push_back(stk, start);
-    visited->data[start] = 1;
+    // 深さ優先探索用のスタック。
+    // 最大フロアサイズが h=66, w=198 なので、スタックオーバーフロー防止のため再帰は使わない。
+    std::stack<int> stk;
 
-    while (!vec_is_empty(stk)) {
-        const int cur = vec_pop_back(stk);
+    stk.emplace(start);
+    visited[start] = TRUE;
+
+    while (!stk.empty()) {
+        const int cur = stk.top();
+        stk.pop();
         const int y = cur / w;
         const int x = cur % w;
 
@@ -474,13 +407,13 @@ static void floor_is_connected_dfs(
             if (y_nxt < 0 || h <= y_nxt || x_nxt < 0 || w <= x_nxt)
                 continue;
             const int nxt = w * y_nxt + x_nxt;
-            if (visited->data[nxt])
+            if (visited[nxt])
                 continue;
             if (is_wall(floor_ptr, y_nxt, x_nxt))
                 continue;
 
-            vec_push_back(stk, nxt);
-            visited->data[nxt] = 1;
+            stk.emplace(nxt);
+            visited[nxt] = TRUE;
         }
     }
 }
@@ -491,33 +424,29 @@ static void floor_is_connected_dfs(
 // 連結成分数が 0 の場合、偽を返す。
 static bool floor_is_connected(const floor_type *const floor_ptr, const IsWallFunc is_wall)
 {
+    static std::array<bool, MAX_HGT * MAX_WID> visited;
+
     const int h = floor_ptr->height;
     const int w = floor_ptr->width;
 
-    // ヒープ上に確保したスタックを用いてDFSする。
-    // 最大フロアサイズが h=66, w=198 なので、単純に再帰DFSするとスタックオーバーフローが不安。
-    Vec stk = vec_with_capacity(1024);
-    Vec visited = vec_new((size_t)h * (size_t)w, 0);
+    std::fill(begin(visited), end(visited), false);
+
     int n_component = 0; // 連結成分数
 
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
             const int idx = w * y + x;
-            if (visited.data[idx])
+            if (visited[idx])
                 continue;
             if (is_wall(floor_ptr, y, x))
                 continue;
 
             if (++n_component >= 2)
-                goto finish;
-            floor_is_connected_dfs(floor_ptr, is_wall, y, x, &stk, &visited);
+                break;
+            floor_is_connected_dfs(floor_ptr, is_wall, y, x, visited.data());
         }
     }
 
-finish:
-    vec_delete(&visited);
-    vec_delete(&stk);
-
     return n_component == 1;
 }