refactor: 着法生成相关逻辑转到 movegen.cpp
顺带将 client.cpp 改为 UTF-8 with BOM, 以通过 Qt 编译.
This commit is contained in:
parent
8fd1303cc3
commit
ae91aa1942
|
@ -23,6 +23,7 @@ INCLUDEPATH += src/ui/qt
|
|||
|
||||
SOURCES += \
|
||||
src/ai/evaluate.cpp \
|
||||
src/ai/movegen.cpp \
|
||||
src/game/millgame.cpp \
|
||||
src/main.cpp \
|
||||
src/base/thread.cpp \
|
||||
|
@ -41,6 +42,7 @@ HEADERS += \
|
|||
include/version.h \
|
||||
include/version.h.template \
|
||||
src/ai/evaluate.h \
|
||||
src/ai/movegen.h \
|
||||
src/base/HashNode.h \
|
||||
src/base/debug.h \
|
||||
src/base/hashMap.h \
|
||||
|
|
|
@ -442,6 +442,7 @@
|
|||
</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="src\ai\evaluate.h" />
|
||||
<ClInclude Include="src\ai\movegen.h" />
|
||||
<ClInclude Include="src\ai\search.h" />
|
||||
<ClInclude Include="src\ai\zobrist.h" />
|
||||
<ClInclude Include="src\base\debug.h" />
|
||||
|
@ -692,6 +693,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\ai\evaluate.cpp" />
|
||||
<ClCompile Include="src\ai\movegen.cpp" />
|
||||
<ClCompile Include="src\ai\search.cpp" />
|
||||
<ClCompile Include="src\base\thread.cpp" />
|
||||
<ClCompile Include="src\game\millgame.cpp" />
|
||||
|
|
|
@ -99,6 +99,9 @@
|
|||
<ClInclude Include="src\ai\evaluate.h">
|
||||
<Filter>ai</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\ai\movegen.h">
|
||||
<Filter>ai</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -310,6 +313,9 @@
|
|||
<ClCompile Include="src\ai\evaluate.cpp">
|
||||
<Filter>ai</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\ai\movegen.cpp">
|
||||
<Filter>ai</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="millgame.rc">
|
||||
|
|
|
@ -0,0 +1,430 @@
|
|||
#include <random>
|
||||
|
||||
#include "movegen.h"
|
||||
|
||||
void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp,
|
||||
MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode,
|
||||
move_t bestMove)
|
||||
{
|
||||
const int MOVE_PRIORITY_TABLE_SIZE = MillGame::N_RINGS * MillGame::N_SEATS;
|
||||
int pos = 0;
|
||||
size_t newCapacity = 24;
|
||||
|
||||
// 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销
|
||||
switch (chessTemp.getStage()) {
|
||||
case MillGame::GAME_PLACING:
|
||||
if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) {
|
||||
if (chessTemp.whosTurn() == MillGame::PLAYER1)
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesInHandCount_1() + chessTemp.getPiecesInHandCount_2());
|
||||
}
|
||||
break;
|
||||
case MillGame::GAME_MOVING:
|
||||
if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) {
|
||||
if (chessTemp.whosTurn() == MillGame::PLAYER1)
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = 6;
|
||||
}
|
||||
break;
|
||||
case MillGame::GAME_NOTSTARTED:
|
||||
newCapacity = 24;
|
||||
break;
|
||||
default:
|
||||
newCapacity = 24;
|
||||
break;
|
||||
};
|
||||
|
||||
node->children.reserve(newCapacity + 2 /* TODO: 未细调故再多留余量2 */);
|
||||
|
||||
// 如果有子节点,则返回,避免重复建立
|
||||
if (!node->children.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 对手
|
||||
MillGame::Player opponent = MillGame::getOpponent(chessTemp.context.turn);
|
||||
|
||||
// 列出所有合法的下一招
|
||||
switch (chessTemp.context.action) {
|
||||
// 对于选子和落子动作
|
||||
case MillGame::ACTION_CHOOSE:
|
||||
case MillGame::ACTION_PLACE:
|
||||
// 对于摆子阶段
|
||||
if (chessTemp.context.stage & (MillGame::GAME_PLACING | MillGame::GAME_NOTSTARTED)) {
|
||||
for (int i : movePriorityTable) {
|
||||
pos = i;
|
||||
|
||||
if (chessTemp.board_[pos]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chessTemp.context.stage != MillGame::GAME_NOTSTARTED || node != rootNode) {
|
||||
ai_ab.addNode(node, 0, pos, bestMove, chessTemp.context.turn);
|
||||
} else {
|
||||
// 若为先手,则抢占星位
|
||||
if (MillGame::isStarPoint(pos)) {
|
||||
ai_ab.addNode(node, MillGameAi_ab::INF_VALUE, pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 对于移子阶段
|
||||
if (chessTemp.context.stage & MillGame::GAME_MOVING) {
|
||||
int newPos, oldPos;
|
||||
|
||||
// 尽量走理论上较差的位置的棋子
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
oldPos = movePriorityTable[i];
|
||||
|
||||
if (!chessTemp.choose(oldPos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((chessTemp.context.turn == MillGame::PLAYER1 &&
|
||||
(chessTemp.context.nPiecesOnBoard_1 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces)) ||
|
||||
(chessTemp.context.turn == MillGame::PLAYER2 &&
|
||||
(chessTemp.context.nPiecesOnBoard_2 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
// 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中
|
||||
for (int moveDirection = MillGame::MOVE_DIRECTION_CLOCKWISE; moveDirection <= MillGame::MOVE_DIRECTION_OUTWARD; moveDirection++) {
|
||||
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中
|
||||
newPos = moveTable[oldPos][moveDirection];
|
||||
if (newPos && !chessTemp.board_[newPos]) {
|
||||
int move = (oldPos << 8) + newPos;
|
||||
ai_ab.addNode(node, 0, move, bestMove, chessTemp.context.turn); // (12%)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行
|
||||
for (newPos = MillGame::POS_BEGIN; newPos < MillGame::POS_END; newPos++) {
|
||||
if (!chessTemp.board_[newPos]) {
|
||||
int move = (oldPos << 8) + newPos;
|
||||
ai_ab.addNode(node, 0, move, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// 对于吃子动作
|
||||
case MillGame::ACTION_CAPTURE:
|
||||
if (chessTemp.isAllInMills(opponent)) {
|
||||
// 全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
pos = movePriorityTable[i];
|
||||
if (chessTemp.board_[pos] & opponent) {
|
||||
ai_ab.addNode(node, 0, -pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 不是全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
pos = movePriorityTable[i];
|
||||
if (chessTemp.board_[pos] & opponent) {
|
||||
if (chessTemp.getRule()->allowRemoveMill || !chessTemp.isInMills(pos)) {
|
||||
ai_ab.addNode(node, 0, -pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MoveList::createMoveTable(MillGame &chess)
|
||||
{
|
||||
#ifdef CONST_MOVE_TABLE
|
||||
#if 1
|
||||
const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {9, 15, 16, 0},
|
||||
/* 9 */ {17, 8, 10, 0},
|
||||
/* 10 */ {9, 11, 18, 0},
|
||||
/* 11 */ {19, 10, 12, 0},
|
||||
/* 12 */ {11, 13, 20, 0},
|
||||
/* 13 */ {21, 12, 14, 0},
|
||||
/* 14 */ {13, 15, 22, 0},
|
||||
/* 15 */ {23, 8, 14, 0},
|
||||
|
||||
/* 16 */ {17, 23, 8, 24},
|
||||
/* 17 */ {9, 25, 16, 18},
|
||||
/* 18 */ {17, 19, 10, 26},
|
||||
/* 19 */ {11, 27, 18, 20},
|
||||
/* 20 */ {19, 21, 12, 28},
|
||||
/* 21 */ {13, 29, 20, 22},
|
||||
/* 22 */ {21, 23, 14, 30},
|
||||
/* 23 */ {15, 31, 16, 22},
|
||||
|
||||
/* 24 */ {25, 31, 16, 0},
|
||||
/* 25 */ {17, 24, 26, 0},
|
||||
/* 26 */ {25, 27, 18, 0},
|
||||
/* 27 */ {19, 26, 28, 0},
|
||||
/* 28 */ {27, 29, 20, 0},
|
||||
/* 29 */ {21, 28, 30, 0},
|
||||
/* 30 */ {29, 31, 22, 0},
|
||||
/* 31 */ {23, 24, 30, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
|
||||
const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {16, 9, 15, 0},
|
||||
/* 9 */ {10, 8, 0, 0},
|
||||
/* 10 */ {18, 11, 9, 0},
|
||||
/* 11 */ {12, 10, 0, 0},
|
||||
/* 12 */ {20, 13, 11, 0},
|
||||
/* 13 */ {14, 12, 0, 0},
|
||||
/* 14 */ {22, 15, 13, 0},
|
||||
/* 15 */ {8, 14, 0, 0},
|
||||
|
||||
/* 16 */ {8, 24, 17, 23},
|
||||
/* 17 */ {18, 16, 0, 0},
|
||||
/* 18 */ {10, 26, 19, 17},
|
||||
/* 19 */ {20, 18, 0, 0},
|
||||
/* 20 */ {12, 28, 21, 19},
|
||||
/* 21 */ {22, 20, 0, 0},
|
||||
/* 22 */ {14, 30, 23, 21},
|
||||
/* 23 */ {16, 22, 0, 0},
|
||||
|
||||
/* 24 */ {16, 25, 31, 0},
|
||||
/* 25 */ {26, 24, 0, 0},
|
||||
/* 26 */ {18, 27, 25, 0},
|
||||
/* 27 */ {28, 26, 0, 0},
|
||||
/* 28 */ {20, 29, 27, 0},
|
||||
/* 29 */ {30, 28, 0, 0},
|
||||
/* 30 */ {22, 31, 29, 0},
|
||||
/* 31 */ {24, 30, 0, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
#else
|
||||
const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{9, 15, 0, 16},
|
||||
{10, 8, 0, 17},
|
||||
{11, 9, 0, 18},
|
||||
{12, 10, 0, 19},
|
||||
{13, 11, 0, 20},
|
||||
{14, 12, 0, 21},
|
||||
{15, 13, 0, 22},
|
||||
{8, 14, 0, 23},
|
||||
|
||||
{17, 23, 8, 24},
|
||||
{18, 16, 9, 25},
|
||||
{19, 17, 10, 26},
|
||||
{20, 18, 11, 27},
|
||||
{21, 19, 12, 28},
|
||||
{22, 20, 13, 29},
|
||||
{23, 21, 14, 30},
|
||||
{16, 22, 15, 31},
|
||||
|
||||
{25, 31, 16, 0},
|
||||
{26, 24, 17, 0},
|
||||
{27, 25, 18, 0},
|
||||
{28, 26, 19, 0},
|
||||
{29, 27, 20, 0},
|
||||
{30, 28, 21, 0},
|
||||
{31, 29, 22, 0},
|
||||
{24, 30, 23, 0},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {9, 15, 0, 16},
|
||||
/* 9 */ {10, 8, 0, 0},
|
||||
/* 10 */ {11, 9, 0, 18},
|
||||
/* 11 */ {12, 10, 0, 0},
|
||||
/* 12 */ {13, 11, 0, 20},
|
||||
/* 13 */ {14, 12, 0, 0},
|
||||
/* 14 */ {15, 13, 0, 22},
|
||||
/* 15 */ {8, 14, 0, 0},
|
||||
|
||||
/* 16 */ {17, 23, 8, 24},
|
||||
/* 17 */ {18, 16, 0, 0},
|
||||
/* 18 */ {19, 17, 10, 26},
|
||||
/* 19 */ {20, 18, 0, 0},
|
||||
/* 20 */ {21, 19, 12, 28},
|
||||
/* 21 */ {22, 20, 0, 0},
|
||||
/* 22 */ {23, 21, 14, 30},
|
||||
/* 23 */ {16, 22, 0, 0},
|
||||
|
||||
/* 24 */ {25, 31, 16, 0},
|
||||
/* 25 */ {26, 24, 0, 0},
|
||||
/* 26 */ {27, 25, 18, 0},
|
||||
/* 27 */ {28, 26, 0, 0},
|
||||
/* 28 */ {29, 27, 20, 0},
|
||||
/* 29 */ {30, 28, 0, 0},
|
||||
/* 30 */ {31, 29, 22, 0},
|
||||
/* 31 */ {24, 30, 0, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
if (chess.currentRule.hasObliqueLines) {
|
||||
memcpy(moveTable, moveTable_obliqueLine, sizeof(moveTable));
|
||||
} else {
|
||||
memcpy(moveTable, moveTable_noObliqueLine, sizeof(moveTable));
|
||||
}
|
||||
|
||||
#else /* CONST_MOVE_TABLE */
|
||||
|
||||
for (int r = 1; r <= N_RINGS; r++) {
|
||||
for (int s = 0; s < N_SEATS; s++) {
|
||||
int p = r * N_SEATS + s;
|
||||
|
||||
// 顺时针走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_CLOCKWISE] = r * N_SEATS + (s + 1) % N_SEATS;
|
||||
|
||||
// 逆时针走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_ANTICLOCKWISE] = r * N_SEATS + (s + N_SEATS - 1) % N_SEATS;
|
||||
|
||||
// 如果是 0、2、4、6位(偶数位)或是有斜线
|
||||
if (!(s & 1) || this->currentRule.hasObliqueLines) {
|
||||
if (r > 1) {
|
||||
// 向内走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_INWARD] = (r - 1) * N_SEATS + s;
|
||||
}
|
||||
|
||||
if (r < N_RINGS) {
|
||||
// 向外走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_OUTWARD] = (r + 1) * N_SEATS + s;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
// 对于无斜线情况下的1、3、5、7位(奇数位),则都设为棋盘外点(默认'\x00')
|
||||
else {
|
||||
// 向内走一步的位置设为随便棋盘外一点
|
||||
moveTable[i * SEAT + j][2] = '\x00';
|
||||
// 向外走一步的位置设为随便棋盘外一点
|
||||
moveTable[i * SEAT + j][3] = '\x00';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif /* CONST_MOVE_TABLE */
|
||||
|
||||
#if 0
|
||||
int sum = 0;
|
||||
for (int i = 0; i < N_POINTS; i++) {
|
||||
loggerDebug("/* %d */ {", i);
|
||||
for (int j = 0; j < N_MOVE_DIRECTIONS; j++) {
|
||||
if (j == N_MOVE_DIRECTIONS - 1)
|
||||
loggerDebug("%d", moveTable[i][j]);
|
||||
else
|
||||
loggerDebug("%d, ", moveTable[i][j]);
|
||||
sum += moveTable[i][j];
|
||||
}
|
||||
loggerDebug("},\n");
|
||||
}
|
||||
loggerDebug("sum = %d\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void MoveList::shuffleMovePriorityTable(MillGame & chess)
|
||||
{
|
||||
array<int, 4> movePriorityTable0 = { 17, 19, 21, 23 }; // 中圈四个顶点 (星位)
|
||||
array<int, 8> movePriorityTable1 = { 25, 27, 29, 31, 9, 11, 13, 15 }; // 外圈和内圈四个顶点
|
||||
array<int, 4> movePriorityTable2 = { 16, 18, 20, 22 }; // 中圈十字架
|
||||
array<int, 8> movePriorityTable3 = { 24, 26, 28, 30, 8, 10, 12, 14 }; // 外内圈十字架
|
||||
|
||||
if (chess.getRandomMove() == true) {
|
||||
uint32_t seed = static_cast<uint32_t>(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
std::shuffle(movePriorityTable0.begin(), movePriorityTable0.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable1.begin(), movePriorityTable1.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable2.begin(), movePriorityTable2.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable3.begin(), movePriorityTable3.end(), std::default_random_engine(seed));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
movePriorityTable[i + 0] = movePriorityTable0[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
movePriorityTable[i + 4] = movePriorityTable1[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
movePriorityTable[i + 12] = movePriorityTable2[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
movePriorityTable[i + 16] = movePriorityTable3[i];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
#ifndef MOVEGEN_H
|
||||
#define MOVEGEN_H
|
||||
|
||||
#include "config.h"
|
||||
#include "millgame.h"
|
||||
#include "search.h"
|
||||
|
||||
class MoveList
|
||||
{
|
||||
public:
|
||||
MoveList() = delete;
|
||||
|
||||
MoveList &operator=(const MoveList &) = delete;
|
||||
|
||||
using move_t = MillGameAi_ab::move_t;
|
||||
|
||||
// 生成所有合法的着法并建立子节点
|
||||
static void generateLegalMoves(MillGameAi_ab &ai_ab, MillGame &chessTemp,
|
||||
MillGameAi_ab::Node *node, MillGameAi_ab::Node *rootNode,
|
||||
move_t bestMove);
|
||||
|
||||
// 生成着法表
|
||||
static void createMoveTable(MillGame &chess);
|
||||
|
||||
// 随机打乱着法搜索顺序
|
||||
static void shuffleMovePriorityTable(MillGame &chess);
|
||||
|
||||
// 着法表 // TODO: Move to private
|
||||
inline static int moveTable[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = { {0} };
|
||||
|
||||
private:
|
||||
// 着法顺序表, 后续会被打乱
|
||||
inline static array<int, MillGame::N_RINGS *MillGame::N_SEATS> movePriorityTable {
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31,
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* MOVEGEN_H */
|
|
@ -21,12 +21,12 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <array>
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
|
||||
#include "search.h"
|
||||
#include "evaluate.h"
|
||||
#include "movegen.h"
|
||||
#include "hashmap.h"
|
||||
|
||||
using namespace CTSL;
|
||||
|
@ -199,178 +199,6 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
|
|||
return newNode;
|
||||
}
|
||||
|
||||
void MillGameAi_ab::shuffleMovePriorityTable()
|
||||
{
|
||||
array<int, 4> movePriorityTable0 = { 17, 19, 21, 23 }; // 中圈四个顶点 (星位)
|
||||
array<int, 8> movePriorityTable1 = { 25, 27, 29, 31, 9, 11, 13, 15 }; // 外圈和内圈四个顶点
|
||||
array<int, 4> movePriorityTable2 = { 16, 18, 20, 22 }; // 中圈十字架
|
||||
array<int, 8> movePriorityTable3 = { 24, 26, 28, 30, 8, 10, 12, 14 }; // 外内圈十字架
|
||||
|
||||
if (chess_.getRandomMove() == true) {
|
||||
uint32_t seed = static_cast<uint32_t>(std::chrono::system_clock::now().time_since_epoch().count());
|
||||
|
||||
std::shuffle(movePriorityTable0.begin(), movePriorityTable0.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable1.begin(), movePriorityTable1.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable2.begin(), movePriorityTable2.end(), std::default_random_engine(seed));
|
||||
std::shuffle(movePriorityTable3.begin(), movePriorityTable3.end(), std::default_random_engine(seed));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
movePriorityTable[i + 0] = movePriorityTable0[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
movePriorityTable[i + 4] = movePriorityTable1[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
movePriorityTable[i + 12] = movePriorityTable2[i];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
movePriorityTable[i + 16] = movePriorityTable3[i];
|
||||
}
|
||||
}
|
||||
|
||||
void MillGameAi_ab::generateLegalMoves(Node *node, move_t bestMove)
|
||||
{
|
||||
const int MOVE_PRIORITY_TABLE_SIZE = MillGame::N_RINGS * MillGame::N_SEATS;
|
||||
int pos = 0;
|
||||
size_t newCapacity = 24;
|
||||
|
||||
// 留足余量空间避免多次重新分配,此动作本身也占用 CPU/内存 开销
|
||||
switch (chessTemp.getStage()) {
|
||||
case MillGame::GAME_PLACING:
|
||||
if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) {
|
||||
if (chessTemp.whosTurn() == MillGame::PLAYER1)
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesInHandCount_1() + chessTemp.getPiecesInHandCount_2());
|
||||
}
|
||||
break;
|
||||
case MillGame::GAME_MOVING:
|
||||
if (chessTemp.getAction() == MillGame::ACTION_CAPTURE) {
|
||||
if (chessTemp.whosTurn() == MillGame::PLAYER1)
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_2());
|
||||
else
|
||||
newCapacity = static_cast<size_t>(chessTemp.getPiecesOnBoardCount_1());
|
||||
} else {
|
||||
newCapacity = 6;
|
||||
}
|
||||
break;
|
||||
case MillGame::GAME_NOTSTARTED:
|
||||
newCapacity = 24;
|
||||
break;
|
||||
default:
|
||||
newCapacity = 24;
|
||||
break;
|
||||
};
|
||||
|
||||
node->children.reserve(newCapacity + 2 /* TODO: 未细调故再多留余量2 */);
|
||||
|
||||
// 如果有子节点,则返回,避免重复建立
|
||||
if (!node->children.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 对手
|
||||
MillGame::Player opponent = MillGame::getOpponent(chessTemp.context.turn);
|
||||
|
||||
// 列出所有合法的下一招
|
||||
switch (chessTemp.context.action) {
|
||||
// 对于选子和落子动作
|
||||
case MillGame::ACTION_CHOOSE:
|
||||
case MillGame::ACTION_PLACE:
|
||||
// 对于摆子阶段
|
||||
if (chessTemp.context.stage & (MillGame::GAME_PLACING | MillGame::GAME_NOTSTARTED)) {
|
||||
for (int i : movePriorityTable) {
|
||||
pos = i;
|
||||
|
||||
if (chessTemp.board_[pos]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chessTemp.context.stage != MillGame::GAME_NOTSTARTED || node != rootNode) {
|
||||
addNode(node, 0, pos, bestMove, chessTemp.context.turn);
|
||||
} else {
|
||||
// 若为先手,则抢占星位
|
||||
if (MillGame::isStarPoint(pos)) {
|
||||
addNode(node, INF_VALUE, pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 对于移子阶段
|
||||
if (chessTemp.context.stage & MillGame::GAME_MOVING) {
|
||||
int newPos, oldPos;
|
||||
|
||||
// 尽量走理论上较差的位置的棋子
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
oldPos = movePriorityTable[i];
|
||||
|
||||
if (!chessTemp.choose(oldPos)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((chessTemp.context.turn == MillGame::PLAYER1 &&
|
||||
(chessTemp.context.nPiecesOnBoard_1 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces)) ||
|
||||
(chessTemp.context.turn == MillGame::PLAYER2 &&
|
||||
(chessTemp.context.nPiecesOnBoard_2 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
// 对于棋盘上还有3个子以上,或不允许飞子的情况,要求必须在着法表中
|
||||
for (int moveDirection = MillGame::MOVE_DIRECTION_CLOCKWISE; moveDirection <= MillGame::MOVE_DIRECTION_OUTWARD; moveDirection++) {
|
||||
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中
|
||||
newPos = MillGame::moveTable[oldPos][moveDirection];
|
||||
if (newPos && !chessTemp.board_[newPos]) {
|
||||
int move = (oldPos << 8) + newPos;
|
||||
addNode(node, 0, move, bestMove, chessTemp.context.turn); // (12%)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 对于棋盘上还有不到3个字,但允许飞子的情况,不要求在着法表中,是空位就行
|
||||
for (newPos = MillGame::POS_BEGIN; newPos < MillGame::POS_END; newPos++) {
|
||||
if (!chessTemp.board_[newPos]) {
|
||||
int move = (oldPos << 8) + newPos;
|
||||
addNode(node, 0, move, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// 对于吃子动作
|
||||
case MillGame::ACTION_CAPTURE:
|
||||
if (chessTemp.isAllInMills(opponent)) {
|
||||
// 全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
pos = movePriorityTable[i];
|
||||
if (chessTemp.board_[pos] & opponent) {
|
||||
addNode(node, 0, -pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 不是全成三的情况
|
||||
for (int i = MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
|
||||
pos = movePriorityTable[i];
|
||||
if (chessTemp.board_[pos] & opponent) {
|
||||
if (chessTemp.getRule()->allowRemoveMill || !chessTemp.isInMills(pos)) {
|
||||
addNode(node, 0, -pos, bestMove, chessTemp.context.turn);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MillGameAi_ab::nodeLess(const Node *first, const Node *second)
|
||||
{
|
||||
#ifdef SORT_CONSIDER_PRUNED
|
||||
|
@ -528,7 +356,7 @@ int MillGameAi_ab::alphaBetaPruning(depth_t depth)
|
|||
#endif // THREEFOLD_REPETITION
|
||||
|
||||
// 随机打乱着法顺序
|
||||
shuffleMovePriorityTable();
|
||||
MoveList::shuffleMovePriorityTable(chess_);
|
||||
|
||||
#ifdef IDS_SUPPORT
|
||||
// 深化迭代
|
||||
|
@ -704,7 +532,7 @@ MillGameAi_ab::value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t al
|
|||
}
|
||||
|
||||
// 生成子节点树,即生成每个合理的着法
|
||||
generateLegalMoves(node, bestMove);
|
||||
MoveList::generateLegalMoves(*this, chessTemp, node, rootNode, bestMove);
|
||||
|
||||
// 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min
|
||||
|
||||
|
|
|
@ -156,10 +156,16 @@ public:
|
|||
static void loadOpeningBookFileToHashMap();
|
||||
#endif // BOOK_LEARNING
|
||||
|
||||
protected:
|
||||
// 生成所有合法的着法并建立子节点
|
||||
void generateLegalMoves(Node *node, move_t bestMove);
|
||||
public: /* TODO: Move to private or protected */
|
||||
// 增加新节点
|
||||
struct Node *addNode(Node *parent, value_t value,
|
||||
move_t move, move_t bestMove,
|
||||
enum MillGame::Player player);
|
||||
|
||||
// 定义极大值
|
||||
static const value_t INF_VALUE = 0x1 << 14;
|
||||
|
||||
protected:
|
||||
// 对合法的着法降序排序
|
||||
void sortLegalMoves(Node *node);
|
||||
|
||||
|
@ -169,11 +175,6 @@ protected:
|
|||
// 构造根节点
|
||||
void buildRoot();
|
||||
|
||||
// 增加新节点
|
||||
struct Node *addNode(Node *parent, value_t value,
|
||||
move_t move, move_t bestMove,
|
||||
enum MillGame::Player player);
|
||||
|
||||
// 评价函数
|
||||
value_t evaluate(Node *node);
|
||||
#ifdef EVALUATE_ENABLE
|
||||
|
@ -208,10 +209,7 @@ protected:
|
|||
|
||||
// 篡改深度
|
||||
depth_t changeDepth(depth_t originalDepth);
|
||||
|
||||
// 随机打乱着法搜索顺序
|
||||
void shuffleMovePriorityTable();
|
||||
|
||||
|
||||
#ifdef HASH_MAP_ENABLE
|
||||
// 查找哈希表
|
||||
bool findHash(MillGame::hash_t hash, HashValue &hashValue);
|
||||
|
@ -264,16 +262,6 @@ private:
|
|||
// 标识,用于跳出剪枝算法,立即返回
|
||||
bool requiredQuit {false};
|
||||
|
||||
// 着法顺序表, 后续会被打乱
|
||||
array<int, MillGame::N_RINGS *MillGame::N_SEATS> movePriorityTable {
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31,
|
||||
};
|
||||
|
||||
// 定义极大值
|
||||
static const value_t INF_VALUE = 0x1 << 14;
|
||||
|
||||
private:
|
||||
// 命令行
|
||||
char cmdline[64] {};
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <algorithm>
|
||||
#include "millgame.h"
|
||||
#include "search.h"
|
||||
#include "movegen.h"
|
||||
|
||||
// 对静态常量数组的定义要放在类外,不要放在头文件
|
||||
// 预定义的4套规则
|
||||
|
@ -123,9 +124,6 @@ const int MillGame::onBoard[N_POINTS] = {
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
// 着法表
|
||||
int MillGame::moveTable[N_POINTS][N_MOVE_DIRECTIONS] = {{0}};
|
||||
|
||||
// 成三表
|
||||
int MillGame::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = {{{0}}};
|
||||
|
||||
|
@ -209,259 +207,6 @@ MillGame::Player MillGame::getOpponent(MillGame::Player player)
|
|||
return NOBODY;
|
||||
}
|
||||
|
||||
void MillGame::createMoveTable()
|
||||
{
|
||||
#ifdef CONST_MOVE_TABLE
|
||||
#if 1
|
||||
const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {9, 15, 16, 0},
|
||||
/* 9 */ {17, 8, 10, 0},
|
||||
/* 10 */ {9, 11, 18, 0},
|
||||
/* 11 */ {19, 10, 12, 0},
|
||||
/* 12 */ {11, 13, 20, 0},
|
||||
/* 13 */ {21, 12, 14, 0},
|
||||
/* 14 */ {13, 15, 22, 0},
|
||||
/* 15 */ {23, 8, 14, 0},
|
||||
|
||||
/* 16 */ {17, 23, 8, 24},
|
||||
/* 17 */ {9, 25, 16, 18},
|
||||
/* 18 */ {17, 19, 10, 26},
|
||||
/* 19 */ {11, 27, 18, 20},
|
||||
/* 20 */ {19, 21, 12, 28},
|
||||
/* 21 */ {13, 29, 20, 22},
|
||||
/* 22 */ {21, 23, 14, 30},
|
||||
/* 23 */ {15, 31, 16, 22},
|
||||
|
||||
/* 24 */ {25, 31, 16, 0},
|
||||
/* 25 */ {17, 24, 26, 0},
|
||||
/* 26 */ {25, 27, 18, 0},
|
||||
/* 27 */ {19, 26, 28, 0},
|
||||
/* 28 */ {27, 29, 20, 0},
|
||||
/* 29 */ {21, 28, 30, 0},
|
||||
/* 30 */ {29, 31, 22, 0},
|
||||
/* 31 */ {23, 24, 30, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
|
||||
const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {16, 9, 15, 0},
|
||||
/* 9 */ {10, 8, 0, 0},
|
||||
/* 10 */ {18, 11, 9, 0},
|
||||
/* 11 */ {12, 10, 0, 0},
|
||||
/* 12 */ {20, 13, 11, 0},
|
||||
/* 13 */ {14, 12, 0, 0},
|
||||
/* 14 */ {22, 15, 13, 0},
|
||||
/* 15 */ {8, 14, 0, 0},
|
||||
|
||||
/* 16 */ {8, 24, 17, 23},
|
||||
/* 17 */ {18, 16, 0, 0},
|
||||
/* 18 */ {10, 26, 19, 17},
|
||||
/* 19 */ {20, 18, 0, 0},
|
||||
/* 20 */ {12, 28, 21, 19},
|
||||
/* 21 */ {22, 20, 0, 0},
|
||||
/* 22 */ {14, 30, 23, 21},
|
||||
/* 23 */ {16, 22, 0, 0},
|
||||
|
||||
/* 24 */ {16, 25, 31, 0},
|
||||
/* 25 */ {26, 24, 0, 0},
|
||||
/* 26 */ {18, 27, 25, 0},
|
||||
/* 27 */ {28, 26, 0, 0},
|
||||
/* 28 */ {20, 29, 27, 0},
|
||||
/* 29 */ {30, 28, 0, 0},
|
||||
/* 30 */ {22, 31, 29, 0},
|
||||
/* 31 */ {24, 30, 0, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
#else
|
||||
const int moveTable_obliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
|
||||
{9, 15, 0, 16},
|
||||
{10, 8, 0, 17},
|
||||
{11, 9, 0, 18},
|
||||
{12, 10, 0, 19},
|
||||
{13, 11, 0, 20},
|
||||
{14, 12, 0, 21},
|
||||
{15, 13, 0, 22},
|
||||
{8, 14, 0, 23},
|
||||
|
||||
{17, 23, 8, 24},
|
||||
{18, 16, 9, 25},
|
||||
{19, 17, 10, 26},
|
||||
{20, 18, 11, 27},
|
||||
{21, 19, 12, 28},
|
||||
{22, 20, 13, 29},
|
||||
{23, 21, 14, 30},
|
||||
{16, 22, 15, 31},
|
||||
|
||||
{25, 31, 16, 0},
|
||||
{26, 24, 17, 0},
|
||||
{27, 25, 18, 0},
|
||||
{28, 26, 19, 0},
|
||||
{29, 27, 20, 0},
|
||||
{30, 28, 21, 0},
|
||||
{31, 29, 22, 0},
|
||||
{24, 30, 23, 0},
|
||||
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
const int moveTable_noObliqueLine[MillGame::N_POINTS][MillGame::N_MOVE_DIRECTIONS] = {
|
||||
/* 0 */ {0, 0, 0, 0},
|
||||
/* 1 */ {0, 0, 0, 0},
|
||||
/* 2 */ {0, 0, 0, 0},
|
||||
/* 3 */ {0, 0, 0, 0},
|
||||
/* 4 */ {0, 0, 0, 0},
|
||||
/* 5 */ {0, 0, 0, 0},
|
||||
/* 6 */ {0, 0, 0, 0},
|
||||
/* 7 */ {0, 0, 0, 0},
|
||||
|
||||
/* 8 */ {9, 15, 0, 16},
|
||||
/* 9 */ {10, 8, 0, 0},
|
||||
/* 10 */ {11, 9, 0, 18},
|
||||
/* 11 */ {12, 10, 0, 0},
|
||||
/* 12 */ {13, 11, 0, 20},
|
||||
/* 13 */ {14, 12, 0, 0},
|
||||
/* 14 */ {15, 13, 0, 22},
|
||||
/* 15 */ {8, 14, 0, 0},
|
||||
|
||||
/* 16 */ {17, 23, 8, 24},
|
||||
/* 17 */ {18, 16, 0, 0},
|
||||
/* 18 */ {19, 17, 10, 26},
|
||||
/* 19 */ {20, 18, 0, 0},
|
||||
/* 20 */ {21, 19, 12, 28},
|
||||
/* 21 */ {22, 20, 0, 0},
|
||||
/* 22 */ {23, 21, 14, 30},
|
||||
/* 23 */ {16, 22, 0, 0},
|
||||
|
||||
/* 24 */ {25, 31, 16, 0},
|
||||
/* 25 */ {26, 24, 0, 0},
|
||||
/* 26 */ {27, 25, 18, 0},
|
||||
/* 27 */ {28, 26, 0, 0},
|
||||
/* 28 */ {29, 27, 20, 0},
|
||||
/* 29 */ {30, 28, 0, 0},
|
||||
/* 30 */ {31, 29, 22, 0},
|
||||
/* 31 */ {24, 30, 0, 0},
|
||||
|
||||
/* 32 */ {0, 0, 0, 0},
|
||||
/* 33 */ {0, 0, 0, 0},
|
||||
/* 34 */ {0, 0, 0, 0},
|
||||
/* 35 */ {0, 0, 0, 0},
|
||||
/* 36 */ {0, 0, 0, 0},
|
||||
/* 37 */ {0, 0, 0, 0},
|
||||
/* 38 */ {0, 0, 0, 0},
|
||||
/* 39 */ {0, 0, 0, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
if (currentRule.hasObliqueLines) {
|
||||
memcpy(moveTable, moveTable_obliqueLine, sizeof(moveTable));
|
||||
} else {
|
||||
memcpy(moveTable, moveTable_noObliqueLine, sizeof(moveTable));
|
||||
}
|
||||
|
||||
#else /* CONST_MOVE_TABLE */
|
||||
|
||||
for (int r = 1; r <= N_RINGS; r++) {
|
||||
for (int s = 0; s < N_SEATS; s++) {
|
||||
int p = r * N_SEATS + s;
|
||||
|
||||
// 顺时针走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_CLOCKWISE] = r * N_SEATS + (s + 1) % N_SEATS;
|
||||
|
||||
// 逆时针走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_ANTICLOCKWISE] = r * N_SEATS + (s + N_SEATS - 1) % N_SEATS;
|
||||
|
||||
// 如果是 0、2、4、6位(偶数位)或是有斜线
|
||||
if (!(s & 1) || this->currentRule.hasObliqueLines) {
|
||||
if (r > 1) {
|
||||
// 向内走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_INWARD] = (r - 1) * N_SEATS + s;
|
||||
}
|
||||
|
||||
if (r < N_RINGS) {
|
||||
// 向外走一步的位置
|
||||
moveTable[p][MOVE_DIRECTION_OUTWARD] = (r + 1) * N_SEATS + s;
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
// 对于无斜线情况下的1、3、5、7位(奇数位),则都设为棋盘外点(默认'\x00')
|
||||
else {
|
||||
// 向内走一步的位置设为随便棋盘外一点
|
||||
moveTable[i * SEAT + j][2] = '\x00';
|
||||
// 向外走一步的位置设为随便棋盘外一点
|
||||
moveTable[i * SEAT + j][3] = '\x00';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif /* CONST_MOVE_TABLE */
|
||||
|
||||
#if 0
|
||||
int sum = 0;
|
||||
for (int i = 0; i < N_POINTS; i++) {
|
||||
loggerDebug("/* %d */ {", i);
|
||||
for (int j = 0; j < N_MOVE_DIRECTIONS; j++) {
|
||||
if (j == N_MOVE_DIRECTIONS - 1)
|
||||
loggerDebug("%d", moveTable[i][j]);
|
||||
else
|
||||
loggerDebug("%d, ", moveTable[i][j]);
|
||||
sum += moveTable[i][j];
|
||||
}
|
||||
loggerDebug("},\n");
|
||||
}
|
||||
loggerDebug("sum = %d\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void MillGame::createMillTable()
|
||||
{
|
||||
#ifdef CONST_MILL_TABLE
|
||||
|
@ -795,7 +540,7 @@ bool MillGame::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int
|
|||
winner = NOBODY;
|
||||
|
||||
// 生成着法表
|
||||
createMoveTable();
|
||||
MoveList::createMoveTable(*this);
|
||||
|
||||
// 生成成三表
|
||||
createMillTable();
|
||||
|
@ -1121,7 +866,7 @@ bool MillGame::place(int pos, int time_p, int8_t rs)
|
|||
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (pos == moveTable[currentPos][i])
|
||||
if (pos == MoveList::moveTable[currentPos][i])
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1810,7 +1555,7 @@ int MillGame::getSurroundedEmptyPosCount(int pos, bool includeFobidden)
|
|||
(context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
int d, movePos;
|
||||
for (d = 0; d < N_MOVE_DIRECTIONS; d++) {
|
||||
movePos = moveTable[pos][d];
|
||||
movePos = MoveList::moveTable[pos][d];
|
||||
if (movePos) {
|
||||
if (board_[movePos] == 0x00 ||
|
||||
(includeFobidden && board_[movePos] == 0x0F)) {
|
||||
|
@ -1857,7 +1602,7 @@ bool MillGame::isSurrounded(int pos)
|
|||
(context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
int i, movePos;
|
||||
for (i = 0; i < 4; i++) {
|
||||
movePos = moveTable[pos][i];
|
||||
movePos = MoveList::moveTable[pos][i];
|
||||
if (movePos && !board_[movePos])
|
||||
break;
|
||||
}
|
||||
|
@ -1891,7 +1636,7 @@ bool MillGame::isAllSurrounded(char ch)
|
|||
}
|
||||
|
||||
for (int d = 0; d < N_MOVE_DIRECTIONS; d++) {
|
||||
movePos = moveTable[i][d];
|
||||
movePos = MoveList::moveTable[i][d];
|
||||
if (movePos && !board_[movePos])
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -556,16 +556,19 @@ public:
|
|||
hash_t updateHashMisc();
|
||||
#endif
|
||||
|
||||
private:
|
||||
// 当前使用的规则
|
||||
struct Rule currentRule {};
|
||||
|
||||
public: /* TODO: move to private */
|
||||
// 棋局上下文
|
||||
struct ChessContext context;
|
||||
|
||||
// 当前使用的规则
|
||||
struct Rule currentRule
|
||||
{
|
||||
};
|
||||
|
||||
// 棋局上下文中的棋盘数据,单独提出来
|
||||
int *board_;
|
||||
|
||||
private:
|
||||
// 棋局哈希值
|
||||
// uint64_t hash;
|
||||
|
||||
|
|
|
@ -1,203 +1,203 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (C) 2019 MillGame authors
|
||||
*
|
||||
* Authors: Calcitem <calcitem@outlook.com>
|
||||
*
|
||||
* 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 <QtWidgets>
|
||||
#include <QtNetwork>
|
||||
|
||||
#include "client.h"
|
||||
#include "thread.h"
|
||||
|
||||
Client::Client(QWidget *parent, uint16_t port)
|
||||
: QDialog(parent)
|
||||
, hostCombo(new QComboBox)
|
||||
, portLineEdit(new QLineEdit)
|
||||
, getActionButton(new QPushButton(tr("Connect")))
|
||||
, tcpSocket(new QTcpSocket(this))
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
hostCombo->setEditable(true);
|
||||
hostCombo->addItem(QString("localhost"));
|
||||
portLineEdit->setValidator(new QIntValidator(1, 65535, this));
|
||||
|
||||
auto hostLabel = new QLabel(tr("&Server name:"));
|
||||
hostLabel->setBuddy(hostCombo);
|
||||
auto portLabel = new QLabel(tr("S&erver port:"));
|
||||
portLabel->setBuddy(portLineEdit);
|
||||
|
||||
statusLabel = new QLabel(tr("This Client requires that you run the "
|
||||
"Server as well."));
|
||||
|
||||
getActionButton->setDefault(true);
|
||||
getActionButton->setEnabled(false);
|
||||
|
||||
auto quitButton = new QPushButton(tr("Close"));
|
||||
|
||||
auto buttonBox = new QDialogButtonBox;
|
||||
buttonBox->addButton(getActionButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
in.setDevice(tcpSocket);
|
||||
in.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
connect(hostCombo, &QComboBox::editTextChanged,
|
||||
this, &Client::enableGetActionButton);
|
||||
connect(portLineEdit, &QLineEdit::textChanged,
|
||||
this, &Client::enableGetActionButton);
|
||||
connect(getActionButton, &QAbstractButton::clicked,
|
||||
this, &Client::requestNewAction);
|
||||
connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
|
||||
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readAction);
|
||||
connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &Client::displayError);
|
||||
|
||||
QGridLayout *mainLayout = nullptr;
|
||||
if (QGuiApplication::styleHints()->showIsFullScreen() || QGuiApplication::styleHints()->showIsMaximized()) {
|
||||
auto outerVerticalLayout = new QVBoxLayout(this);
|
||||
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
|
||||
auto outerHorizontalLayout = new QHBoxLayout;
|
||||
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
|
||||
auto groupBox = new QGroupBox(QGuiApplication::applicationDisplayName());
|
||||
mainLayout = new QGridLayout(groupBox);
|
||||
outerHorizontalLayout->addWidget(groupBox);
|
||||
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
|
||||
outerVerticalLayout->addLayout(outerHorizontalLayout);
|
||||
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
|
||||
} else {
|
||||
mainLayout = new QGridLayout(this);
|
||||
}
|
||||
|
||||
mainLayout->addWidget(hostLabel, 0, 0);
|
||||
mainLayout->addWidget(hostCombo, 0, 1);
|
||||
mainLayout->addWidget(portLabel, 1, 0);
|
||||
mainLayout->addWidget(portLineEdit, 1, 1);
|
||||
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
|
||||
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
|
||||
|
||||
setWindowTitle(QGuiApplication::applicationDisplayName());
|
||||
portLineEdit->setFocus();
|
||||
portLineEdit->setText(QString::number(port));
|
||||
|
||||
QNetworkConfigurationManager manager;
|
||||
|
||||
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) {
|
||||
// Get saved network configuration
|
||||
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
|
||||
settings.beginGroup(QLatin1String("QtNetwork"));
|
||||
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
|
||||
settings.endGroup();
|
||||
|
||||
// If the saved network configuration is not currently discovered use the system default
|
||||
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
|
||||
if ((config.state() & QNetworkConfiguration::Discovered) !=
|
||||
QNetworkConfiguration::Discovered) {
|
||||
config = manager.defaultConfiguration();
|
||||
}
|
||||
|
||||
networkSession = new QNetworkSession(config, this);
|
||||
connect(networkSession, &QNetworkSession::opened, this, &Client::sessionOpened);
|
||||
|
||||
getActionButton->setEnabled(false);
|
||||
statusLabel->setText(tr("Opening network session."));
|
||||
networkSession->open();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::requestNewAction()
|
||||
{
|
||||
getActionButton->setEnabled(false);
|
||||
tcpSocket->abort();
|
||||
tcpSocket->connectToHost(hostCombo->currentText(),
|
||||
portLineEdit->text().toUShort());
|
||||
}
|
||||
|
||||
void Client::readAction()
|
||||
{
|
||||
QString nextAction;
|
||||
in >> nextAction;
|
||||
|
||||
if (nextAction == currentAction) {
|
||||
QTimer::singleShot(0, this, &Client::requestNewAction);
|
||||
return;
|
||||
}
|
||||
|
||||
currentAction = nextAction;
|
||||
statusLabel->setText(currentAction);
|
||||
emit command(currentAction);
|
||||
getActionButton->setEnabled(true);
|
||||
|
||||
QTimer::singleShot(10, this, &Client::requestNewAction);
|
||||
}
|
||||
|
||||
void Client::displayError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
switch (socketError) {
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The host was not found. Please check the "
|
||||
"host name and port settings."));
|
||||
break;
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The connection was refused by the peer. "
|
||||
"Make sure the server is running, "
|
||||
"and check that the host name and port "
|
||||
"settings are correct."));
|
||||
break;
|
||||
default:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The following error occurred: %1.")
|
||||
.arg(tcpSocket->errorString()));
|
||||
}
|
||||
|
||||
getActionButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void Client::enableGetActionButton()
|
||||
{
|
||||
getActionButton->setEnabled((!networkSession || networkSession->isOpen()) &&
|
||||
!hostCombo->currentText().isEmpty() &&
|
||||
!portLineEdit->text().isEmpty());
|
||||
|
||||
}
|
||||
|
||||
void Client::sessionOpened()
|
||||
{
|
||||
// Save the used configuration
|
||||
QNetworkConfiguration config = networkSession->configuration();
|
||||
QString id;
|
||||
if (config.type() == QNetworkConfiguration::UserChoice)
|
||||
id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();
|
||||
else
|
||||
id = config.identifier();
|
||||
|
||||
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
|
||||
settings.beginGroup(QLatin1String("QtNetwork"));
|
||||
settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);
|
||||
settings.endGroup();
|
||||
|
||||
statusLabel->setText(tr("This Client requires that you run the "
|
||||
"Server as well."));
|
||||
|
||||
enableGetActionButton();
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Copyright (C) 2019 MillGame authors
|
||||
*
|
||||
* Authors: Calcitem <calcitem@outlook.com>
|
||||
*
|
||||
* 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 <QtWidgets>
|
||||
#include <QtNetwork>
|
||||
|
||||
#include "client.h"
|
||||
#include "thread.h"
|
||||
|
||||
Client::Client(QWidget *parent, uint16_t port)
|
||||
: QDialog(parent)
|
||||
, hostCombo(new QComboBox)
|
||||
, portLineEdit(new QLineEdit)
|
||||
, getActionButton(new QPushButton(tr("Connect")))
|
||||
, tcpSocket(new QTcpSocket(this))
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
hostCombo->setEditable(true);
|
||||
hostCombo->addItem(QString("localhost"));
|
||||
portLineEdit->setValidator(new QIntValidator(1, 65535, this));
|
||||
|
||||
auto hostLabel = new QLabel(tr("&Server name:"));
|
||||
hostLabel->setBuddy(hostCombo);
|
||||
auto portLabel = new QLabel(tr("S&erver port:"));
|
||||
portLabel->setBuddy(portLineEdit);
|
||||
|
||||
statusLabel = new QLabel(tr("This Client requires that you run the "
|
||||
"Server as well."));
|
||||
|
||||
getActionButton->setDefault(true);
|
||||
getActionButton->setEnabled(false);
|
||||
|
||||
auto quitButton = new QPushButton(tr("Close"));
|
||||
|
||||
auto buttonBox = new QDialogButtonBox;
|
||||
buttonBox->addButton(getActionButton, QDialogButtonBox::ActionRole);
|
||||
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
|
||||
|
||||
in.setDevice(tcpSocket);
|
||||
in.setVersion(QDataStream::Qt_4_0);
|
||||
|
||||
connect(hostCombo, &QComboBox::editTextChanged,
|
||||
this, &Client::enableGetActionButton);
|
||||
connect(portLineEdit, &QLineEdit::textChanged,
|
||||
this, &Client::enableGetActionButton);
|
||||
connect(getActionButton, &QAbstractButton::clicked,
|
||||
this, &Client::requestNewAction);
|
||||
connect(quitButton, &QAbstractButton::clicked, this, &QWidget::close);
|
||||
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readAction);
|
||||
connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &Client::displayError);
|
||||
|
||||
QGridLayout *mainLayout = nullptr;
|
||||
if (QGuiApplication::styleHints()->showIsFullScreen() || QGuiApplication::styleHints()->showIsMaximized()) {
|
||||
auto outerVerticalLayout = new QVBoxLayout(this);
|
||||
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
|
||||
auto outerHorizontalLayout = new QHBoxLayout;
|
||||
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
|
||||
auto groupBox = new QGroupBox(QGuiApplication::applicationDisplayName());
|
||||
mainLayout = new QGridLayout(groupBox);
|
||||
outerHorizontalLayout->addWidget(groupBox);
|
||||
outerHorizontalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Ignored));
|
||||
outerVerticalLayout->addLayout(outerHorizontalLayout);
|
||||
outerVerticalLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
|
||||
} else {
|
||||
mainLayout = new QGridLayout(this);
|
||||
}
|
||||
|
||||
mainLayout->addWidget(hostLabel, 0, 0);
|
||||
mainLayout->addWidget(hostCombo, 0, 1);
|
||||
mainLayout->addWidget(portLabel, 1, 0);
|
||||
mainLayout->addWidget(portLineEdit, 1, 1);
|
||||
mainLayout->addWidget(statusLabel, 2, 0, 1, 2);
|
||||
mainLayout->addWidget(buttonBox, 3, 0, 1, 2);
|
||||
|
||||
setWindowTitle(QGuiApplication::applicationDisplayName());
|
||||
portLineEdit->setFocus();
|
||||
portLineEdit->setText(QString::number(port));
|
||||
|
||||
QNetworkConfigurationManager manager;
|
||||
|
||||
if (manager.capabilities() & QNetworkConfigurationManager::NetworkSessionRequired) {
|
||||
// Get saved network configuration
|
||||
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
|
||||
settings.beginGroup(QLatin1String("QtNetwork"));
|
||||
const QString id = settings.value(QLatin1String("DefaultNetworkConfiguration")).toString();
|
||||
settings.endGroup();
|
||||
|
||||
// If the saved network configuration is not currently discovered use the system default
|
||||
QNetworkConfiguration config = manager.configurationFromIdentifier(id);
|
||||
if ((config.state() & QNetworkConfiguration::Discovered) !=
|
||||
QNetworkConfiguration::Discovered) {
|
||||
config = manager.defaultConfiguration();
|
||||
}
|
||||
|
||||
networkSession = new QNetworkSession(config, this);
|
||||
connect(networkSession, &QNetworkSession::opened, this, &Client::sessionOpened);
|
||||
|
||||
getActionButton->setEnabled(false);
|
||||
statusLabel->setText(tr("Opening network session."));
|
||||
networkSession->open();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::requestNewAction()
|
||||
{
|
||||
getActionButton->setEnabled(false);
|
||||
tcpSocket->abort();
|
||||
tcpSocket->connectToHost(hostCombo->currentText(),
|
||||
portLineEdit->text().toUShort());
|
||||
}
|
||||
|
||||
void Client::readAction()
|
||||
{
|
||||
QString nextAction;
|
||||
in >> nextAction;
|
||||
|
||||
if (nextAction == currentAction) {
|
||||
QTimer::singleShot(0, this, &Client::requestNewAction);
|
||||
return;
|
||||
}
|
||||
|
||||
currentAction = nextAction;
|
||||
statusLabel->setText(currentAction);
|
||||
emit command(currentAction);
|
||||
getActionButton->setEnabled(true);
|
||||
|
||||
QTimer::singleShot(10, this, &Client::requestNewAction);
|
||||
}
|
||||
|
||||
void Client::displayError(QAbstractSocket::SocketError socketError)
|
||||
{
|
||||
switch (socketError) {
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
break;
|
||||
case QAbstractSocket::HostNotFoundError:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The host was not found. Please check the "
|
||||
"host name and port settings."));
|
||||
break;
|
||||
case QAbstractSocket::ConnectionRefusedError:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The connection was refused by the peer. "
|
||||
"Make sure the server is running, "
|
||||
"and check that the host name and port "
|
||||
"settings are correct."));
|
||||
break;
|
||||
default:
|
||||
QMessageBox::information(this, tr("Client"),
|
||||
tr("The following error occurred: %1.")
|
||||
.arg(tcpSocket->errorString()));
|
||||
}
|
||||
|
||||
getActionButton->setEnabled(true);
|
||||
}
|
||||
|
||||
void Client::enableGetActionButton()
|
||||
{
|
||||
getActionButton->setEnabled((!networkSession || networkSession->isOpen()) &&
|
||||
!hostCombo->currentText().isEmpty() &&
|
||||
!portLineEdit->text().isEmpty());
|
||||
|
||||
}
|
||||
|
||||
void Client::sessionOpened()
|
||||
{
|
||||
// Save the used configuration
|
||||
QNetworkConfiguration config = networkSession->configuration();
|
||||
QString id;
|
||||
if (config.type() == QNetworkConfiguration::UserChoice)
|
||||
id = networkSession->sessionProperty(QLatin1String("UserChoiceConfiguration")).toString();
|
||||
else
|
||||
id = config.identifier();
|
||||
|
||||
QSettings settings(QSettings::UserScope, QLatin1String("QtProject"));
|
||||
settings.beginGroup(QLatin1String("QtNetwork"));
|
||||
settings.setValue(QLatin1String("DefaultNetworkConfiguration"), id);
|
||||
settings.endGroup();
|
||||
|
||||
statusLabel->setText(tr("This Client requires that you run the "
|
||||
"Server as well."));
|
||||
|
||||
enableGetActionButton();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/*****************************************************************************
|
||||
/*****************************************************************************
|
||||
* Copyright (C) 2019 MillGame authors
|
||||
*
|
||||
* Authors: Calcitem <calcitem@outlook.com>
|
||||
|
|
Loading…
Reference in New Issue