diff --git a/CMakeLists.txt b/CMakeLists.txt index e15ad9a..951cae3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,10 @@ set(SOURCE_FILES src/main.cpp src/core/Game.cpp src/ui/Render.cpp + src/core/Board.cpp + src/core/Piece.cpp + src/core/Rule.cpp + src/core/ComponentManager.cpp ) # 添加可执行文件 diff --git a/src/core/Board.cpp b/src/core/Board.cpp index 5ef0653..deaee71 100644 --- a/src/core/Board.cpp +++ b/src/core/Board.cpp @@ -12,41 +12,167 @@ Board::~Board() { } //将二维坐标转化成一维坐标 -const int Board::getIndex(int row, int col) { +int Board::getPieceID(int row, int col) const { + if (row < 0 || row >= m_rows || col < 0 || col >= m_cols) { + std::cout << "PieceID over!!" << std::endl; + exit(EXIT_FAILURE); + } return row * m_cols + col; } +std::pair Board::getCoordFromID(int PieceID) const { + int row = PieceID / m_cols; + int col = PieceID % m_cols; + return {row, col}; +} + +const Piece* Board::getPieceAt(int row, int col) const { + int PieceID = getPieceID(row, col); + return m_grid[PieceID].get(); +} + void Board::placePieceAt(int row, int col, PlayerID ID) { - int index = getIndex(row, col); - m_grid[index] = std::make_unique(ID); + int PieceID = getPieceID(row, col); + if (!Rule::canPlacePiece(m_grid[PieceID].get())) { + std::cout << "can't place piece at " << row << ", " << col << "\n"; + return; + } + m_grid[PieceID] = std::make_unique(ID); //创建方向数组 const int dx[] = {0, 0, -1, 1}; const int dy[] = {-1, 1, 0, 0}; + std::vector existingNeighbors; //记录附近的同色邻居 for (int i = 0; i < 4; i++) { int nx = row + dx[i]; int ny = col + dy[i]; - int pos = getIndex(nx, ny); + if (nx < 0 || nx >= m_rows || ny < 0 || ny >= m_cols) { + continue; + } + int pos = getPieceID(nx, ny); //添加邻居 - if (m_grid[pos] != nullptr && Rule::canConnect(m_grid[index]->getPieceOwner(), m_grid[pos]->getPieceOwner())) { + if (Rule::canConnect(m_grid[PieceID].get(), m_grid[pos].get())) { existingNeighbors.push_back(pos); } } // 调用componet处理一片的添加和连接操作 - m_component->addPiece(index, existingNeighbors); + m_component->addPiece(PieceID, existingNeighbors); + std::cout << "Place piece at" << row << " " << col << "\n"; } +void Board::removePieceAt(int row, int col) { + + int PieceID = getPieceID(row, col); + if (m_grid[PieceID] == nullptr) { + return; + } + m_grid[PieceID].reset(); + + m_component->removePiece(PieceID); + std::cout << "Remove piece at" << row << " " << col << "\n"; +} + bool Board::initialize() { placePieceAt(0, 0, PlayerID::P1); placePieceAt(0 ,2, PlayerID::P1); placePieceAt(6, 6, PlayerID::P2); placePieceAt(5, 5, PlayerID::P2); + return true; } -std::unique_ptr& Board::at(int row, int col) { - return m_grid[row * m_cols + col]; + +void Board::printBoard() { + //removePieceAt(0, 0); + std::vector gameBoard(m_rows); + for (int i = 0; i < m_rows * m_cols; i++) { + if (m_grid[i] == nullptr) { + gameBoard[i / m_cols] += "0"; + } else if (m_grid[i]->getPieceOwner() == PlayerID::P1) { + gameBoard[i / m_cols] += "1"; + } else { + gameBoard[i / m_cols] += "2"; + } + } + for (std::string& s : gameBoard) { + std::cout << s << std::endl; + } +} + +std::unordered_set Board::getAllPlayerComponent(PlayerID ID) const { + std::unordered_set PlayerComponentIDs; + auto allComponents = m_component->getAllComponents(); + //因为组件没有区分玩家,所以先要获取所有的组件判断组件是否是玩家的在添加进去 + for (const auto& [componentID, pieceSet] : allComponents) { + + if (pieceSet.empty()) continue; + + // 取组件中任意一个棋子(比如第一个) + int somePieceID = *pieceSet.begin(); + + // 安全检查:确保该位置有棋子 + if (m_grid[somePieceID] != nullptr && + m_grid[somePieceID]->getPieceOwner() == ID) { + PlayerComponentIDs.insert(componentID); + } + } + return PlayerComponentIDs; +} + +int Board::getComponentID(int row, int col) const{ + int pieceID = getPieceID(row, col); + int componentID = m_component->getComponentID(pieceID); + if (pieceID == -1) { + std::cout << "can't find component id\n"; + } + return componentID; +} + +std::unordered_set Board::getComponentByPieceID(int PieceID) const { + m_component->selectComponentByPiece(PieceID); + auto SelectedComponent = m_component->getSelectedComponent(); + return SelectedComponent; +} + +std::unordered_set Board::getOrthogonalNeighbors(int PieceID) const { + auto [row, col] = getCoordFromID(PieceID); + std::unordered_set neighbors; + + // 上 (row-1, col) + if (row > 0) neighbors.insert(getPieceID(row - 1, col)); + // 下 (row+1, col) + if (row < m_rows - 1) neighbors.insert(getPieceID(row + 1, col)); + // 左 (row, col-1) + if (col > 0) neighbors.insert(getPieceID(row, col - 1)); + // 右 (row, col+1) + if (col < m_cols - 1) neighbors.insert(getPieceID(row, col + 1)); + + return neighbors; + +} + +std::unordered_set Board::getSporeRange(int PieceID) const { + auto [row, col] = getCoordFromID(PieceID); + std::unordered_set SporeRegion; + const std::vector> dir = { + {1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {1, -1}, {-1, 1}, {-1, -1}, + {2, 0}, {-2, 0}, {0, 2}, {0, -2} + }; + + for (auto [dx, dy] : dir) { + int nx = row + dx; + int ny = col + dy; + if (nx < 0 || nx >= m_rows || ny < 0 || ny >= m_cols) { + continue; + } + + SporeRegion.insert(getPieceID(nx, ny)); + } + + + return SporeRegion; } \ No newline at end of file diff --git a/src/core/Board.h b/src/core/Board.h index 8458cdf..84e8aee 100644 --- a/src/core/Board.h +++ b/src/core/Board.h @@ -4,6 +4,8 @@ #include "Rule.h" #include #include +#include +#include // 用 (row, col) 表示坐标,0-based struct Position { int row; @@ -25,9 +27,28 @@ private: public: Board(int rows, int cols); ~Board(); - const int getIndex(int row, int col); - std::unique_ptr& at(int row, int col); + // 获取一维索引 + int getPieceID(int row, int col) const; + // 从一维反推二维 + std::pair getCoordFromID(int PieceID) const; + //获取棋子指针(不可修改) + const Piece* getPieceAt(int row, int col) const; + //初始化 bool initialize(); // 放置棋子 void placePieceAt(int row, int col, PlayerID ID); + //删除棋子 + void removePieceAt(int row, int col); + //打印棋盘 + void printBoard(); + //获取相同玩家棋子的所有棋子块的根节点 + std::unordered_set getAllPlayerComponent(PlayerID ID) const; + // 获取坐标对应的块的ID + int getComponentID(int row, int col) const; + // 获取组件ID对应的组件 + std::unordered_set getComponentByPieceID(int PieceID) const; + // 获取 上下左右四个相邻位置(即 4-邻域) + std::unordered_set getOrthogonalNeighbors(int PieceID) const; + // 获取扩散范围 + std::unordered_set getSporeRange(int PieceID) const; }; \ No newline at end of file diff --git a/src/core/ComponentManager.cpp b/src/core/ComponentManager.cpp index 2c605ae..9e5245b 100644 --- a/src/core/ComponentManager.cpp +++ b/src/core/ComponentManager.cpp @@ -11,10 +11,10 @@ ComponentManager::ComponentManager(int maxPossiblePieces) void ComponentManager::addPiece(int PieceID, const std::vector& adjacentPiece) { if (PieceID < 0 || PieceID >= m_maxPossiblePieces) return; - if (m_parent[PieceID] == -1) return; // 已存在 + if (m_parent[PieceID] != -1) return; // 已存在 m_parent[PieceID] = PieceID; - m_rank[PieceID] = PieceID; + m_rank[PieceID] = 0; m_componentPieces[PieceID] = {PieceID}; m_pieceToComponent[PieceID] = PieceID; @@ -24,11 +24,20 @@ void ComponentManager::addPiece(int PieceID, const std::vector& adjacentPie // 合并连通组件(unite 会自动处理是否已在同一组件) unite(PieceID, neighbor); - addConnection(PieceID, neighbor); + } } +void ComponentManager::removePiece(int PieceID) { + disconnectFromComponent(PieceID); + m_parent[PieceID] = -1; + //m_rank[PieceID] = 0; 重复设置了 + //m_componentPieces.erase(PieceID); + +} + + int ComponentManager::find(int pieceID) { if (m_parent[pieceID] != pieceID) { m_parent[pieceID] = find(m_parent[pieceID]); @@ -70,7 +79,7 @@ void ComponentManager::addConnection(int pieceID1, int pieceID2) { m_adjacentList[pieceID2].insert(pieceID1); } - +/* bool ComponentManager::disconnectFromNeighbor(int pieceID, int neighborID){ // 检查是否真的相连 if (!areDirectlyConnected(pieceID, neighborID)) { @@ -84,7 +93,7 @@ bool ComponentManager::disconnectFromNeighbor(int pieceID, int neighborID){ return true; } - +*/ bool ComponentManager::disconnectFromComponent(int pieceID) { int oldComponentID = find(pieceID); if (oldComponentID == -1) return false; @@ -94,33 +103,44 @@ bool ComponentManager::disconnectFromComponent(int pieceID) { // 断开所有连接 for (int neighborID : neighbors) { - m_adjacentList[pieceID].erase(neighborID); + m_adjacentList[neighborID].erase(pieceID); } - - // 将被断开的棋子设为独立组件 + // 删除pieceID的邻接表 + m_adjacentList[pieceID].clear(); + /* // 将被断开的棋子设为独立组件 m_parent[pieceID] = pieceID; m_rank[pieceID] = 0; m_componentPieces[pieceID] = {pieceID}; m_pieceToComponent[pieceID] = pieceID; +*/ + // 从原组件中移除 pieceID + if (m_componentPieces.count(oldComponentID)) { + m_componentPieces[oldComponentID].erase(pieceID); + } + m_pieceToComponent.erase(pieceID); // 如果原组件还有其他棋子,需要重新计算连通性 - if (!neighbors.empty()) { - recomputeComponentsAfterDisconnection(pieceID); + if (!m_componentPieces[oldComponentID].empty()) { + recomputeComponentsAfterDisconnection(oldComponentID, m_componentPieces[oldComponentID]); + } else { + m_componentPieces.erase(oldComponentID); } return true; } -void ComponentManager::recomputeComponentsAfterDisconnection(int disconnectedPiece) { - int oldComponentID = find(disconnectedPiece); - if (oldComponentID == -1 || m_componentPieces[disconnectedPiece].size() <= 1) { +void ComponentManager::recomputeComponentsAfterDisconnection(int oldComponentID, const std::unordered_set& remainingPieces) { + + + + // 安全检查:remainingPieces 至少要有 2 个棋子才可能分裂 + if (remainingPieces.size() <= 1) { + // 如果只剩 0 或 1 个棋子,不需要分裂 + // (如果剩 1 个,它自己就是一个新组件;如果为 0,调用方应已处理) return; } - // 获取原组件中除断开棋子外的所有棋子 - std::unordered_set remainingPieces = m_componentPieces[oldComponentID]; - remainingPieces.erase(disconnectedPiece); - + //处理组件分裂 handleComponentSplit(oldComponentID, remainingPieces); } @@ -246,6 +266,7 @@ void ComponentManager::clearSelection() { std:: unordered_map> ComponentManager::getAllComponents() const { return m_componentPieces; + } diff --git a/src/core/ComponentManager.h b/src/core/ComponentManager.h index d3af0eb..941977d 100644 --- a/src/core/ComponentManager.h +++ b/src/core/ComponentManager.h @@ -23,7 +23,7 @@ private: int m_maxPossiblePieces; // 重新计算连通性(断开后调用) - void recomputeComponentsAfterDisconnection(int disconnectedPiece); + void recomputeComponentsAfterDisconnection(int oldComponentID, const std::unordered_set& remainingPieces); // 处理组件分裂 void handleComponentSplit(int oldComponentID, const std::unordered_set& affectedPieces); @@ -38,6 +38,8 @@ public: explicit ComponentManager(int maxPossiblePieces); //添加棋子 void addPiece(int PieceID, const std::vector& adjacentPiece); + //移除棋子 + void removePiece(int PieceID); // 查找操作(带路径压缩) int find(int pieceID); // 合并两个棋子所在的组件(按秩合并) diff --git a/src/core/Game.cpp b/src/core/Game.cpp index 6342728..0b1ead1 100644 --- a/src/core/Game.cpp +++ b/src/core/Game.cpp @@ -4,7 +4,7 @@ Game::Game() { - m_board = std::make_unique(); + m_board = std::make_unique(7, 7); } @@ -23,6 +23,7 @@ bool Game::initialize() { if (!m_board->initialize()) { return false; } + m_board->printBoard(); @@ -32,5 +33,67 @@ bool Game::initialize() { return true; } +void Game::resetActionableComponents() { + + m_actionableComponents = m_board->getAllPlayerComponent(m_currentPlayer); +} +bool Game::playerSelectPiece(int row, int col) { + if (Rule::canbeSelect(m_board->getPieceAt(row, col), m_currentPlayer)) { + /*if (m_seletedPiece == std::nullopt) { + m_seletedPiece = {row, col}; + }*/ + m_seletedPiece = {row, col}; + } + return true; +} + +void Game::setPlayerAction(ActionType type) { + m_currentActionType = type; +} + + +void Game::executeAction(int toRow, int toCol) { + auto [fromRow, fromCol] = *m_seletedPiece; + if (m_currentActionType == ActionType::GROW) { + if (Rule::canGrow(m_board.get(), fromRow, fromCol, toRow, toCol, m_currentPlayer)) { + m_board->placePieceAt(toRow, toCol, m_currentPlayer); + // 如果执行了操作就擦除 + markComponentAsUsed(m_board->getComponentID(fromRow, fromCol)); + return; + } + } + if (m_currentActionType == ActionType::MOVE) { + if (Rule::canMove(m_board.get(), fromRow, fromCol, toRow, toCol, m_currentPlayer)) { + m_board->removePieceAt(fromRow, fromCol); + m_board->removePieceAt(toRow, toCol); + m_board->placePieceAt(toRow, toCol, m_currentPlayer); + + markComponentAsUsed(m_board->getComponentID(fromRow, fromCol)); + return; + } + } + if (m_currentActionType == ActionType::SPORE) { + if (Rule::canSpore(m_board.get(), fromRow, fromCol, toRow, toCol, m_currentPlayer)) { + m_board->removePieceAt(fromRow, fromCol); + m_board->placePieceAt(toRow, toCol, m_currentPlayer); + + markComponentAsUsed(m_board->getComponentID(fromRow, fromCol)); + return; + } + } + +} + +void Game::markComponentAsUsed(int componentID) { + m_actionableComponents.erase(componentID); +} + +PlayerID Game::getCurrentPlayer() const { + return m_currentPlayer; +} + +void Game::printBoard() const { + m_board->printBoard(); +} diff --git a/src/core/Game.h b/src/core/Game.h index 277039a..17b8586 100644 --- a/src/core/Game.h +++ b/src/core/Game.h @@ -1,7 +1,9 @@ // src/core/Game.h #pragma once -#include +#include +#include #include + #include "Board.h" @@ -10,14 +12,28 @@ class Game { private: std::unique_ptr m_board; - - - + PlayerID m_currentPlayer = PlayerID::P1; + ActionType m_currentActionType = ActionType::GROW; + std::optional> m_seletedPiece; //表示“可能有值,也可能没有值” + std::unordered_set m_actionableComponents;; + // 如果操作执行成功就从 + void markComponentAsUsed(int componentID); public: Game(); ~Game(); void cleanup(); bool initialize(); - + //获取当前玩家的拥有的组件 + void resetActionableComponents(); + // 获取用户选择的棋子 + bool playerSelectPiece(int row, int col); + // 设置行动类型 + void setPlayerAction(ActionType type); + // 执行玩家的行动 + void executeAction(int row, int col); + // 获取当前玩家 + PlayerID getCurrentPlayer() const; + // 打印棋盘 + void printBoard() const; }; \ No newline at end of file diff --git a/src/core/GameTypes.h b/src/core/GameTypes.h index 1915ccc..60da370 100644 --- a/src/core/GameTypes.h +++ b/src/core/GameTypes.h @@ -1,6 +1,16 @@ #pragma once // 这里存储游戏的各种状态 + + + enum class PlayerID { P1, P2 }; + +enum class ActionType { + GROW, + MOVE, + SPORE +}; + diff --git a/src/core/Rule.cpp b/src/core/Rule.cpp index 6929121..fd92276 100644 --- a/src/core/Rule.cpp +++ b/src/core/Rule.cpp @@ -1,10 +1,116 @@ #include "Rule.h" +#include "Piece.h" +#include "Board.h" - -bool Rule::canConnect(const PlayerID a, const PlayerID b) { - if (a == b) { - return true; - } - return false; +bool Rule::canConnect(const Piece* a, const Piece* b) { + if (a == nullptr || b == nullptr) { + return false; + } + if (a->getPieceOwner() != b->getPieceOwner()) { + return false; + } + return true; +} + +bool Rule::canPlacePiece(const Piece* piecePos) { + if (piecePos != nullptr) { + return false; + } + return true; +} + +bool Rule::canbeSelect(const Piece* PieceID, PlayerID ID) { + if (PieceID->getPieceOwner() != ID) { + return false; + } + return true; +} + +bool Rule::canGrow(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player) { + + + + // 检查是否有棋子并且属于自己 + const Piece* fromPiece = board->getPieceAt(fromRow, fromCol); + std::cout << "fromRow fromCol = " << fromRow << " " << fromCol << "\n"; + std::cout << "toRow toCol = " << toRow << " " << toCol << "\n"; + if (fromPiece == nullptr || fromPiece->getPieceOwner() != player) { + std::cout << "not belong to player\n"; + return false; + } + + + if (board->getPieceAt(toRow, toCol) != nullptr) { + return false; // 有棋子不能生长 + } + + int fromPieceID = board->getPieceID(fromRow, fromCol); + int toPieceID = board->getPieceID(toRow, toCol); + //std::cout << "fromPieceID= " << fromPieceID << ", toPieceID= " << toPieceID << "\n"; + auto selectedComponent = board->getComponentByPieceID(fromPieceID); + std::cout << "before action Component size: " << selectedComponent.size() << "\n"; + for (auto PieceID : selectedComponent) { + auto Neighbors = board->getOrthogonalNeighbors(PieceID); + if (Neighbors.find(toPieceID) != Neighbors.end()) { + return true; + } + } + return false; +} + +bool Rule::canMove(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player) { + + // 检查是否有棋子并且属于自己 + const Piece* fromPiece = board->getPieceAt(fromRow, fromCol); + std::cout << "fromRow fromCol = " << fromRow << " " << fromCol << "\n"; + std::cout << "toRow toCol = " << toRow << " " << toCol << "\n"; + if (fromPiece == nullptr || fromPiece->getPieceOwner() != player) { + return false; + } + // 检查是否是对方棋子或者空格 + const Piece* toPiece = board->getPieceAt(toRow, toCol); + if (toPiece != nullptr && toPiece->getPieceOwner() == player) { + return false; + } + + int fromPieceID = board->getPieceID(fromRow, fromCol); + int toPieceID = board->getPieceID(toRow, toCol); + auto selectedComponent = board->getComponentByPieceID(fromPieceID); + std::cout << "before action Component size: " << selectedComponent.size() << "\n"; + for (auto PieceID : selectedComponent) { + auto Neighbors = board->getOrthogonalNeighbors(PieceID); + if (Neighbors.find(toPieceID) != Neighbors.end()) { + return true; + } + } + return false; +} + +bool Rule::canSpore(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player) { + // 检查是否有棋子并且属于自己 + const Piece* fromPiece = board->getPieceAt(fromRow, fromCol); + std::cout << "fromRow fromCol = " << fromRow << " " << fromCol << "\n"; + std::cout << "toRow toCol = " << toRow << " " << toCol << "\n"; + if (fromPiece == nullptr || fromPiece->getPieceOwner() != player) { + return false; + } + + + if (board->getPieceAt(toRow, toCol) != nullptr) { + return false; // 有棋子不能扩散 + } + + int fromPieceID = board->getPieceID(fromRow, fromCol); + int toPieceID = board->getPieceID(toRow, toCol); + auto selectedComponent = board->getComponentByPieceID(fromPieceID); + std::cout << "before action Component size: " << selectedComponent.size() << "\n"; + for (auto PieceID : selectedComponent) { + auto SporeRegion = board->getSporeRange(PieceID); + if (SporeRegion.find(toPieceID) != SporeRegion.end()) { + return true; + } + } + return false; + } \ No newline at end of file diff --git a/src/core/Rule.h b/src/core/Rule.h index a249d71..61bd9d9 100644 --- a/src/core/Rule.h +++ b/src/core/Rule.h @@ -1,10 +1,14 @@ #pragma once #include "GameTypes.h" - - +class Piece; // 前向声明 +class Board; class Rule { public: - static bool canConnect(const PlayerID a, PlayerID b); - + static bool canConnect(const Piece* a, const Piece* b); + static bool canPlacePiece(const Piece* piecePos); + static bool canbeSelect(const Piece* PieceID, const PlayerID ID); + static bool canGrow(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player); + static bool canMove(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player); + static bool canSpore(const Board* board, const int fromRow, const int fromCol, const int toRow, const int toCol, const PlayerID player); }; \ No newline at end of file diff --git a/src/core/testcore.cpp b/src/core/testcore.cpp new file mode 100644 index 0000000..f386da3 --- /dev/null +++ b/src/core/testcore.cpp @@ -0,0 +1,32 @@ +// main.cpp +#include "Game.h" +#include + +int main() { + Game game; + if (!game.initialize()) { + std::cerr << "Failed to initialize game.\n"; + return -1; + } + + // 简单控制台循环 + while (true) { + std::cout << "Current player: " + << (game.getCurrentPlayer() == PlayerID::P1 ? "P1" : "P2") << "\n"; + std::cout << "Action type (0=GROW, 1=MOVE, 2=SPORE): "; + int act; + std::cin >> act; + game.setPlayerAction(static_cast(act)); + + std::cout << "Select piece (row col): "; + int r, c; + std::cin >> r >> c; + game.playerSelectPiece(r, c); + + std::cout << "Target position (row col): "; + std::cin >> r >> c; + game.executeAction(r, c); + + game.printBoard(); + } +} \ No newline at end of file