mirror of
https://github.com/zhenyan121/SporeBG-Conid.git
synced 2026-04-10 06:14:08 +08:00
Add ConnectedComponentManager
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
#include "Board.h"
|
||||
|
||||
Board::Board(int rows, int cols) :
|
||||
m_rows(rows),
|
||||
m_cols(cols),
|
||||
m_grid(rows, )
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "Piece.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
// 用 (row, col) 表示坐标,0-based
|
||||
struct Position {
|
||||
int row;
|
||||
int col;
|
||||
//运算符重载用于判断位置是否相等
|
||||
bool operator==(const Position& other) const {
|
||||
return row == other.row && col == other.col;
|
||||
}
|
||||
};
|
||||
|
||||
class Board {
|
||||
private:
|
||||
std::vector<std::vector<std::unique_ptr<Piece>>> m_grid;
|
||||
const int m_rows;
|
||||
const int m_cols;
|
||||
public:
|
||||
Board(int rows, int cols);
|
||||
~Board();
|
||||
|
||||
};
|
||||
238
src/core/ConnectedComponentManager.cpp
Normal file
238
src/core/ConnectedComponentManager.cpp
Normal file
@@ -0,0 +1,238 @@
|
||||
#include "ConnectedComponentManager.h"
|
||||
|
||||
ConnectedComponentManager::ConnectedComponentManager(int numPieces)
|
||||
: m_totalPieces(numPieces) {
|
||||
//初始化数组大小 ROWS * COLS
|
||||
m_parent.resize(numPieces);
|
||||
m_rank.resize(numPieces, 0);
|
||||
m_adjacentList.resize(numPieces);
|
||||
//初始化每个棋子为独立组件
|
||||
for (int i = 0; i < numPieces; i++) {
|
||||
m_parent[i] = i;
|
||||
m_componentPieces[i] = {i}; //每个组件初始包含一个棋子
|
||||
m_pieceToComponent[i] = i; //棋子指向的组件
|
||||
}
|
||||
}
|
||||
|
||||
int ConnectedComponentManager::find(int pieceId) {
|
||||
if (m_parent[pieceId] != pieceId) {
|
||||
m_parent[pieceId] = find(m_parent[pieceId]);
|
||||
}
|
||||
return m_parent[pieceId];
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::unite(int pieceId1, int pieceId2) {
|
||||
int root1 = find(pieceId1);
|
||||
int root2 = find(pieceId2);
|
||||
|
||||
if (root1 == root2) {
|
||||
return;
|
||||
|
||||
}
|
||||
if (m_rank[root1] < m_rank[root2]) {
|
||||
m_parent[root1] = root2;
|
||||
// 合并棋子集合
|
||||
m_componentPieces[root2].merge(m_componentPieces[root1]);
|
||||
m_componentPieces.erase(root1);
|
||||
// 更新棋子到组件的映射
|
||||
for (int piece : m_componentPieces[root2]) {
|
||||
m_pieceToComponent[piece] = root2;
|
||||
}
|
||||
} else {
|
||||
m_parent[root2] = root1;
|
||||
m_componentPieces[root1].merge(m_componentPieces[root2]);
|
||||
m_componentPieces.erase(root2);
|
||||
for (int piece : m_componentPieces[root1]) {
|
||||
m_pieceToComponent[piece] = root1;
|
||||
}
|
||||
}
|
||||
addConnection(pieceId1, pieceId2);
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::addConnection(int pieceId1, int pieceId2) {
|
||||
//将元素放入邻接表
|
||||
m_adjacentList[pieceId1].insert(pieceId2);
|
||||
m_adjacentList[pieceId2].insert(pieceId1);
|
||||
|
||||
}
|
||||
|
||||
bool ConnectedComponentManager::disconnectFromNeighbor(int pieceId, int neighborId){
|
||||
// 检查是否真的相连
|
||||
if (!areDirectlyConnected(pieceId, neighborId)) {
|
||||
return false;
|
||||
}
|
||||
// 从邻接表中移除连接
|
||||
m_adjacentList[pieceId].erase(neighborId);
|
||||
m_adjacentList[neighborId].erase(pieceId);
|
||||
// 重新计算连通性
|
||||
recomputeComponentsAfterDisconnection(pieceId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectedComponentManager::disconnectFromComponent(int pieceId) {
|
||||
int oldComponentId = find(pieceId);
|
||||
if (oldComponentId == -1) return false;
|
||||
|
||||
// 记录所有直接连接
|
||||
auto neighbors = m_adjacentList[pieceId];
|
||||
|
||||
// 断开所有连接
|
||||
for (int neighborId : neighbors) {
|
||||
m_adjacentList[pieceId].erase(neighborId);
|
||||
m_adjacentList[neighborId].erase(pieceId);
|
||||
}
|
||||
|
||||
// 将被断开的棋子设为独立组件
|
||||
m_parent[pieceId] = pieceId;
|
||||
m_rank[pieceId] = 0;
|
||||
m_componentPieces[pieceId] = {pieceId};
|
||||
m_pieceToComponent[pieceId] = pieceId;
|
||||
|
||||
// 如果原组件还有其他棋子,需要重新计算连通性
|
||||
if (!neighbors.empty()) {
|
||||
recomputeComponentsAfterDisconnection(pieceId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::recomputeComponentsAfterDisconnection(int disconnectedPiece) {
|
||||
int oldComponentId = find(disconnectedPiece);
|
||||
if (oldComponentId = -1 || m_componentPieces[disconnectedPiece].size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取原组件中除断开棋子外的所有棋子
|
||||
std::unordered_set<int> remainingPieces = m_componentPieces[oldComponentId];
|
||||
remainingPieces.erase(disconnectedPiece);
|
||||
|
||||
//处理组件分裂
|
||||
handleComponentSplit(oldComponentId, remainingPieces);
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::handleComponentSplit(int oldComponentId, const std:: unordered_set<int>& remainingPieces ) {
|
||||
std::unordered_set<int> visited;
|
||||
std::vector<std::unordered_set<int>> newComponents;
|
||||
|
||||
//;// 对剩余棋子进行BFS,找到所有连通区域
|
||||
for (int piece : remainingPieces) {
|
||||
if (visited.find(piece) != visited.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 找到一个新的连通区域
|
||||
std::unordered_set<int> connectedRegion = bfsConnectedRegion(piece, remainingPieces);
|
||||
|
||||
// 标记为已访问
|
||||
visited.insert(connectedRegion.begin(), connectedRegion.end());
|
||||
newComponents.push_back(connectedRegion);
|
||||
}
|
||||
// 删除原组件
|
||||
m_componentPieces.erase(oldComponentId);
|
||||
|
||||
// 为每个新连通区域创建组件
|
||||
for (const auto& region : newComponents) {
|
||||
if (!region.empty()) {
|
||||
int newRoot = *region.begin();
|
||||
createNewComponent(newRoot);
|
||||
|
||||
// 设置新组件的父节点和棋子集合
|
||||
for (int piece : region) {
|
||||
m_parent[piece] = newRoot;
|
||||
m_pieceToComponent[piece] = newRoot;
|
||||
}
|
||||
m_componentPieces[newRoot] = region;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::unordered_set<int> ConnectedComponentManager::bfsConnectedRegion(int startPiece, const std::unordered_set<int>& availablepieces) {
|
||||
std::unordered_set<int> connectedRegion;
|
||||
std::queue<int> queue;
|
||||
|
||||
queue.push(startPiece);
|
||||
connectedRegion.insert(startPiece);
|
||||
|
||||
while(!queue.empty()) {
|
||||
int current = queue.front();
|
||||
queue.pop();
|
||||
// 遍历当前棋子的所有邻居
|
||||
for (int neighbor : m_adjacentList[current]) {
|
||||
// 如果邻居在可用棋子中且未被访问
|
||||
if (availablepieces.find(neighbor) != availablepieces.end() &&
|
||||
connectedRegion.find(neighbor) == connectedRegion.end()) {
|
||||
connectedRegion.insert(neighbor);
|
||||
queue.push(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return connectedRegion;
|
||||
}
|
||||
|
||||
int ConnectedComponentManager::createNewComponent(int rootPiece) {
|
||||
m_parent[rootPiece] = rootPiece;
|
||||
m_rank[rootPiece] = 0;
|
||||
m_componentPieces[rootPiece] = {rootPiece};
|
||||
m_pieceToComponent[rootPiece] = rootPiece;
|
||||
return rootPiece;
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::selectComponentByPiece(int pieceId) {
|
||||
m_slelectedComponentId = find(pieceId);
|
||||
}
|
||||
|
||||
const std::unordered_set<int>& ConnectedComponentManager::getSelectedComponent() const {
|
||||
static std::unordered_set<int> emptySet;
|
||||
if (m_slelectedComponentId == -1 ||
|
||||
m_componentPieces.find(m_slelectedComponentId) == m_componentPieces.end()) {
|
||||
|
||||
return emptySet;
|
||||
}
|
||||
return m_componentPieces.at(m_slelectedComponentId);
|
||||
}
|
||||
|
||||
int ConnectedComponentManager::getComponentId(int pieceId) const {
|
||||
auto it = m_pieceToComponent.find(pieceId);
|
||||
return (it != m_pieceToComponent.end()) ? it->second : -1;
|
||||
}
|
||||
|
||||
const std::unordered_set<int>& ConnectedComponentManager::getPiecesInComponent(int componentId) const {
|
||||
static std::unordered_set<int> emptySet;
|
||||
auto it = m_componentPieces.find(componentId);
|
||||
|
||||
return (it != m_componentPieces.end()) ? it->second : emptySet;
|
||||
}
|
||||
|
||||
bool ConnectedComponentManager::areConnected(int pieceId1, int pieceId2) {
|
||||
return find(pieceId1) == find(pieceId2);
|
||||
}
|
||||
|
||||
const std::unordered_set<int>& ConnectedComponentManager::getPieceConnections(int pieceId) const {
|
||||
static std::unordered_set<int> emptySet;
|
||||
if (pieceId < 0 || pieceId >= m_totalPieces) return emptySet;
|
||||
return m_adjacentList[pieceId];
|
||||
}
|
||||
|
||||
bool ConnectedComponentManager::areDirectlyConnected(int pieceId1, int pieceId2) const {
|
||||
if (pieceId1 < 0 || pieceId1 >= m_totalPieces ||
|
||||
pieceId2 < 0 || pieceId2 >= m_totalPieces) {
|
||||
return false;
|
||||
}
|
||||
return m_adjacentList[pieceId1].find(pieceId2) != m_adjacentList[pieceId1].end();
|
||||
}
|
||||
|
||||
void ConnectedComponentManager::clearSelection() {
|
||||
m_slelectedComponentId = -1;
|
||||
}
|
||||
|
||||
std:: unordered_map<int, std::unordered_set<int>> ConnectedComponentManager::getAllComponents() const {
|
||||
return m_componentPieces;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
70
src/core/ConnectedComponentManager.h
Normal file
70
src/core/ConnectedComponentManager.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <memory>
|
||||
|
||||
class ConnectedComponentManager {
|
||||
private:
|
||||
// 并查集的父节点数组,使用连续内存存储
|
||||
std::vector<int> m_parent;
|
||||
//记录根节点的大小(按秩合并)
|
||||
std::vector<int> m_rank;
|
||||
// 记录每个棋子的直接连接关系(邻接表)
|
||||
std::vector<std::unordered_set<int>> m_adjacentList;
|
||||
// 组件ID到棋子集合的映射
|
||||
std::unordered_map<int, std::unordered_set<int>> m_componentPieces;
|
||||
// 棋子ID到组件ID的映射
|
||||
std::unordered_map<int, int> m_pieceToComponent;
|
||||
// 当前选中的组件ID
|
||||
int m_slelectedComponentId = -1;
|
||||
// 总棋子数
|
||||
int m_totalPieces;
|
||||
|
||||
// 重新计算连通性(断开后调用)
|
||||
void recomputeComponentsAfterDisconnection(int disconnectedPiece);
|
||||
// 处理组件分裂
|
||||
void handleComponentSplit(int oldComponentId, const std::unordered_set<int>& affectedPieces);
|
||||
|
||||
// 使用BFS查找连通区域
|
||||
std::unordered_set<int> bfsConnectedRegion(int startPiece, const std::unordered_set<int>& afftedPieces);
|
||||
|
||||
// 创建新组件
|
||||
int createNewComponent(int rootPiece);
|
||||
|
||||
public:
|
||||
// 构造函数:初始化指定数量的棋子
|
||||
explicit ConnectedComponentManager(int numPieces);
|
||||
// 查找操作(带路径压缩)
|
||||
int find(int pieceId);
|
||||
// 合并两个棋子所在的组件(按秩合并)
|
||||
void unite(int pieceId1, int pieceId2);
|
||||
// 连接两个组件
|
||||
void addConnection(int pieceId1, int pieceId2);
|
||||
// 断开棋子与指定邻居的连接
|
||||
bool disconnectFromNeighbor(int pieceId, int neighborId);
|
||||
// 将棋子完全从当前组件断开,成为独立组件
|
||||
bool disconnectFromComponent(int pieceId);
|
||||
// 通过棋子选择整个连通片
|
||||
void selectComponentByPiece(int pieceId);
|
||||
// 获取当前选中的组件中的所有棋子
|
||||
const std::unordered_set<int>& getSelectedComponent() const;
|
||||
// 获取棋子所属的组件ID
|
||||
int getComponentId(int pieceId) const;
|
||||
// 获取组件内所有棋子
|
||||
const std::unordered_set<int>& getPiecesInComponent(int pieceId) const;
|
||||
// 检查两个棋子是否在同一个组件中
|
||||
bool areConnected(int pieceId1, int pieceId2);
|
||||
// 获取棋子的所有直接连接
|
||||
const std::unordered_set<int>& getPieceConnections(int pieceId) const;
|
||||
// 检查两个棋子是否直接相连
|
||||
bool areDirectlyConnected(int pieceId1, int pieceId2) const;
|
||||
// 清除选中状态
|
||||
void clearSelection();
|
||||
|
||||
//获取所有组件
|
||||
std::unordered_map<int, std::unordered_set<int>> getAllComponents() const;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Piece.h"
|
||||
|
||||
Piece::Piece(PlayerColer color) : m_color(color) {
|
||||
|
||||
}
|
||||
|
||||
Piece::~Piece() {
|
||||
|
||||
}
|
||||
|
||||
PlayerColer Piece::getPlayerColor() const {
|
||||
return m_color;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
enum class PlayerColer {
|
||||
WHITE,
|
||||
BLACK
|
||||
};
|
||||
|
||||
|
||||
class Piece {
|
||||
private:
|
||||
PlayerColer m_color;
|
||||
public:
|
||||
Piece(PlayerColer color);
|
||||
~Piece();
|
||||
PlayerColer getPlayerColor() const;
|
||||
|
||||
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
#pragma once
|
||||
@@ -1,10 +1,10 @@
|
||||
#include "Render.h"
|
||||
|
||||
Renderer::Renderer(int WIDTH, int HEIGHT) : Width(WIDTH), Height(HEIGHT) {
|
||||
Renderer::Renderer(int WIDTH, int HEIGHT) : m_Width(WIDTH), m_Height(HEIGHT) {
|
||||
|
||||
}
|
||||
|
||||
Renderer::Renderer() : Width(1600), Height(900) {
|
||||
Renderer::Renderer() : m_Width(1600), m_Height(900) {
|
||||
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ bool Renderer::initialize() {
|
||||
// 创建窗口(支持高DPI和横屏)[3,4](@ref)
|
||||
m_window = SDL_CreateWindow(
|
||||
"孢子棋", // 窗口标题,显示在标题栏上
|
||||
Width, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
||||
Height, // 窗口的逻辑高度(例如 600)
|
||||
m_Width, // 窗口的逻辑宽度(例如 800),用于统一布局,不受屏幕 DPI 影响
|
||||
m_Height, // 窗口的逻辑高度(例如 600)
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | // 启用高像素密度支持(HiDPI/Retina),确保在高分屏上画面清晰
|
||||
SDL_WINDOW_RESIZABLE // 允许用户调整窗口大小(可拉伸)
|
||||
);
|
||||
@@ -38,10 +38,10 @@ bool Renderer::initialize() {
|
||||
|
||||
// 设置逻辑呈现模式,实现分辨率自适应[3](@ref)
|
||||
SDL_SetRenderLogicalPresentation(m_renderer,
|
||||
Width,
|
||||
Height,
|
||||
m_Width,
|
||||
m_Height,
|
||||
SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
||||
SDL_SetWindowSize(m_window, Width, Height);
|
||||
SDL_SetWindowSize(m_window, m_Width, m_Height);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ class Renderer
|
||||
private:
|
||||
SDL_Window* m_window = nullptr;
|
||||
SDL_Renderer* m_renderer = nullptr;
|
||||
int Width;
|
||||
int Height;
|
||||
int m_Width;
|
||||
int m_Height;
|
||||
public:
|
||||
Renderer(int WIDTH, int HEIGHT);
|
||||
Renderer();
|
||||
|
||||
Reference in New Issue
Block a user