diff --git a/NineChess/ninechess.pro b/NineChess/ninechess.pro index 92feeb95..469b12be 100644 --- a/NineChess/ninechess.pro +++ b/NineChess/ninechess.pro @@ -68,9 +68,9 @@ DISTFILES += \ RC_FILE += NineChess.rc -# With C++14 support +# With C++17 support greaterThan(QT_MAJOR_VERSION, 4) { -CONFIG += c++14 +CONFIG += c++17 } else { QMAKE_CXXFLAGS += -std=c++0x } diff --git a/NineChess/src/MemoryPool.h b/NineChess/src/MemoryPool.h index 8f4d56ac..974d087e 100644 --- a/NineChess/src/MemoryPool.h +++ b/NineChess/src/MemoryPool.h @@ -25,6 +25,7 @@ #include #include +#include template class MemoryPool diff --git a/NineChess/src/MemoryPool.tcc b/NineChess/src/MemoryPool.tcc index e815381f..859cf686 100644 --- a/NineChess/src/MemoryPool.tcc +++ b/NineChess/src/MemoryPool.tcc @@ -23,7 +23,7 @@ #ifndef MEMORY_BLOCK_TCC #define MEMORY_BLOCK_TCC - +#include template inline typename MemoryPool::size_type diff --git a/NineChess/src/aithread.cpp b/NineChess/src/aithread.cpp index 512543b9..d2108e25 100644 --- a/NineChess/src/aithread.cpp +++ b/NineChess/src/aithread.cpp @@ -25,8 +25,8 @@ AiThread::AiThread(int id, QObject *parent) : QThread(parent), - chess_(nullptr), waiting_(false), + chess_(nullptr), aiDepth(2), aiTime(120) { @@ -41,11 +41,11 @@ AiThread::AiThread(int id, QObject *parent) : // 连接定时器处理函数 connect(&timer, &QTimer::timeout, this, &AiThread::act, Qt::QueuedConnection); - // 网络 + // 网络 if (id == 1) { - server = new Server(nullptr, 30001); - uint16_t clientPort = server->getPort() == 30001 ? 30002 : 30001; + server = new Server(nullptr, 30001); + uint16_t clientPort = server->getPort() == 30001 ? 30002 : 30001; client = new Client(nullptr, clientPort); } } @@ -53,7 +53,7 @@ AiThread::AiThread(int id, QObject *parent) : AiThread::~AiThread() { // 网络相关 - //delete server; + //delete server; //delete client; stop(); @@ -78,7 +78,7 @@ void AiThread::setAi(const NineChess &chess) mutex.unlock(); } -void AiThread::setAi(const NineChess &chess, int depth, int time) +void AiThread::setAi(const NineChess &chess, NineChessAi_ab::depth_t depth, int time) { mutex.lock(); this->chess_ = &chess; diff --git a/NineChess/src/aithread.h b/NineChess/src/aithread.h index a870de50..1010de73 100644 --- a/NineChess/src/aithread.h +++ b/NineChess/src/aithread.h @@ -37,7 +37,7 @@ class AiThread : public QThread public: explicit AiThread(int id, QObject *parent = nullptr); - ~AiThread(); + ~AiThread() override; signals: // 着法信号 @@ -55,7 +55,7 @@ protected: public: // AI设置 void setAi(const NineChess &chess); - void setAi(const NineChess &chess, int depth, int time); + void setAi(const NineChess &chess, NineChessAi_ab::depth_t depth, int time); Server *getServer() { @@ -68,7 +68,7 @@ public: } // 深度和限时 - void getDepthTime(int &depth, int &time) + void getDepthTime(NineChessAi_ab::depth_t &depth, int &time) { depth = aiDepth; time = aiTime; @@ -113,7 +113,7 @@ private: NineChessAi_ab ai_ab; // AI的层数 - int aiDepth; + NineChessAi_ab::depth_t aiDepth; // AI的限时 int aiTime; diff --git a/NineChess/src/client.cpp b/NineChess/src/client.cpp index c9d2c0e5..00e2db0e 100644 --- a/NineChess/src/client.cpp +++ b/NineChess/src/client.cpp @@ -1,21 +1,21 @@ -/***************************************************************************** - * Copyright (C) 2019 NineChess authors - * - * Authors: Calcitem - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. +/***************************************************************************** + * Copyright (C) 2019 NineChess authors + * + * Authors: Calcitem + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ #include @@ -125,7 +125,7 @@ void Client::requestNewAction() getActionButton->setEnabled(false); tcpSocket->abort(); tcpSocket->connectToHost(hostCombo->currentText(), - portLineEdit->text().toInt()); + portLineEdit->text().toUShort()); } void Client::readAction() @@ -140,7 +140,6 @@ void Client::readAction() currentAction = nextAction; statusLabel->setText(currentAction); - // ź emit command(currentAction); getActionButton->setEnabled(true); diff --git a/NineChess/src/config.h b/NineChess/src/config.h index 20650e2c..16c6d87d 100644 --- a/NineChess/src/config.h +++ b/NineChess/src/config.h @@ -21,6 +21,10 @@ #ifndef CONFIG_H #define CONFIG_H +#if _MSC_VER >= 1600 +#pragma execution_character_set("utf-8") +#endif + //#define DEBUG_MODE //#define DEBUG_MODE_A diff --git a/NineChess/src/gamecontroller.cpp b/NineChess/src/gamecontroller.cpp index 665837c4..14148c9f 100644 --- a/NineChess/src/gamecontroller.cpp +++ b/NineChess/src/gamecontroller.cpp @@ -19,10 +19,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#if _MSC_VER >= 1600 -#pragma execution_character_set("utf-8") -#endif - #include #include #include @@ -42,6 +38,8 @@ GameController::GameController(GameScene & scene, QObject * parent) : QObject(parent), + ai1(1), + ai2(2), scene(scene), currentPiece(nullptr), currentRow(-1), @@ -56,8 +54,6 @@ GameController::GameController(GameScene & scene, QObject * parent) : ruleNo_(-1), timeLimit(0), stepsLimit(50), - ai1(1), - ai2(2), score1(-2), score2(-2) { @@ -316,7 +312,7 @@ void GameController::setEngine2(bool arg) } } -void GameController::setAiDepthTime(int depth1, int time1, int depth2, int time2) +void GameController::setAiDepthTime(NineChessAi_ab::depth_t depth1, int time1, NineChessAi_ab::depth_t depth2, int time2) { if (isEngine1) { ai1.stop(); @@ -338,7 +334,7 @@ void GameController::setAiDepthTime(int depth1, int time1, int depth2, int time2 } } -void GameController::getAiDepthTime(int &depth1, int &time1, int &depth2, int &time2) +void GameController::getAiDepthTime(NineChessAi_ab::depth_t &depth1, int &time1, NineChessAi_ab::depth_t &depth2, int &time2) { ai1.getDepthTime(depth1, time1); ai2.getDepthTime(depth2, time2); diff --git a/NineChess/src/gamecontroller.h b/NineChess/src/gamecontroller.h index 4f1be7c1..81a9f0d2 100644 --- a/NineChess/src/gamecontroller.h +++ b/NineChess/src/gamecontroller.h @@ -90,8 +90,8 @@ public: return &manualListModel; } - void setAiDepthTime(int depth1, int time1, int depth2, int time2); - void getAiDepthTime(int &depth1, int &time1, int &depth2, int &time2); + void setAiDepthTime(NineChessAi_ab::depth_t depth1, int time1, NineChessAi_ab::depth_t depth2, int time2); + void getAiDepthTime(NineChessAi_ab::depth_t &depth1, int &time1, NineChessAi_ab::depth_t &depth2, int &time2); signals: @@ -231,10 +231,10 @@ private: int stepsLimit; // 玩家1剩余时间(毫秒) - int remainingTime1; + long remainingTime1; // 玩家2剩余时间(毫秒) - int remainingTime2; + long remainingTime2; // 玩家1赢盘数 int score1; diff --git a/NineChess/src/gamescene.cpp b/NineChess/src/gamescene.cpp index e1431258..7d54400b 100644 --- a/NineChess/src/gamescene.cpp +++ b/NineChess/src/gamescene.cpp @@ -32,11 +32,11 @@ GameScene::GameScene(QObject *parent) : QGraphicsScene(parent), - board(nullptr), pos_p1(LINE_INTERVAL * 4, LINE_INTERVAL * 6), pos_p1_g(LINE_INTERVAL *(-4), LINE_INTERVAL * 6), pos_p2(LINE_INTERVAL *(-4), LINE_INTERVAL *(-6)), - pos_p2_g(LINE_INTERVAL * 4, LINE_INTERVAL *(-6)) + pos_p2_g(LINE_INTERVAL * 4, LINE_INTERVAL *(-6)), + board(nullptr) { // 添加棋盘 board = new BoardItem; diff --git a/NineChess/src/hashmap.h b/NineChess/src/hashmap.h index 88f60c70..e5cee791 100644 --- a/NineChess/src/hashmap.h +++ b/NineChess/src/hashmap.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include "HashNode.h" @@ -91,10 +91,10 @@ namespace CTSL //Concurrent Thread Safe Library //Function to remove an entry from the bucket, if found void erase(const K &key) { - size_t hashValue = hashFn(key) & (hashSize - 1); #ifdef DISABLE_HASHBUCKET // std::unique_lock lock(mutex_); #else + size_t hashValue = hashFn(key) & (hashSize - 1); hashTable[hashValue].erase(key); #endif } @@ -117,9 +117,9 @@ namespace CTSL //Concurrent Thread Safe Library void dump(const QString &filename) { #ifdef DISABLE_HASHBUCKET - QFile file(filename); - file.open(QIODevice::WriteOnly); - file.write((char *)hashTable, sizeof(HashNode) * hashSize); + QFile file(filename); + file.open(QIODevice::WriteOnly); + file.write((char *)hashTable, sizeof(HashNode) * hashSize); file.close(); #endif } @@ -128,9 +128,9 @@ namespace CTSL //Concurrent Thread Safe Library void load(const QString &filename) { #ifdef DISABLE_HASHBUCKET - QFile file(filename); - file.open(QIODevice::ReadOnly); - file.read((char *)hashTable, sizeof(HashNode) * hashSize); + QFile file(filename); + file.open(QIODevice::ReadOnly); + file.read((char *)hashTable, sizeof(HashNode) * hashSize); file.close(); #endif } diff --git a/NineChess/src/ninechess.cpp b/NineChess/src/ninechess.cpp index 3cb79b08..1e03ce09 100644 --- a/NineChess/src/ninechess.cpp +++ b/NineChess/src/ninechess.cpp @@ -19,10 +19,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#if _MSC_VER >= 1600 -#pragma execution_character_set("utf-8") -#endif - #include #include "ninechess.h" #include "ninechessai_ab.h" @@ -129,10 +125,10 @@ const int NineChess::onBoard[(N_RINGS + 2) * N_SEATS] = { }; // 着法表 -int NineChess::moveTable[N_POINTS][N_MOVE_DIRECTIONS] = { 0 }; +int NineChess::moveTable[N_POINTS][N_MOVE_DIRECTIONS] = {{0}}; // 成三表 -int NineChess::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = { 0 }; +int NineChess::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = {{{0}}}; NineChess::NineChess() { @@ -667,373 +663,373 @@ int NineChess::cp2pos(int c, int p) return c * N_SEATS + p - 1; } - -bool NineChess::place(int pos, long time_p, bool cp) -{ - // 如果局面为“结局”,返回false - if (context.stage == GAME_OVER) - return false; - - // 如果局面为“未开局”,则开局 - if (context.stage == GAME_NOTSTARTED) - start(); - - // 如非“落子”状态,返回false - if (context.action != ACTION_PLACE) - return false; - - // 如果落子位置在棋盘外、已有子点或禁点,返回false - if (!onBoard[pos] || board_[pos]) - return false; - - // 格式转换 - int c = 0; - int p = 0; - pos2cp(pos, c, p); - - // 时间的临时变量 - long player_ms = -1; - - // 对于开局落子 - int piece = '\x00'; - int n = 0; - - if (context.stage == GAME_PLACING) { - // 先手下 - if (context.turn == PLAYER1) { - piece = '\x11' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_1; - context.nPiecesInHand_1--; - context.nPiecesOnBoard_1++; - } - // 后手下 - else { - piece = '\x21' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_2; - context.nPiecesInHand_2--; - context.nPiecesOnBoard_2++; - } - - board_[pos] = piece; - -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - updateHash(pos); -#endif - move_ = pos; - - if (cp == true) { - player_ms = update(time_p); - sprintf(cmdline, "(%1u,%1u) %02u:%02u.%03u", - c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); - cmdlist.push_back(string(cmdline)); - currentStep++; - } - - currentPos = pos; - - // 如果决出胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - - n = addMills(currentPos); - - // 开局阶段未成三 - if (n == 0) { - // 如果双方都无未放置的棋子 - if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) { - // 进入中局阶段 - context.stage = GAME_MOVING; - - // 进入选子状态 - context.action = ACTION_CHOOSE; - - // 清除禁点 - cleanForbiddenPoints(); - - // 设置轮到谁走 - if (currentRule.isDefenderMoveFirst) { - context.turn = PLAYER2; - } else { - context.turn = PLAYER1; - } - - // 再决胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - } - // 如果双方还有子 - else { - // 设置轮到谁走 - changeTurn(); - } - } - // 如果成三 - else { - // 设置去子数目 - context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1; - - // 进入去子状态 - context.action = ACTION_CAPTURE; - } - - if (cp == true) { - setTips(); - } - - return true; - } - - // 对于中局落子 - else if (context.stage == GAME_MOVING) { - // 如果落子不合法 - if ((context.turn == PLAYER1 && - (context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || - (context.turn == PLAYER2 && - (context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { - - int i; - for (i = 0; i < 4; i++) { - if (pos == moveTable[currentPos][i]) - break; - } - - // 不在着法表中 - if (i == 4) - return false; - } - - // 移子 - move_ = (currentPos << 8) + pos; - if (cp == true) { - player_ms = update(time_p); - sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u.%03u", currentPos / N_SEATS, currentPos % N_SEATS + 1, - c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); - cmdlist.push_back(string(cmdline)); - currentStep++; - moveStep++; - } - board_[pos] = board_[currentPos]; -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - updateHash(pos); -#endif - board_[currentPos] = '\x00'; -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - revertHash(currentPos); -#endif - currentPos = pos; - n = addMills(currentPos); - - // 中局阶段未成三 - if (n == 0) { - // 进入选子状态 - context.action = ACTION_CHOOSE; - - // 设置轮到谁走 - changeTurn(); - - // 如果决出胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - } - // 中局阶段成三 - else { - // 设置去子数目 - context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1; - - // 进入去子状态 - context.action = ACTION_CAPTURE; - if (cp == true) { - setTips(); - } - } - - if (cp == true) { - setTips(); - } - - return true; - } - - return false; + +bool NineChess::place(int pos, long time_p, bool cp) +{ + // 如果局面为“结局”,返回false + if (context.stage == GAME_OVER) + return false; + + // 如果局面为“未开局”,则开局 + if (context.stage == GAME_NOTSTARTED) + start(); + + // 如非“落子”状态,返回false + if (context.action != ACTION_PLACE) + return false; + + // 如果落子位置在棋盘外、已有子点或禁点,返回false + if (!onBoard[pos] || board_[pos]) + return false; + + // 格式转换 + int c = 0; + int p = 0; + pos2cp(pos, c, p); + + // 时间的临时变量 + long player_ms = -1; + + // 对于开局落子 + int piece = '\x00'; + int n = 0; + + if (context.stage == GAME_PLACING) { + // 先手下 + if (context.turn == PLAYER1) { + piece = '\x11' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_1; + context.nPiecesInHand_1--; + context.nPiecesOnBoard_1++; + } + // 后手下 + else { + piece = '\x21' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_2; + context.nPiecesInHand_2--; + context.nPiecesOnBoard_2++; + } + + board_[pos] = piece; + +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + updateHash(pos); +#endif + move_ = pos; + + if (cp == true) { + player_ms = update(time_p); + sprintf(cmdline, "(%1u,%1u) %02u:%02u.%03u", + c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); + cmdlist.push_back(string(cmdline)); + currentStep++; + } + + currentPos = pos; + + // 如果决出胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + + n = addMills(currentPos); + + // 开局阶段未成三 + if (n == 0) { + // 如果双方都无未放置的棋子 + if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) { + // 进入中局阶段 + context.stage = GAME_MOVING; + + // 进入选子状态 + context.action = ACTION_CHOOSE; + + // 清除禁点 + cleanForbiddenPoints(); + + // 设置轮到谁走 + if (currentRule.isDefenderMoveFirst) { + context.turn = PLAYER2; + } else { + context.turn = PLAYER1; + } + + // 再决胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + } + // 如果双方还有子 + else { + // 设置轮到谁走 + changeTurn(); + } + } + // 如果成三 + else { + // 设置去子数目 + context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1; + + // 进入去子状态 + context.action = ACTION_CAPTURE; + } + + if (cp == true) { + setTips(); + } + + return true; + } + + // 对于中局落子 + else if (context.stage == GAME_MOVING) { + // 如果落子不合法 + if ((context.turn == PLAYER1 && + (context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) || + (context.turn == PLAYER2 && + (context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) { + + int i; + for (i = 0; i < 4; i++) { + if (pos == moveTable[currentPos][i]) + break; + } + + // 不在着法表中 + if (i == 4) + return false; + } + + // 移子 + move_ = (currentPos << 8) + pos; + if (cp == true) { + player_ms = update(time_p); + sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u.%03u", currentPos / N_SEATS, currentPos % N_SEATS + 1, + c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); + cmdlist.push_back(string(cmdline)); + currentStep++; + moveStep++; + } + board_[pos] = board_[currentPos]; +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + updateHash(pos); +#endif + board_[currentPos] = '\x00'; +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + revertHash(currentPos); +#endif + currentPos = pos; + n = addMills(currentPos); + + // 中局阶段未成三 + if (n == 0) { + // 进入选子状态 + context.action = ACTION_CHOOSE; + + // 设置轮到谁走 + changeTurn(); + + // 如果决出胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + } + // 中局阶段成三 + else { + // 设置去子数目 + context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1; + + // 进入去子状态 + context.action = ACTION_CAPTURE; + if (cp == true) { + setTips(); + } + } + + if (cp == true) { + setTips(); + } + + return true; + } + + return false; } -bool NineChess::place(int c, int p, long time_p) -{ - // 转换为 pos - int pos = cp2pos(c, p); - - return place(pos, time_p, true); +bool NineChess::place(int c, int p, long time_p) +{ + // 转换为 pos + int pos = cp2pos(c, p); + + return place(pos, time_p, true); } -bool NineChess::capture(int c, int p, long time_p) -{ - // 转换为 pos - int pos = cp2pos(c, p); - - return capture(pos, time_p, true); -} - -bool NineChess::capture(int pos, long time_p, bool cp) -{ - // 如果局面为"未开局"或“结局”,返回false - if (context.stage == GAME_NOTSTARTED || context.stage == GAME_OVER) - return false; - - // 如非“去子”状态,返回false - if (context.action != ACTION_CAPTURE) - return false; - - // 如果去子完成,返回false - if (context.nPiecesNeedRemove <= 0) - return false; - - // 格式转换 - int c = 0; - int p = 0; - pos2cp(pos, c, p); - - // 时间的临时变量 - long player_ms = -1; - - // 对手 - char opponent = context.turn == PLAYER1 ? 0x20 : 0x10; - - // 判断去子是不是对手棋 - if (!(opponent & board_[pos])) - return false; - - // 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中 - if (currentRule.allowRemoveMill == false && - isInMills(pos) && !isAllInMills(opponent)) { - return false; - } - - // 去子(设置禁点) - if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING) { -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - revertHash(pos); -#endif - board_[pos] = '\x0f'; -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - updateHash(pos); -#endif - } else { // 去子 -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - revertHash(pos); -#endif - board_[pos] = '\x00'; - } - - if (context.turn == PLAYER1) - context.nPiecesOnBoard_2--; - else if (context.turn == PLAYER2) - context.nPiecesOnBoard_1--; - - move_ = -pos; - if (cp == true) { - player_ms = update(time_p); - sprintf(cmdline, "-(%1u,%1u) %02u:%02u.%03u", c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); - cmdlist.push_back(string(cmdline)); - currentStep++; - moveStep = 0; - } - currentPos = 0; - context.nPiecesNeedRemove--; -#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) - updateHash(pos); -#endif - - // 去子完成 - - // 如果决出胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - - // 还有其余的子要去吗 - if (context.nPiecesNeedRemove > 0) { - // 继续去子 - return true; - } - // 所有去子都完成了 - else { - // 开局阶段 - if (context.stage == GAME_PLACING) { - // 如果双方都无未放置的棋子 - if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) { - - // 进入中局阶段 - context.stage = GAME_MOVING; - - // 进入选子状态 - context.action = ACTION_CHOOSE; - - // 清除禁点 - cleanForbiddenPoints(); - - // 设置轮到谁走 - if (currentRule.isDefenderMoveFirst) { - context.turn = PLAYER2; - } else { - context.turn = PLAYER1; - } - - // 再决胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - } - // 如果双方还有子 - else { - // 进入落子状态 - context.action = ACTION_PLACE; - // 设置轮到谁走 - changeTurn(); - // 如果决出胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - } - } - // 中局阶段 - else { - // 进入选子状态 - context.action = ACTION_CHOOSE; - // 设置轮到谁走 - changeTurn(); - // 如果决出胜负 - if (win()) { - if (cp == true) { - setTips(); - } - return true; - } - } - } - - if (cp == true) { - setTips(); - } - return true; +bool NineChess::capture(int c, int p, long time_p) +{ + // 转换为 pos + int pos = cp2pos(c, p); + + return capture(pos, time_p, true); +} + +bool NineChess::capture(int pos, long time_p, bool cp) +{ + // 如果局面为"未开局"或“结局”,返回false + if (context.stage == GAME_NOTSTARTED || context.stage == GAME_OVER) + return false; + + // 如非“去子”状态,返回false + if (context.action != ACTION_CAPTURE) + return false; + + // 如果去子完成,返回false + if (context.nPiecesNeedRemove <= 0) + return false; + + // 格式转换 + int c = 0; + int p = 0; + pos2cp(pos, c, p); + + // 时间的临时变量 + long player_ms = -1; + + // 对手 + char opponent = context.turn == PLAYER1 ? 0x20 : 0x10; + + // 判断去子是不是对手棋 + if (!(opponent & board_[pos])) + return false; + + // 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中 + if (currentRule.allowRemoveMill == false && + isInMills(pos) && !isAllInMills(opponent)) { + return false; + } + + // 去子(设置禁点) + if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING) { +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + revertHash(pos); +#endif + board_[pos] = '\x0f'; +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + updateHash(pos); +#endif + } else { // 去子 +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + revertHash(pos); +#endif + board_[pos] = '\x00'; + } + + if (context.turn == PLAYER1) + context.nPiecesOnBoard_2--; + else if (context.turn == PLAYER2) + context.nPiecesOnBoard_1--; + + move_ = -pos; + if (cp == true) { + player_ms = update(time_p); + sprintf(cmdline, "-(%1u,%1u) %02u:%02u.%03u", c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000); + cmdlist.push_back(string(cmdline)); + currentStep++; + moveStep = 0; + } + currentPos = 0; + context.nPiecesNeedRemove--; +#if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) + updateHash(pos); +#endif + + // 去子完成 + + // 如果决出胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + + // 还有其余的子要去吗 + if (context.nPiecesNeedRemove > 0) { + // 继续去子 + return true; + } + // 所有去子都完成了 + else { + // 开局阶段 + if (context.stage == GAME_PLACING) { + // 如果双方都无未放置的棋子 + if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) { + + // 进入中局阶段 + context.stage = GAME_MOVING; + + // 进入选子状态 + context.action = ACTION_CHOOSE; + + // 清除禁点 + cleanForbiddenPoints(); + + // 设置轮到谁走 + if (currentRule.isDefenderMoveFirst) { + context.turn = PLAYER2; + } else { + context.turn = PLAYER1; + } + + // 再决胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + } + // 如果双方还有子 + else { + // 进入落子状态 + context.action = ACTION_PLACE; + // 设置轮到谁走 + changeTurn(); + // 如果决出胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + } + } + // 中局阶段 + else { + // 进入选子状态 + context.action = ACTION_CHOOSE; + // 设置轮到谁走 + changeTurn(); + // 如果决出胜负 + if (win()) { + if (cp == true) { + setTips(); + } + return true; + } + } + } + + if (cp == true) { + setTips(); + } + return true; } bool NineChess::choose(int pos) @@ -1364,7 +1360,7 @@ int NineChess::isInMills(int pos, bool test) { int n = 0; int pos1, pos2; - char m = test? INT32_MAX : board_[pos] & '\x30'; + int m = test? INT32_MAX : board_[pos] & '\x30'; for (int i = 0; i < 3; i++) { pos1 = millTable[pos][i][0]; pos2 = millTable[pos][i][1]; @@ -1633,7 +1629,7 @@ enum NineChess::Player NineChess::getWhosPiece(int c, int p) return NOBODY; } -void NineChess::getElapsedTimeMS(int &p1_ms, int &p2_ms) +void NineChess::getElapsedTimeMS(long &p1_ms, long &p2_ms) { update(); p1_ms = elapsedMS_1; diff --git a/NineChess/src/ninechess.h b/NineChess/src/ninechess.h index 9da73c0e..f9404948 100644 --- a/NineChess/src/ninechess.h +++ b/NineChess/src/ninechess.h @@ -75,7 +75,6 @@ public: // 定义类型 typedef int32_t move_t; - // 位置迭代器 // typedef typename std::vector::iterator posIterator; // typedef typename std::vector::const_iterator constPosIterator; @@ -148,7 +147,7 @@ public: }; uint64_t rand64(void) { - return rand() ^ + return (uint64_t)rand() ^ ((uint64_t)rand() << 15) ^ ((uint64_t)rand() << 30) ^ ((uint64_t)rand() << 45) ^ @@ -379,7 +378,7 @@ public: } // 玩家1和玩家2的用时 - void getElapsedTimeMS(int &p1_ms, int &p2_ms); + void getElapsedTimeMS(long &p1_ms, long &p2_ms); // 获取棋局的字符提示 const string getTips() const diff --git a/NineChess/src/ninechessai_ab.cpp b/NineChess/src/ninechessai_ab.cpp index 6e43737a..bba46f99 100644 --- a/NineChess/src/ninechessai_ab.cpp +++ b/NineChess/src/ninechessai_ab.cpp @@ -29,7 +29,7 @@ #include #include "ninechessai_ab.h" -#include "HashMap.h" +#include "hashmap.h" using namespace CTSL; @@ -103,7 +103,7 @@ struct NineChessAi_ab::Node *NineChessAi_ab::addNode( newNode->pruned = false; #endif - player = player; // Remove warning + //player = player; // Remove warning #ifdef DEBUG_AB_TREE #if ((defined HASH_MAP_ENABLE) || (defined BOOK_LEARNING) || (defined THREEFOLD_REPETITION)) @@ -190,23 +190,23 @@ void NineChessAi_ab::shuffleMovePriorityTable() std::shuffle(movePriorityTable3.begin(), movePriorityTable3.end(), std::default_random_engine(seed)); std::shuffle(movePriorityTable4.begin(), movePriorityTable4.end(), std::default_random_engine(seed)); - for (int i = 0; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { movePriorityTable[i + 0] = movePriorityTable0[i]; } - for (int i = 0; i < 8; i++) { + for (size_t i = 0; i < 8; i++) { movePriorityTable[i + 4] = movePriorityTable1[i]; } - for (int i = 0; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { movePriorityTable[i + 12] = movePriorityTable2[i]; } - for (int i = 0; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { movePriorityTable[i + 16] = movePriorityTable3[i]; } - for (int i = 0; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { movePriorityTable[i + 20] = movePriorityTable4[i]; } } @@ -217,7 +217,7 @@ void NineChessAi_ab::generateLegalMoves(Node *node, move_t bestMove) { const int MOVE_PRIORITY_TABLE_SIZE = NineChess::N_RINGS * NineChess::N_SEATS; int pos = 0; - size_t newCapacity = 24; + int newCapacity = 24; // 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销 switch (chessTemp.getStage()) { diff --git a/NineChess/src/ninechessai_ab.h b/NineChess/src/ninechessai_ab.h index 7734b853..a9631aac 100644 --- a/NineChess/src/ninechessai_ab.h +++ b/NineChess/src/ninechessai_ab.h @@ -32,11 +32,11 @@ //#endif #include #include -#include +#include #include #include "ninechess.h" -#include "hashMap.h" +#include "hashmap.h" #ifdef MEMORY_POOL #include "MemoryPool.h" @@ -62,13 +62,14 @@ public: struct Node { public: - move_t move; // 着法的命令行指令,图上标示为节点前的连线 - value_t value; // 节点的值 vector children; // 子节点列表 struct Node* parent; // 父节点 + move_t move; // 着法的命令行指令,图上标示为节点前的连线 + value_t value; // 节点的值 #ifdef SORT_CONSIDER_PRUNED bool pruned; // 是否在此处剪枝 #endif + #ifdef DEBUG_AB_TREE size_t id; // 结点编号 string cmd; @@ -278,4 +279,12 @@ private: char cmdline[32]; }; +#ifdef HASH_MAP_ENABLE +extern HashMap hashmap; +#endif /* #ifdef HASH_MAP_ENABLE */ + +#ifdef THREEFOLD_REPETITION +extern vector positions; +#endif + #endif diff --git a/NineChess/src/ninechesswindow.cpp b/NineChess/src/ninechesswindow.cpp index 01fcacc0..3f52d747 100644 --- a/NineChess/src/ninechesswindow.cpp +++ b/NineChess/src/ninechesswindow.cpp @@ -17,899 +17,897 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#if _MSC_VER >= 1600 -#pragma execution_character_set("utf-8") -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ninechesswindow.h" -#include "gamecontroller.h" -#include "gamescene.h" -#include "graphicsconst.h" + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ninechesswindow.h" +#include "gamecontroller.h" +#include "gamescene.h" +#include "graphicsconst.h" #include "server.h" -#include "client.h" - -NineChessWindow::NineChessWindow(QWidget * parent) : - QMainWindow(parent), - scene(nullptr), - game(nullptr), - ruleNo(-1), - autoRunTimer(this) -{ - ui.setupUi(this); - - // 去掉标题栏 - //setWindowFlags(Qt::FramelessWindowHint); - - // 设置透明(窗体标题栏不透明,背景透明,如果不去掉标题栏,背景就变为黑色) - //setAttribute(Qt::WA_TranslucentBackground); - - // 设置全体透明度系数 - //setWindowOpacity(0.7); - - // 设置场景 - scene = new GameScene(this); - - // 设置场景尺寸大小为棋盘大小的1.08倍 - scene->setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54, - BOARD_SIZE * 1.08, BOARD_SIZE * 1.08); - - // 初始化各个控件 - - // 关联视图和场景 - ui.gameView->setScene(scene); - - // 视图反走样 - ui.gameView->setRenderHint(QPainter::Antialiasing, true); - - // 视图反锯齿 - ui.gameView->setRenderHint(QPainter::Antialiasing); - - // 因功能限制,使部分功能不可用,将来再添加 - ui.actionInternet_I->setDisabled(false); - ui.actionSetting_O->setDisabled(true); - - // 初始化游戏规则菜单 - ui.menu_R->installEventFilter(this); - - // 关联自动运行定时器 - connect(&autoRunTimer, SIGNAL(timeout()), - this, SLOT(onAutoRunTimeOut())); - - // 主窗口居中显示 - QRect deskTopRect = QGuiApplication::primaryScreen()->geometry(); - int unitw = (deskTopRect.width() - width()) / 2; - int unith = (deskTopRect.height() - height()) / 2; - this->move(unitw, unith); - - // 游戏初始化 - initialize(); -} - -NineChessWindow::~NineChessWindow() -{ - if (game) { - game->disconnect(); - game->deleteLater(); - } - - qDeleteAll(ruleActionList); -} - -void NineChessWindow::closeEvent(QCloseEvent *event) -{ - if (file.isOpen()) - file.close(); - - // 取消自动运行 - ui.actionAutoRun_A->setChecked(false); - - //qDebug() << "closed"; - - QMainWindow::closeEvent(event); -} - -bool NineChessWindow::eventFilter(QObject *watched, QEvent *event) -{ - // 重载这个函数只是为了让规则菜单(动态)显示提示 - if (watched == ui.menu_R) { - switch (event->type()) { - case QEvent::ToolTip: - QHelpEvent *he = dynamic_cast (event); - QAction *action = ui.menu_R->actionAt(he->pos()); - if (action) { - QToolTip::showText(he->globalPos(), action->toolTip(), this); - return true; - } - break; - } - } - - return QMainWindow::eventFilter(watched, event); -} - -void NineChessWindow::initialize() -{ - // 初始化函数,仅执行一次 - if (game) - return; - - // 开辟一个新的游戏控制器 - game = new GameController(*scene, this); - - // 添加新菜单栏动作 - QMap actions = game->getActions(); - - for (auto i = actions.constBegin(); i != actions.constEnd(); i++) { - // qDebug() << i.key() << i.value(); - // QMap的key存放int索引值,value存放规则名称和规则提示 - QAction *ruleAction = new QAction(i.value().at(0), this); - ruleAction->setToolTip(i.value().at(1)); - ruleAction->setCheckable(true); - - // 索引值放在QAction的Data里 - ruleAction->setData(i.key()); - - // 添加到动作列表 - ruleActionList.append(ruleAction); - - // 添加到“规则”菜单 - ui.menu_R->addAction(ruleAction); - - connect(ruleAction, SIGNAL(triggered()), - this, SLOT(actionRules_triggered())); - } - - // 关联主窗口动作信号和控制器的槽 - - connect(ui.actionGiveUp_G, SIGNAL(triggered()), - game, SLOT(giveUp())); - - connect(ui.actionEngine1_T, SIGNAL(toggled(bool)), - game, SLOT(setEngine1(bool))); - - connect(ui.actionEngine2_R, SIGNAL(toggled(bool)), - game, SLOT(setEngine2(bool))); - - connect(ui.actionSound_S, SIGNAL(toggled(bool)), - game, SLOT(setSound(bool))); - - connect(ui.actionAnimation_A, SIGNAL(toggled(bool)), - game, SLOT(setAnimation(bool))); - - // 视图上下翻转 - connect(ui.actionFlip_F, &QAction::triggered, - game, &GameController::flip); - - // 视图左右镜像 - connect(ui.actionMirror_M, &QAction::triggered, - game, &GameController::mirror); - - // 视图须时针旋转90° - connect(ui.actionTurnRight_R, &QAction::triggered, - game, &GameController::turnRight); - - // 视图逆时针旋转90° - connect(ui.actionTurnLeftt_L, &QAction::triggered, - game, &GameController::turnLeft); - - // 关联控制器的信号和主窗口控件的槽 - - // 更新LCD1,显示玩家1用时 - connect(game, SIGNAL(time1Changed(QString)), - ui.lcdNumber_1, SLOT(display(QString))); - - // 更新LCD2,显示玩家2用时 - connect(game, SIGNAL(time2Changed(QString)), - ui.lcdNumber_2, SLOT(display(QString))); - - // 关联场景的信号和控制器的槽 - connect(scene, SIGNAL(mouseReleased(QPointF)), - game, SLOT(actionPiece(QPointF))); - - // 为状态栏添加一个正常显示的标签 - QLabel *statusBarlabel = new QLabel(this); - QFont statusBarFont; - statusBarFont.setPointSize(16); - statusBarlabel->setFont(statusBarFont); - ui.statusBar->addWidget(statusBarlabel); - - // 更新状态栏 - connect(game, SIGNAL(statusBarChanged(QString)), - statusBarlabel, SLOT(setText(QString))); - - // 默认第2号规则 - ruleNo = 1; - ruleActionList.at(ruleNo)->setChecked(true); - - // 重置游戏规则 - game->setRule(ruleNo); - - // 更新规则显示 - ruleInfo(); - - // 关联列表视图和字符串列表模型 - ui.listView->setModel(game->getManualListModel()); - - // 因为QListView的rowsInserted在setModel之后才能启动, - // 第一次需手动初始化选中listView第一项 - //qDebug() << ui.listView->model(); - ui.listView->setCurrentIndex(ui.listView->model()->index(0, 0)); - - // 初始局面、前一步、后一步、最终局面的槽 - - connect(ui.actionBegin_S, &QAction::triggered, - this, &NineChessWindow::on_actionRowChange); - - connect(ui.actionPrevious_B, &QAction::triggered, - this, &NineChessWindow::on_actionRowChange); - - connect(ui.actionNext_F, &QAction::triggered, - this, &NineChessWindow::on_actionRowChange); - - connect(ui.actionEnd_E, &QAction::triggered, - this, &NineChessWindow::on_actionRowChange); - - // 手动在listView里选择着法后更新的槽 - connect(ui.listView, &ManualListView::currentChangedSignal, - this, &NineChessWindow::on_actionRowChange); - - // 更新四个键的状态 - on_actionRowChange(); - - int h = QApplication::desktop()->height(); - this->resize(QSize(h * 3/4, h * 3/4)); - - // 窗口最大化 -#ifdef SHOW_MAXIMIZED_ON_LOAD - showMaximized(); - QWidget::setWindowFlags(Qt::WindowMaximizeButtonHint | - Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint); -#endif // SHOW_MAXIMIZED_ON_LOAD -} - -void NineChessWindow::ruleInfo() -{ - int s = game->getStepsLimit(); - int t = game->getTimeLimit(); - - QString tl(" 不限时"); - QString sl(" 不限步"); - - if (s > 0) - sl = " 限" + QString::number(s) + "步"; - if (t > 0) - tl = " 限时" + QString::number(s) + "分"; - - // 规则显示 - ui.labelRule->setText(tl + sl); - - // 规则提示 - ui.labelInfo->setToolTip(QString(NineChess::RULES[ruleNo].name) + "\n" + - NineChess::RULES[ruleNo].description); - - ui.labelRule->setToolTip(ui.labelInfo->toolTip()); - -#if 0 - QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name)) - .arg(tr(NineChess::RULES[ruleNo].info)); -#endif -} - -void NineChessWindow::on_actionLimited_T_triggered() -{ - /* - * 其实本来可以用设计器做个ui,然后从QDialog派生个自己的对话框 - * 但我不想再派生新类了,又要多出一个类和两个文件 - * 还要写与主窗口的接口,费劲 - * 于是手写QDialog界面 - */ - int gStep = game->getStepsLimit(); - int gTime = game->getTimeLimit(); - - // 定义新对话框 - QDialog *dialog = new QDialog(this); - dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); - dialog->setObjectName(QStringLiteral("Dialog")); - dialog->setWindowTitle(tr("步数和时间限制")); - dialog->resize(256, 108); - dialog->setModal(true); - - // 生成各个控件 - QFormLayout *formLayout = new QFormLayout(dialog); - QLabel *label_step = new QLabel(dialog); - QLabel *label_time = new QLabel(dialog); - QComboBox *comboBox_step = new QComboBox(dialog); - QComboBox *comboBox_time = new QComboBox(dialog); - QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); -#if 0 - // 设置各个控件ObjectName,不设也没关系 - formLayout->setObjectName(QStringLiteral("formLayout")); - label_step->setObjectName(QStringLiteral("label_step")); - label_time->setObjectName(QStringLiteral("label_time")); - comboBox_step->setObjectName(QStringLiteral("comboBox_step")); - comboBox_time->setObjectName(QStringLiteral("comboBox_time")); - buttonBox->setObjectName(QStringLiteral("buttonBox")); -#endif - // 设置各个控件数据 - label_step->setText(tr("超出限制步数判和:")); - label_time->setText(tr("任意一方超时判负:")); - comboBox_step->addItem(tr("无限制"), 0); - comboBox_step->addItem(tr("50步"), 50); - comboBox_step->addItem(tr("100步"), 100); - comboBox_step->addItem(tr("200步"), 200); - comboBox_time->addItem(tr("无限制"), 0); - comboBox_time->addItem(tr("5分钟"), 5); - comboBox_time->addItem(tr("10分钟"), 10); - comboBox_time->addItem(tr("20分钟"), 20); - comboBox_step->setCurrentIndex(comboBox_step->findData(gStep)); - comboBox_time->setCurrentIndex(comboBox_time->findData(gTime)); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定")); - buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消")); - - // 布局 - formLayout->setSpacing(6); - formLayout->setContentsMargins(11, 11, 11, 11); - formLayout->setWidget(0, QFormLayout::LabelRole, label_step); - formLayout->setWidget(0, QFormLayout::FieldRole, comboBox_step); - formLayout->setWidget(1, QFormLayout::LabelRole, label_time); - formLayout->setWidget(1, QFormLayout::FieldRole, comboBox_time); - formLayout->setWidget(2, QFormLayout::SpanningRole, buttonBox); - - // 关联信号和槽函数 - connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); - - // 收集数据 - if (dialog->exec() == QDialog::Accepted) { - int dStep = comboBox_step->currentData().toInt(); - int dTime = comboBox_time->currentData().toInt(); - if (gStep != dStep || gTime != dTime) { - // 重置游戏规则 - game->setRule(ruleNo, dStep, dTime); - } - } - - // 删除对话框,子控件会一并删除 - dialog->disconnect(); - delete dialog; - - // 更新规则显示 - ruleInfo(); -} - -void NineChessWindow::actionRules_triggered() -{ - // 取消自动运行 - ui.actionAutoRun_A->setChecked(false); - - // 取消其它规则的选择 - for (QAction *action : ruleActionList) - action->setChecked(false); - - // 选择当前规则 - QAction *action = dynamic_cast(sender()); - action->setChecked(true); - ruleNo = action->data().toInt(); - - // 如果游戏规则没变化,则返回 - if (ruleNo == game->getRuleNo()) - return; - - // 取消AI设定 - ui.actionEngine1_T->setChecked(false); - ui.actionEngine2_R->setChecked(false); - - // 重置游戏规则 - game->setRule(ruleNo); - - // 更新规则显示 - ruleInfo(); -} - -void NineChessWindow::on_actionNew_N_triggered() -{ - if (file.isOpen()) - file.close(); - -#ifdef SAVE_CHESSBOOK_WHEN_ACTION_NEW_TRIGGERED - QString path = QDir::currentPath() + "/" + tr("book_") + QString::number(QDateTime::currentDateTime().toTime_t()) + ".txt"; - QStringListModel* strlist = qobject_cast(ui.listView->model()); - - if (path.isEmpty() == false && strlist->stringList().size() > 18) { - // 文件对象 - file.setFileName(path); - - // 打开文件,只写方式打开 - bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); - - if (isok) { - // 写文件 - QTextStream textStream(&file); - for (QString cmd : strlist->stringList()) - textStream << cmd << endl; - file.flush(); - } - } -#endif /* SAVE_CHESSBOOK_WHEN_ACTION_NEW_TRIGGERED */ - - // 取消自动运行 - ui.actionAutoRun_A->setChecked(false); - - // 取消AI设定 - ui.actionEngine1_T->setChecked(false); - ui.actionEngine2_R->setChecked(false); - - // 重置游戏规则 - game->gameReset(); -} - -void NineChessWindow::on_actionOpen_O_triggered() -{ - QString path = QFileDialog::getOpenFileName(this, tr("打开棋谱文件"), QDir::currentPath(), "TXT(*.txt)"); - if (path.isEmpty() == false) { - if (file.isOpen()) - file.close(); - - // 文件对象 - file.setFileName(path); - - // 不支持1MB以上的文件 - if (file.size() > 0x100000) { - // 定义新对话框 - QMessageBox msgBox(QMessageBox::Warning, - tr("文件过大"), tr("不支持1MB以上文件"), QMessageBox::Ok); - msgBox.exec(); - return; - } - - // 打开文件,只读方式打开 - bool isok = file.open(QFileDevice::ReadOnly | QFileDevice::Text); - - if (isok) { - // 取消AI设定 - ui.actionEngine1_T->setChecked(false); - ui.actionEngine2_R->setChecked(false); - - // 读文件 - QTextStream textStream(&file); - QString cmd; - cmd = textStream.readLine(); - - // 读取并显示棋谱时,不必刷新棋局场景 - if (!(game->command(cmd, false))) { - // 定义新对话框 - QMessageBox msgBox(QMessageBox::Warning, tr("文件错误"), tr("不是正确的棋谱文件"), QMessageBox::Ok); - msgBox.exec(); - return; - } - - while (!textStream.atEnd()) { - cmd = textStream.readLine(); - game->command(cmd, false); - } - - // 最后刷新棋局场景 - game->updateScence(); - } - } -} - -void NineChessWindow::on_actionSave_S_triggered() -{ - if (file.isOpen()) { - file.close(); - - // 打开文件,只写方式打开 - bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); - - if (isok) { - // 写文件 - QTextStream textStream(&file); - QStringListModel *strlist = qobject_cast(ui.listView->model()); - for (QString cmd : strlist->stringList()) - textStream << cmd << endl; - file.flush(); - } - } else { - on_actionSaveAs_A_triggered(); - } -} - -void NineChessWindow::on_actionSaveAs_A_triggered() -{ - QString path = QFileDialog::getSaveFileName(this, - tr("打开棋谱文件"), - QDir::currentPath() + tr("棋谱_") + QString::number(QDateTime::currentDateTime().toTime_t()) + ".txt", "TXT(*.txt)"); - - if (path.isEmpty() == false) { - if (file.isOpen()) - file.close(); - - // 文件对象 - file.setFileName(path); - - // 打开文件,只写方式打开 - bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); - - if (isok) { - // 写文件 - QTextStream textStream(&file); - QStringListModel* strlist = qobject_cast(ui.listView->model()); - for (QString cmd : strlist->stringList()) - textStream << cmd << endl; - file.flush(); - } - } -} - -void NineChessWindow::on_actionEdit_E_toggled(bool arg1) -{ - Q_UNUSED(arg1) -} - -void NineChessWindow::on_actionInvert_I_toggled(bool arg1) -{ - // 如果黑白反转 - if (arg1) { - // 设置玩家1和玩家2的标识图 - ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/White.png")); - ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/Black.png")); - ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); - ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); - } else { - // 设置玩家1和玩家2的标识图 - ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/Black.png")); - ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/White.png")); - ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); - ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); - } - - // 让控制器改变棋子颜色 - game->setInvert(arg1); -} - -// 前后招的公共槽 -void NineChessWindow::on_actionRowChange() -{ - QAbstractItemModel *model = ui.listView->model(); - int rows = model->rowCount(); - int currentRow = ui.listView->currentIndex().row(); - - QObject *const obsender = sender(); - if (obsender != nullptr) { - if (obsender == ui.actionBegin_S) { - ui.listView->setCurrentIndex(model->index(0, 0)); - } else if (obsender == ui.actionPrevious_B) { - if (currentRow > 0) { - ui.listView->setCurrentIndex(model->index(currentRow - 1, 0)); - } - } else if (obsender == ui.actionNext_F) { - if (currentRow < rows - 1) { - ui.listView->setCurrentIndex(model->index(currentRow + 1, 0)); - } - } else if (obsender == ui.actionEnd_E) { - ui.listView->setCurrentIndex(model->index(rows - 1, 0)); - } - - currentRow = ui.listView->currentIndex().row(); - } - - // 更新动作状态 - if (rows <= 1) { - ui.actionBegin_S->setEnabled(false); - ui.actionPrevious_B->setEnabled(false); - ui.actionNext_F->setEnabled(false); - ui.actionEnd_E->setEnabled(false); - ui.actionAutoRun_A->setEnabled(false); - } else { - if (currentRow <= 0) { - ui.actionBegin_S->setEnabled(false); - ui.actionPrevious_B->setEnabled(false); - ui.actionNext_F->setEnabled(true); - ui.actionEnd_E->setEnabled(true); - ui.actionAutoRun_A->setEnabled(true); - } else if (currentRow >= rows - 1) { - ui.actionBegin_S->setEnabled(true); - ui.actionPrevious_B->setEnabled(true); - ui.actionNext_F->setEnabled(false); - ui.actionEnd_E->setEnabled(false); - ui.actionAutoRun_A->setEnabled(false); - } else { - ui.actionBegin_S->setEnabled(true); - ui.actionPrevious_B->setEnabled(true); - ui.actionNext_F->setEnabled(true); - ui.actionEnd_E->setEnabled(true); - ui.actionAutoRun_A->setEnabled(true); - } - } - - // 更新局面 - game->stageChange(currentRow); - -#if 0 - // 下面的代码全部取消,改用QTimer的方式实现 - // 更新局面 - bool changed = game->stageChange(currentRow); - // 处理自动播放时的动画 - if (changed && game->isAnimation()) { - // 不使用processEvents函数进行非阻塞延时,频繁调用占用CPU较多 - //QElapsedTimer et; - //et.start(); - //while (et.elapsed() < waitTime) { - // qApp->processEvents(QEventLoop::ExcludeUserInputEvents); - //} - - int waitTime = game->getDurationTime() + 50; - // 使用QEventLoop进行非阻塞延时,CPU占用低 - QEventLoop loop; - QTimer::singleShot(waitTime, &loop, SLOT(quit())); - loop.exec(); - } -#endif // 0 -} - -void NineChessWindow::onAutoRunTimeOut(QPrivateSignal signal) -{ - Q_UNUSED(signal) - int rows = ui.listView->model()->rowCount(); - int currentRow = ui.listView->currentIndex().row(); - - if (rows <= 1) { - ui.actionAutoRun_A->setChecked(false); - return; - } - - // 执行“下一招” - if (currentRow < rows - 1) { - if (currentRow < rows - 1) { - ui.listView->setCurrentIndex(ui.listView->model()->index(currentRow + 1, 0)); - } - - currentRow = ui.listView->currentIndex().row(); - - // 更新动作状态 - if (currentRow <= 0) { - ui.actionBegin_S->setEnabled(false); - ui.actionPrevious_B->setEnabled(false); - ui.actionNext_F->setEnabled(true); - ui.actionEnd_E->setEnabled(true); - ui.actionAutoRun_A->setEnabled(true); - } else if (currentRow >= rows - 1) { - ui.actionBegin_S->setEnabled(true); - ui.actionPrevious_B->setEnabled(true); - ui.actionNext_F->setEnabled(false); - ui.actionEnd_E->setEnabled(false); - ui.actionAutoRun_A->setEnabled(false); - } else { - ui.actionBegin_S->setEnabled(true); - ui.actionPrevious_B->setEnabled(true); - ui.actionNext_F->setEnabled(true); - ui.actionEnd_E->setEnabled(true); - ui.actionAutoRun_A->setEnabled(true); - } - - // 更新局面 - game->stageChange(currentRow); - } else { - ui.actionAutoRun_A->setChecked(false); - } -} - -// 自动运行 -void NineChessWindow::on_actionAutoRun_A_toggled(bool arg1) -{ - if (arg1) { - // 自动运行前禁用控件 - ui.dockWidget->setEnabled(false); - ui.gameView->setEnabled(false); - - // 启动定时器 - autoRunTimer.start(game->getDurationTime() + 50); - } else { - // 关闭定时器 - autoRunTimer.stop(); - - // 自动运行结束后启用控件 - ui.dockWidget->setEnabled(true); - ui.gameView->setEnabled(true); - } -} - -void NineChessWindow::on_actionLocal_L_triggered() -{ - ui.actionLocal_L->setChecked(true); - ui.actionInternet_I->setChecked(false); -} - -void NineChessWindow::on_actionInternet_I_triggered() -{ - ui.actionLocal_L->setChecked(false); - ui.actionInternet_I->setChecked(true); - - game->showNetworkWindow(); -} - -void NineChessWindow::on_actionEngine_E_triggered() -{ - // 定义新对话框 - QDialog *dialog = new QDialog(this); - dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); - dialog->setObjectName(QStringLiteral("Dialog")); - dialog->setWindowTitle(tr("AI设置")); - dialog->resize(256, 188); - dialog->setModal(true); - - // 生成各个控件 - QVBoxLayout *vLayout = new QVBoxLayout(dialog); - QGroupBox *groupBox1 = new QGroupBox(dialog); - QGroupBox *groupBox2 = new QGroupBox(dialog); - - QHBoxLayout *hLayout1 = new QHBoxLayout; - QLabel *label_depth1 = new QLabel(dialog); - QSpinBox *spinBox_depth1 = new QSpinBox(dialog); - QLabel *label_time1 = new QLabel(dialog); - QSpinBox *spinBox_time1 = new QSpinBox(dialog); - - QHBoxLayout *hLayout2 = new QHBoxLayout; - QLabel *label_depth2 = new QLabel(dialog); - QSpinBox *spinBox_depth2 = new QSpinBox(dialog); - QLabel *label_time2 = new QLabel(dialog); - QSpinBox *spinBox_time2 = new QSpinBox(dialog); - - QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); - - // 设置各个控件数据 - groupBox1->setTitle(tr("玩家1 AI设置")); - label_depth1->setText(tr("深度")); - spinBox_depth1->setMinimum(1); - spinBox_depth1->setMaximum(99); - label_time1->setText(tr("限时")); - spinBox_time1->setMinimum(1); - spinBox_time1->setMaximum(3600); - - groupBox2->setTitle(tr("玩家2 AI设置")); - label_depth2->setText(tr("深度")); - spinBox_depth2->setMinimum(1); - spinBox_depth2->setMaximum(99); - label_time2->setText(tr("限时")); - spinBox_time2->setMinimum(1); - spinBox_time2->setMaximum(3600); - - buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定")); - buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消")); - - // 布局控件 - vLayout->addWidget(groupBox1); - vLayout->addWidget(groupBox2); - vLayout->addWidget(buttonBox); - groupBox1->setLayout(hLayout1); - groupBox2->setLayout(hLayout2); - hLayout1->addWidget(label_depth1); - hLayout1->addWidget(spinBox_depth1); - hLayout1->addWidget(label_time1); - hLayout1->addWidget(spinBox_time1); - hLayout2->addWidget(label_depth2); - hLayout2->addWidget(spinBox_depth2); - hLayout2->addWidget(label_time2); - hLayout2->addWidget(spinBox_time2); - - // 关联信号和槽函数 - connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); - connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); - - // 目前数据 - int depth1, depth2, time1, time2; - game->getAiDepthTime(depth1, time1, depth2, time2); - spinBox_depth1->setValue(depth1); - spinBox_depth2->setValue(depth2); - spinBox_time1->setValue(time1); - spinBox_time2->setValue(time2); - - // 新设数据 - if (dialog->exec() == QDialog::Accepted) { - int depth1_new, depth2_new, time1_new, time2_new; - depth1_new = spinBox_depth1->value(); - depth2_new = spinBox_depth2->value(); - time1_new = spinBox_time1->value(); - time2_new = spinBox_time2->value(); - - if (depth1 != depth1_new || - depth2 != depth2_new || - time1 != time1_new || - time2 != time2_new) { - // 重置AI - game->setAiDepthTime(depth1_new, time1_new, depth2_new, time2_new); - } - } - - // 删除对话框,子控件会一并删除 - dialog->disconnect(); - delete dialog; -} - -void NineChessWindow::on_actionViewHelp_V_triggered() -{ - QDesktopServices::openUrl(QUrl("https://blog.csdn.net/liuweilhy/article/details/83832180")); -} - -void NineChessWindow::on_actionWeb_W_triggered() -{ - QDesktopServices::openUrl(QUrl("http://hy-tech.top")); -} - -void NineChessWindow::on_actionAbout_A_triggered() -{ - QDialog *dialog = new QDialog; - - dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); - dialog->setObjectName(QStringLiteral("aboutDialog")); - dialog->setWindowTitle(tr("三棋")); - dialog->setModal(true); - - // 生成各个控件 - QVBoxLayout *vLayout = new QVBoxLayout(dialog); - QHBoxLayout *hLayout = new QHBoxLayout; - QLabel *label_icon1 = new QLabel(dialog); - QLabel *label_icon2 = new QLabel(dialog); - QLabel *label_text = new QLabel(dialog); - QLabel *label_image = new QLabel(dialog); - - // 设置各个控件数据 - label_icon1->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/black_piece.png"))); - label_icon2->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/white_piece.png"))); - label_icon1->setAlignment(Qt::AlignCenter); - label_icon2->setAlignment(Qt::AlignCenter); - label_icon1->setFixedSize(32, 32); - label_icon2->setFixedSize(32, 32); - label_icon1->setScaledContents(true); - label_icon2->setScaledContents(true); - - label_text->setText(tr("Donate")); - label_text->setAlignment(Qt::AlignCenter); - label_image->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/donate.png"))); - label_image->setAlignment(Qt::AlignCenter); - label_image->setScaledContents(true); - - // 布局 - vLayout->addLayout(hLayout); - hLayout->addWidget(label_icon1); - hLayout->addWidget(label_icon2); - hLayout->addWidget(label_text); - vLayout->addWidget(label_image); - - // 运行对话框 - dialog->exec(); - - // 删除对话框 - dialog->disconnect(); - delete dialog; -} +#include "client.h" + +NineChessWindow::NineChessWindow(QWidget * parent) : + QMainWindow(parent), + scene(nullptr), + game(nullptr), + ruleNo(-1), + autoRunTimer(this) +{ + ui.setupUi(this); + + // 去掉标题栏 + //setWindowFlags(Qt::FramelessWindowHint); + + // 设置透明(窗体标题栏不透明,背景透明,如果不去掉标题栏,背景就变为黑色) + //setAttribute(Qt::WA_TranslucentBackground); + + // 设置全体透明度系数 + //setWindowOpacity(0.7); + + // 设置场景 + scene = new GameScene(this); + + // 设置场景尺寸大小为棋盘大小的1.08倍 + scene->setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54, + BOARD_SIZE * 1.08, BOARD_SIZE * 1.08); + + // 初始化各个控件 + + // 关联视图和场景 + ui.gameView->setScene(scene); + + // 视图反走样 + ui.gameView->setRenderHint(QPainter::Antialiasing, true); + + // 视图反锯齿 + ui.gameView->setRenderHint(QPainter::Antialiasing); + + // 因功能限制,使部分功能不可用,将来再添加 + ui.actionInternet_I->setDisabled(false); + ui.actionSetting_O->setDisabled(true); + + // 初始化游戏规则菜单 + ui.menu_R->installEventFilter(this); + + // 关联自动运行定时器 + connect(&autoRunTimer, SIGNAL(timeout()), + this, SLOT(onAutoRunTimeOut())); + + // 主窗口居中显示 + QRect deskTopRect = QGuiApplication::primaryScreen()->geometry(); + int unitw = (deskTopRect.width() - width()) / 2; + int unith = (deskTopRect.height() - height()) / 2; + this->move(unitw, unith); + + // 游戏初始化 + initialize(); +} + +NineChessWindow::~NineChessWindow() +{ + if (game) { + game->disconnect(); + game->deleteLater(); + } + + qDeleteAll(ruleActionList); +} + +void NineChessWindow::closeEvent(QCloseEvent *event) +{ + if (file.isOpen()) + file.close(); + + // 取消自动运行 + ui.actionAutoRun_A->setChecked(false); + + //qDebug() << "closed"; + + QMainWindow::closeEvent(event); +} + +bool NineChessWindow::eventFilter(QObject *watched, QEvent *event) +{ + // 重载这个函数只是为了让规则菜单(动态)显示提示 + if (watched == ui.menu_R) { + switch (event->type()) { + case QEvent::ToolTip: + QHelpEvent *he = dynamic_cast (event); + QAction *action = ui.menu_R->actionAt(he->pos()); + if (action) { + QToolTip::showText(he->globalPos(), action->toolTip(), this); + return true; + } + break; + } + } + + return QMainWindow::eventFilter(watched, event); +} + +void NineChessWindow::initialize() +{ + // 初始化函数,仅执行一次 + if (game) + return; + + // 开辟一个新的游戏控制器 + game = new GameController(*scene, this); + + // 添加新菜单栏动作 + QMap actions = game->getActions(); + + for (auto i = actions.constBegin(); i != actions.constEnd(); i++) { + // qDebug() << i.key() << i.value(); + // QMap的key存放int索引值,value存放规则名称和规则提示 + QAction *ruleAction = new QAction(i.value().at(0), this); + ruleAction->setToolTip(i.value().at(1)); + ruleAction->setCheckable(true); + + // 索引值放在QAction的Data里 + ruleAction->setData(i.key()); + + // 添加到动作列表 + ruleActionList.append(ruleAction); + + // 添加到“规则”菜单 + ui.menu_R->addAction(ruleAction); + + connect(ruleAction, SIGNAL(triggered()), + this, SLOT(actionRules_triggered())); + } + + // 关联主窗口动作信号和控制器的槽 + + connect(ui.actionGiveUp_G, SIGNAL(triggered()), + game, SLOT(giveUp())); + + connect(ui.actionEngine1_T, SIGNAL(toggled(bool)), + game, SLOT(setEngine1(bool))); + + connect(ui.actionEngine2_R, SIGNAL(toggled(bool)), + game, SLOT(setEngine2(bool))); + + connect(ui.actionSound_S, SIGNAL(toggled(bool)), + game, SLOT(setSound(bool))); + + connect(ui.actionAnimation_A, SIGNAL(toggled(bool)), + game, SLOT(setAnimation(bool))); + + // 视图上下翻转 + connect(ui.actionFlip_F, &QAction::triggered, + game, &GameController::flip); + + // 视图左右镜像 + connect(ui.actionMirror_M, &QAction::triggered, + game, &GameController::mirror); + + // 视图须时针旋转90° + connect(ui.actionTurnRight_R, &QAction::triggered, + game, &GameController::turnRight); + + // 视图逆时针旋转90° + connect(ui.actionTurnLeftt_L, &QAction::triggered, + game, &GameController::turnLeft); + + // 关联控制器的信号和主窗口控件的槽 + + // 更新LCD1,显示玩家1用时 + connect(game, SIGNAL(time1Changed(QString)), + ui.lcdNumber_1, SLOT(display(QString))); + + // 更新LCD2,显示玩家2用时 + connect(game, SIGNAL(time2Changed(QString)), + ui.lcdNumber_2, SLOT(display(QString))); + + // 关联场景的信号和控制器的槽 + connect(scene, SIGNAL(mouseReleased(QPointF)), + game, SLOT(actionPiece(QPointF))); + + // 为状态栏添加一个正常显示的标签 + QLabel *statusBarlabel = new QLabel(this); + QFont statusBarFont; + statusBarFont.setPointSize(16); + statusBarlabel->setFont(statusBarFont); + ui.statusBar->addWidget(statusBarlabel); + + // 更新状态栏 + connect(game, SIGNAL(statusBarChanged(QString)), + statusBarlabel, SLOT(setText(QString))); + + // 默认第2号规则 + ruleNo = 1; + ruleActionList.at(ruleNo)->setChecked(true); + + // 重置游戏规则 + game->setRule(ruleNo); + + // 更新规则显示 + ruleInfo(); + + // 关联列表视图和字符串列表模型 + ui.listView->setModel(game->getManualListModel()); + + // 因为QListView的rowsInserted在setModel之后才能启动, + // 第一次需手动初始化选中listView第一项 + //qDebug() << ui.listView->model(); + ui.listView->setCurrentIndex(ui.listView->model()->index(0, 0)); + + // 初始局面、前一步、后一步、最终局面的槽 + + connect(ui.actionBegin_S, &QAction::triggered, + this, &NineChessWindow::on_actionRowChange); + + connect(ui.actionPrevious_B, &QAction::triggered, + this, &NineChessWindow::on_actionRowChange); + + connect(ui.actionNext_F, &QAction::triggered, + this, &NineChessWindow::on_actionRowChange); + + connect(ui.actionEnd_E, &QAction::triggered, + this, &NineChessWindow::on_actionRowChange); + + // 手动在listView里选择着法后更新的槽 + connect(ui.listView, &ManualListView::currentChangedSignal, + this, &NineChessWindow::on_actionRowChange); + + // 更新四个键的状态 + on_actionRowChange(); + + int h = QApplication::desktop()->height(); + this->resize(QSize(h * 3/4, h * 3/4)); + + // 窗口最大化 +#ifdef SHOW_MAXIMIZED_ON_LOAD + showMaximized(); + QWidget::setWindowFlags(Qt::WindowMaximizeButtonHint | + Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint); +#endif // SHOW_MAXIMIZED_ON_LOAD +} + +void NineChessWindow::ruleInfo() +{ + int s = game->getStepsLimit(); + int t = game->getTimeLimit(); + + QString tl(" 不限时"); + QString sl(" 不限步"); + + if (s > 0) + sl = " 限" + QString::number(s) + "步"; + if (t > 0) + tl = " 限时" + QString::number(s) + "分"; + + // 规则显示 + ui.labelRule->setText(tl + sl); + + // 规则提示 + ui.labelInfo->setToolTip(QString(NineChess::RULES[ruleNo].name) + "\n" + + NineChess::RULES[ruleNo].description); + + ui.labelRule->setToolTip(ui.labelInfo->toolTip()); + +#if 0 + QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name)) + .arg(tr(NineChess::RULES[ruleNo].info)); +#endif +} + +void NineChessWindow::on_actionLimited_T_triggered() +{ + /* + * 其实本来可以用设计器做个ui,然后从QDialog派生个自己的对话框 + * 但我不想再派生新类了,又要多出一个类和两个文件 + * 还要写与主窗口的接口,费劲 + * 于是手写QDialog界面 + */ + int gStep = game->getStepsLimit(); + int gTime = game->getTimeLimit(); + + // 定义新对话框 + QDialog *dialog = new QDialog(this); + dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dialog->setObjectName(QStringLiteral("Dialog")); + dialog->setWindowTitle(tr("步数和时间限制")); + dialog->resize(256, 108); + dialog->setModal(true); + + // 生成各个控件 + QFormLayout *formLayout = new QFormLayout(dialog); + QLabel *label_step = new QLabel(dialog); + QLabel *label_time = new QLabel(dialog); + QComboBox *comboBox_step = new QComboBox(dialog); + QComboBox *comboBox_time = new QComboBox(dialog); + QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); +#if 0 + // 设置各个控件ObjectName,不设也没关系 + formLayout->setObjectName(QStringLiteral("formLayout")); + label_step->setObjectName(QStringLiteral("label_step")); + label_time->setObjectName(QStringLiteral("label_time")); + comboBox_step->setObjectName(QStringLiteral("comboBox_step")); + comboBox_time->setObjectName(QStringLiteral("comboBox_time")); + buttonBox->setObjectName(QStringLiteral("buttonBox")); +#endif + // 设置各个控件数据 + label_step->setText(tr("超出限制步数判和:")); + label_time->setText(tr("任意一方超时判负:")); + comboBox_step->addItem(tr("无限制"), 0); + comboBox_step->addItem(tr("50步"), 50); + comboBox_step->addItem(tr("100步"), 100); + comboBox_step->addItem(tr("200步"), 200); + comboBox_time->addItem(tr("无限制"), 0); + comboBox_time->addItem(tr("5分钟"), 5); + comboBox_time->addItem(tr("10分钟"), 10); + comboBox_time->addItem(tr("20分钟"), 20); + comboBox_step->setCurrentIndex(comboBox_step->findData(gStep)); + comboBox_time->setCurrentIndex(comboBox_time->findData(gTime)); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + buttonBox->setCenterButtons(true); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定")); + buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消")); + + // 布局 + formLayout->setSpacing(6); + formLayout->setContentsMargins(11, 11, 11, 11); + formLayout->setWidget(0, QFormLayout::LabelRole, label_step); + formLayout->setWidget(0, QFormLayout::FieldRole, comboBox_step); + formLayout->setWidget(1, QFormLayout::LabelRole, label_time); + formLayout->setWidget(1, QFormLayout::FieldRole, comboBox_time); + formLayout->setWidget(2, QFormLayout::SpanningRole, buttonBox); + + // 关联信号和槽函数 + connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); + + // 收集数据 + if (dialog->exec() == QDialog::Accepted) { + int dStep = comboBox_step->currentData().toInt(); + int dTime = comboBox_time->currentData().toInt(); + if (gStep != dStep || gTime != dTime) { + // 重置游戏规则 + game->setRule(ruleNo, dStep, dTime); + } + } + + // 删除对话框,子控件会一并删除 + dialog->disconnect(); + delete dialog; + + // 更新规则显示 + ruleInfo(); +} + +void NineChessWindow::actionRules_triggered() +{ + // 取消自动运行 + ui.actionAutoRun_A->setChecked(false); + + // 取消其它规则的选择 + for (QAction *action : ruleActionList) + action->setChecked(false); + + // 选择当前规则 + QAction *action = dynamic_cast(sender()); + action->setChecked(true); + ruleNo = action->data().toInt(); + + // 如果游戏规则没变化,则返回 + if (ruleNo == game->getRuleNo()) + return; + + // 取消AI设定 + ui.actionEngine1_T->setChecked(false); + ui.actionEngine2_R->setChecked(false); + + // 重置游戏规则 + game->setRule(ruleNo); + + // 更新规则显示 + ruleInfo(); +} + +void NineChessWindow::on_actionNew_N_triggered() +{ + if (file.isOpen()) + file.close(); + +#ifdef SAVE_CHESSBOOK_WHEN_ACTION_NEW_TRIGGERED + QString path = QDir::currentPath() + "/" + tr("book_") + QString::number(QDateTime::currentDateTime().toTime_t()) + ".txt"; + QStringListModel* strlist = qobject_cast(ui.listView->model()); + + if (path.isEmpty() == false && strlist->stringList().size() > 18) { + // 文件对象 + file.setFileName(path); + + // 打开文件,只写方式打开 + bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); + + if (isok) { + // 写文件 + QTextStream textStream(&file); + for (QString cmd : strlist->stringList()) + textStream << cmd << endl; + file.flush(); + } + } +#endif /* SAVE_CHESSBOOK_WHEN_ACTION_NEW_TRIGGERED */ + + // 取消自动运行 + ui.actionAutoRun_A->setChecked(false); + + // 取消AI设定 + ui.actionEngine1_T->setChecked(false); + ui.actionEngine2_R->setChecked(false); + + // 重置游戏规则 + game->gameReset(); +} + +void NineChessWindow::on_actionOpen_O_triggered() +{ + QString path = QFileDialog::getOpenFileName(this, tr("打开棋谱文件"), QDir::currentPath(), "TXT(*.txt)"); + if (path.isEmpty() == false) { + if (file.isOpen()) + file.close(); + + // 文件对象 + file.setFileName(path); + + // 不支持1MB以上的文件 + if (file.size() > 0x100000) { + // 定义新对话框 + QMessageBox msgBox(QMessageBox::Warning, + tr("文件过大"), tr("不支持1MB以上文件"), QMessageBox::Ok); + msgBox.exec(); + return; + } + + // 打开文件,只读方式打开 + bool isok = file.open(QFileDevice::ReadOnly | QFileDevice::Text); + + if (isok) { + // 取消AI设定 + ui.actionEngine1_T->setChecked(false); + ui.actionEngine2_R->setChecked(false); + + // 读文件 + QTextStream textStream(&file); + QString cmd; + cmd = textStream.readLine(); + + // 读取并显示棋谱时,不必刷新棋局场景 + if (!(game->command(cmd, false))) { + // 定义新对话框 + QMessageBox msgBox(QMessageBox::Warning, tr("文件错误"), tr("不是正确的棋谱文件"), QMessageBox::Ok); + msgBox.exec(); + return; + } + + while (!textStream.atEnd()) { + cmd = textStream.readLine(); + game->command(cmd, false); + } + + // 最后刷新棋局场景 + game->updateScence(); + } + } +} + +void NineChessWindow::on_actionSave_S_triggered() +{ + if (file.isOpen()) { + file.close(); + + // 打开文件,只写方式打开 + bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); + + if (isok) { + // 写文件 + QTextStream textStream(&file); + QStringListModel *strlist = qobject_cast(ui.listView->model()); + for (QString cmd : strlist->stringList()) + textStream << cmd << endl; + file.flush(); + } + } else { + on_actionSaveAs_A_triggered(); + } +} + +void NineChessWindow::on_actionSaveAs_A_triggered() +{ + QString path = QFileDialog::getSaveFileName(this, + tr("打开棋谱文件"), + QDir::currentPath() + tr("棋谱_") + QString::number(QDateTime::currentDateTime().toTime_t()) + ".txt", "TXT(*.txt)"); + + if (path.isEmpty() == false) { + if (file.isOpen()) + file.close(); + + // 文件对象 + file.setFileName(path); + + // 打开文件,只写方式打开 + bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text); + + if (isok) { + // 写文件 + QTextStream textStream(&file); + QStringListModel* strlist = qobject_cast(ui.listView->model()); + for (QString cmd : strlist->stringList()) + textStream << cmd << endl; + file.flush(); + } + } +} + +void NineChessWindow::on_actionEdit_E_toggled(bool arg1) +{ + Q_UNUSED(arg1) +} + +void NineChessWindow::on_actionInvert_I_toggled(bool arg1) +{ + // 如果黑白反转 + if (arg1) { + // 设置玩家1和玩家2的标识图 + ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/White.png")); + ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/Black.png")); + ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); + ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); + } else { + // 设置玩家1和玩家2的标识图 + ui.actionEngine1_T->setIcon(QIcon(":/icon/Resources/icon/Black.png")); + ui.actionEngine2_R->setIcon(QIcon(":/icon/Resources/icon/White.png")); + ui.picLabel1->setPixmap(QPixmap(":/icon/Resources/icon/Black.png")); + ui.picLabel2->setPixmap(QPixmap(":/icon/Resources/icon/White.png")); + } + + // 让控制器改变棋子颜色 + game->setInvert(arg1); +} + +// 前后招的公共槽 +void NineChessWindow::on_actionRowChange() +{ + QAbstractItemModel *model = ui.listView->model(); + int rows = model->rowCount(); + int currentRow = ui.listView->currentIndex().row(); + + QObject *const obsender = sender(); + if (obsender != nullptr) { + if (obsender == ui.actionBegin_S) { + ui.listView->setCurrentIndex(model->index(0, 0)); + } else if (obsender == ui.actionPrevious_B) { + if (currentRow > 0) { + ui.listView->setCurrentIndex(model->index(currentRow - 1, 0)); + } + } else if (obsender == ui.actionNext_F) { + if (currentRow < rows - 1) { + ui.listView->setCurrentIndex(model->index(currentRow + 1, 0)); + } + } else if (obsender == ui.actionEnd_E) { + ui.listView->setCurrentIndex(model->index(rows - 1, 0)); + } + + currentRow = ui.listView->currentIndex().row(); + } + + // 更新动作状态 + if (rows <= 1) { + ui.actionBegin_S->setEnabled(false); + ui.actionPrevious_B->setEnabled(false); + ui.actionNext_F->setEnabled(false); + ui.actionEnd_E->setEnabled(false); + ui.actionAutoRun_A->setEnabled(false); + } else { + if (currentRow <= 0) { + ui.actionBegin_S->setEnabled(false); + ui.actionPrevious_B->setEnabled(false); + ui.actionNext_F->setEnabled(true); + ui.actionEnd_E->setEnabled(true); + ui.actionAutoRun_A->setEnabled(true); + } else if (currentRow >= rows - 1) { + ui.actionBegin_S->setEnabled(true); + ui.actionPrevious_B->setEnabled(true); + ui.actionNext_F->setEnabled(false); + ui.actionEnd_E->setEnabled(false); + ui.actionAutoRun_A->setEnabled(false); + } else { + ui.actionBegin_S->setEnabled(true); + ui.actionPrevious_B->setEnabled(true); + ui.actionNext_F->setEnabled(true); + ui.actionEnd_E->setEnabled(true); + ui.actionAutoRun_A->setEnabled(true); + } + } + + // 更新局面 + game->stageChange(currentRow); + +#if 0 + // 下面的代码全部取消,改用QTimer的方式实现 + // 更新局面 + bool changed = game->stageChange(currentRow); + // 处理自动播放时的动画 + if (changed && game->isAnimation()) { + // 不使用processEvents函数进行非阻塞延时,频繁调用占用CPU较多 + //QElapsedTimer et; + //et.start(); + //while (et.elapsed() < waitTime) { + // qApp->processEvents(QEventLoop::ExcludeUserInputEvents); + //} + + int waitTime = game->getDurationTime() + 50; + // 使用QEventLoop进行非阻塞延时,CPU占用低 + QEventLoop loop; + QTimer::singleShot(waitTime, &loop, SLOT(quit())); + loop.exec(); + } +#endif // 0 +} + +void NineChessWindow::onAutoRunTimeOut(QPrivateSignal signal) +{ + Q_UNUSED(signal) + int rows = ui.listView->model()->rowCount(); + int currentRow = ui.listView->currentIndex().row(); + + if (rows <= 1) { + ui.actionAutoRun_A->setChecked(false); + return; + } + + // 执行“下一招” + if (currentRow < rows - 1) { + if (currentRow < rows - 1) { + ui.listView->setCurrentIndex(ui.listView->model()->index(currentRow + 1, 0)); + } + + currentRow = ui.listView->currentIndex().row(); + + // 更新动作状态 + if (currentRow <= 0) { + ui.actionBegin_S->setEnabled(false); + ui.actionPrevious_B->setEnabled(false); + ui.actionNext_F->setEnabled(true); + ui.actionEnd_E->setEnabled(true); + ui.actionAutoRun_A->setEnabled(true); + } else if (currentRow >= rows - 1) { + ui.actionBegin_S->setEnabled(true); + ui.actionPrevious_B->setEnabled(true); + ui.actionNext_F->setEnabled(false); + ui.actionEnd_E->setEnabled(false); + ui.actionAutoRun_A->setEnabled(false); + } else { + ui.actionBegin_S->setEnabled(true); + ui.actionPrevious_B->setEnabled(true); + ui.actionNext_F->setEnabled(true); + ui.actionEnd_E->setEnabled(true); + ui.actionAutoRun_A->setEnabled(true); + } + + // 更新局面 + game->stageChange(currentRow); + } else { + ui.actionAutoRun_A->setChecked(false); + } +} + +// 自动运行 +void NineChessWindow::on_actionAutoRun_A_toggled(bool arg1) +{ + if (arg1) { + // 自动运行前禁用控件 + ui.dockWidget->setEnabled(false); + ui.gameView->setEnabled(false); + + // 启动定时器 + autoRunTimer.start(game->getDurationTime() + 50); + } else { + // 关闭定时器 + autoRunTimer.stop(); + + // 自动运行结束后启用控件 + ui.dockWidget->setEnabled(true); + ui.gameView->setEnabled(true); + } +} + +void NineChessWindow::on_actionLocal_L_triggered() +{ + ui.actionLocal_L->setChecked(true); + ui.actionInternet_I->setChecked(false); +} + +void NineChessWindow::on_actionInternet_I_triggered() +{ + ui.actionLocal_L->setChecked(false); + ui.actionInternet_I->setChecked(true); + + game->showNetworkWindow(); +} + +void NineChessWindow::on_actionEngine_E_triggered() +{ + // 定义新对话框 + QDialog *dialog = new QDialog(this); + dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dialog->setObjectName(QStringLiteral("Dialog")); + dialog->setWindowTitle(tr("AI设置")); + dialog->resize(256, 188); + dialog->setModal(true); + + // 生成各个控件 + QVBoxLayout *vLayout = new QVBoxLayout(dialog); + QGroupBox *groupBox1 = new QGroupBox(dialog); + QGroupBox *groupBox2 = new QGroupBox(dialog); + + QHBoxLayout *hLayout1 = new QHBoxLayout; + QLabel *label_depth1 = new QLabel(dialog); + QSpinBox *spinBox_depth1 = new QSpinBox(dialog); + QLabel *label_time1 = new QLabel(dialog); + QSpinBox *spinBox_time1 = new QSpinBox(dialog); + + QHBoxLayout *hLayout2 = new QHBoxLayout; + QLabel *label_depth2 = new QLabel(dialog); + QSpinBox *spinBox_depth2 = new QSpinBox(dialog); + QLabel *label_time2 = new QLabel(dialog); + QSpinBox *spinBox_time2 = new QSpinBox(dialog); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); + + // 设置各个控件数据 + groupBox1->setTitle(tr("玩家1 AI设置")); + label_depth1->setText(tr("深度")); + spinBox_depth1->setMinimum(1); + spinBox_depth1->setMaximum(99); + label_time1->setText(tr("限时")); + spinBox_time1->setMinimum(1); + spinBox_time1->setMaximum(3600); + + groupBox2->setTitle(tr("玩家2 AI设置")); + label_depth2->setText(tr("深度")); + spinBox_depth2->setMinimum(1); + spinBox_depth2->setMaximum(99); + label_time2->setText(tr("限时")); + spinBox_time2->setMinimum(1); + spinBox_time2->setMaximum(3600); + + buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); + buttonBox->setCenterButtons(true); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定")); + buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消")); + + // 布局控件 + vLayout->addWidget(groupBox1); + vLayout->addWidget(groupBox2); + vLayout->addWidget(buttonBox); + groupBox1->setLayout(hLayout1); + groupBox2->setLayout(hLayout2); + hLayout1->addWidget(label_depth1); + hLayout1->addWidget(spinBox_depth1); + hLayout1->addWidget(label_time1); + hLayout1->addWidget(spinBox_time1); + hLayout2->addWidget(label_depth2); + hLayout2->addWidget(spinBox_depth2); + hLayout2->addWidget(label_time2); + hLayout2->addWidget(spinBox_time2); + + // 关联信号和槽函数 + connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); + + // 目前数据 + NineChessAi_ab::depth_t depth1, depth2; + int time1, time2; + game->getAiDepthTime(depth1, time1, depth2, time2); + spinBox_depth1->setValue(depth1); + spinBox_depth2->setValue(depth2); + spinBox_time1->setValue(time1); + spinBox_time2->setValue(time2); + + // 新设数据 + if (dialog->exec() == QDialog::Accepted) { + NineChessAi_ab::depth_t depth1_new, depth2_new; + int time1_new, time2_new; + depth1_new = spinBox_depth1->value(); + depth2_new = spinBox_depth2->value(); + time1_new = spinBox_time1->value(); + time2_new = spinBox_time2->value(); + + if (depth1 != depth1_new || + depth2 != depth2_new || + time1 != time1_new || + time2 != time2_new) { + // 重置AI + game->setAiDepthTime(depth1_new, time1_new, depth2_new, time2_new); + } + } + + // 删除对话框,子控件会一并删除 + dialog->disconnect(); + delete dialog; +} + +void NineChessWindow::on_actionViewHelp_V_triggered() +{ + QDesktopServices::openUrl(QUrl("https://blog.csdn.net/liuweilhy/article/details/83832180")); +} + +void NineChessWindow::on_actionWeb_W_triggered() +{ + QDesktopServices::openUrl(QUrl("http://hy-tech.top")); +} + +void NineChessWindow::on_actionAbout_A_triggered() +{ + QDialog *dialog = new QDialog; + + dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint); + dialog->setObjectName(QStringLiteral("aboutDialog")); + dialog->setWindowTitle(tr("三棋")); + dialog->setModal(true); + + // 生成各个控件 + QVBoxLayout *vLayout = new QVBoxLayout(dialog); + QHBoxLayout *hLayout = new QHBoxLayout; + QLabel *label_icon1 = new QLabel(dialog); + QLabel *label_icon2 = new QLabel(dialog); + QLabel *label_text = new QLabel(dialog); + QLabel *label_image = new QLabel(dialog); + + // 设置各个控件数据 + label_icon1->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/black_piece.png"))); + label_icon2->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/white_piece.png"))); + label_icon1->setAlignment(Qt::AlignCenter); + label_icon2->setAlignment(Qt::AlignCenter); + label_icon1->setFixedSize(32, 32); + label_icon2->setFixedSize(32, 32); + label_icon1->setScaledContents(true); + label_icon2->setScaledContents(true); + + label_text->setText(tr("Donate")); + label_text->setAlignment(Qt::AlignCenter); + label_image->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/donate.png"))); + label_image->setAlignment(Qt::AlignCenter); + label_image->setScaledContents(true); + + // 布局 + vLayout->addLayout(hLayout); + hLayout->addWidget(label_icon1); + hLayout->addWidget(label_icon2); + hLayout->addWidget(label_text); + vLayout->addWidget(label_image); + + // 运行对话框 + dialog->exec(); + + // 删除对话框 + dialog->disconnect(); + delete dialog; +} diff --git a/NineChess/src/pieceitem.cpp b/NineChess/src/pieceitem.cpp index 679cd0f3..e37601d0 100644 --- a/NineChess/src/pieceitem.cpp +++ b/NineChess/src/pieceitem.cpp @@ -46,7 +46,7 @@ PieceItem::PieceItem(QGraphicsItem *parent) : //setAcceptedMouseButtons(Qt::LeftButton); // 不接受鼠标事件 - setAcceptedMouseButtons(0); + setAcceptedMouseButtons(nullptr); //setAcceptHoverEvents(true); // 默认模型为没有棋子 @@ -105,6 +105,8 @@ void PieceItem::paint(QPainter *painter, painter->drawPixmap(-size / 2, -size / 2, size, size, QPixmap(":/image/resources/image/white_piece.png")); break; + default: + break; } // 如果模型要求显示序号 diff --git a/NineChess/src/server.h b/NineChess/src/server.h index 9734652d..0b0faf94 100644 --- a/NineChess/src/server.h +++ b/NineChess/src/server.h @@ -16,48 +16,48 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. - *****************************************************************************/ - -#ifndef SERVER_H -#define SERVER_H - -#include -#include -#include - -QT_BEGIN_NAMESPACE -class QLabel; -class QTcpServer; -class QNetworkSession; -QT_END_NAMESPACE - -class Server : public QDialog -{ - Q_OBJECT - -public: - explicit Server(QWidget *parent = nullptr, uint16_t port = 33333); - void setAction(const QString &action); - void setPort(uint16_t port) - { - this->port = port; - } - uint16_t getPort() - { - return port; - } - -private slots: - void sessionOpened(); - void sendAction(); - -private: - QLabel *statusLabel = nullptr; - QTcpServer *tcpServer = nullptr; - QVector actions; - QString action; - QNetworkSession *networkSession = nullptr; - uint16_t port; -}; - -#endif // SERVER_H + *****************************************************************************/ + +#ifndef SERVER_H +#define SERVER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QLabel; +class QTcpServer; +class QNetworkSession; +QT_END_NAMESPACE + +class Server : public QDialog +{ + Q_OBJECT + +public: + explicit Server(QWidget *parent = nullptr, uint16_t port = 33333); + void setAction(const QString &action); + void setPort(uint16_t port) + { + this->port = port; + } + uint16_t getPort() + { + return port; + } + +private slots: + void sessionOpened(); + void sendAction(); + +private: + QLabel *statusLabel = nullptr; + QTcpServer *tcpServer = nullptr; + QNetworkSession *networkSession = nullptr; + uint16_t port; + QVector actions; + QString action; +}; + +#endif // SERVER_H