diff --git a/src/map.cpp b/src/map.cpp index 49b16b642f..4778fd9c5a 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -16,6 +16,7 @@ #include "tunnelbridge_map.h" #include "3rdparty/cpp-btree/btree_map.h" #include +#include #include "safeguards.h" @@ -370,6 +371,58 @@ bool CircularTileSearch(TileIndex *tile, uint radius, uint w, uint h, TestTileOn return false; } +/** + * Generalized contiguous matching tile area size threshold function. + * Contiguous means directly adjacent by DiagDirection directions. + * + * @param tile to start the search from. + * @param threshold minimum number of matching tiles for success, searching is halted when this is reached. + * @param proc callback testing function pointer. + * @param user_data to be passed to the callback function. Depends on the implementation + * @return whether the contiguous tile area size is >= threshold + * @pre proc != nullptr + */ +bool EnoughContiguousTilesMatchingCondition(TileIndex tile, uint threshold, TestTileOnSearchProc proc, void *user_data) +{ + assert(proc != nullptr); + if (threshold == 0) return true; + + static_assert(MAX_MAP_TILES_BITS <= 30); + + btree::btree_set processed_tiles; + std::deque candidates; + uint matching_count = 0; + + auto process_tile = [&](TileIndex t, DiagDirection exclude_onward_dir) { + auto iter = processed_tiles.lower_bound(t); + if (iter != processed_tiles.end() && *iter == t) { + /* done this tile already */ + } else { + if (proc(t, user_data)) { + matching_count++; + for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { + if (dir == exclude_onward_dir) continue; + TileIndex neighbour_tile = AddTileIndexDiffCWrap(t, TileIndexDiffCByDiagDir(dir)); + if (IsValidTile(neighbour_tile)) { + candidates.push_back(neighbour_tile | (ReverseDiagDir(dir) << 30)); + } + } + } + processed_tiles.insert(iter, t); + } + }; + process_tile(tile, INVALID_DIAGDIR); + + while (matching_count < threshold && !candidates.empty()) { + uint32 next = candidates.front(); + candidates.pop_front(); + TileIndex t = GB(next, 0, 30); + DiagDirection exclude_onward_dir = (DiagDirection)GB(next, 30, 2); + process_tile(t, exclude_onward_dir); + } + return matching_count >= threshold; +} + /** * Finds the distance for the closest tile with water/land given a tile * @param tile the tile to find the distance too diff --git a/src/map_func.h b/src/map_func.h index eebd21fb67..616c972f03 100644 --- a/src/map_func.h +++ b/src/map_func.h @@ -438,6 +438,8 @@ typedef bool TestTileOnSearchProc(TileIndex tile, void *user_data); bool CircularTileSearch(TileIndex *tile, uint size, TestTileOnSearchProc proc, void *user_data); bool CircularTileSearch(TileIndex *tile, uint radius, uint w, uint h, TestTileOnSearchProc proc, void *user_data); +bool EnoughContiguousTilesMatchingCondition(TileIndex tile, uint threshold, TestTileOnSearchProc proc, void *user_data); + /** * Get a random tile out of a given seed. * @param r the random 'seed'