refactor: 将 _1 和 _2 形式改为数组形式 [1] 和 [2]

This commit is contained in:
CalciteM Team 2019-09-14 03:06:48 +08:00
parent 01c7c6b12f
commit 2eb0872597
14 changed files with 413 additions and 533 deletions

View File

@ -27,6 +27,7 @@ SOURCES += \
src/ai/tt.cpp \
src/base/misc.cpp \
src/game/board.cpp \
src/game/player.cpp \
src/game/position.cpp \
src/game/rule.cpp \
src/main.cpp \
@ -59,6 +60,7 @@ HEADERS += \
src/ai/search.h \
src/ai/zobrist.h \
src/game/board.h \
src/game/player.h \
src/game/position.h \
src/game/rule.h \
src/game/types.h \

View File

@ -87,12 +87,12 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC
#endif /* EVALUATE_MOBILITY */
switch (positionContext->action) {
// 选子和落子使用相同的评价方法
// 选子和落子使用相同的评价方法
case ACTION_CHOOSE:
case ACTION_PLACE:
break;
// 如果形成去子状态每有一个可去的子算128分
// 如果形成去子状态每有一个可去的子算128分
case ACTION_CAPTURE:
nPiecesNeedRemove = (positionContext->turn == PLAYER_1) ?
positionContext->nPiecesNeedRemove : -(positionContext->nPiecesNeedRemove);
@ -107,17 +107,13 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC
break;
// 终局评价最简单
// 终局评价最简单
case PHASE_GAMEOVER:
// 布局阶段闷棋判断
if (positionContext->nPiecesOnBoard[1] + positionContext->nPiecesOnBoard[2] >=
Board::N_SEATS * Board::N_RINGS) {
if (dummyPosition.getRule()->isStartingPlayerLoseWhenBoardFull) {
// winner = PLAYER_2;
value -= VALUE_WIN;
#ifdef DEBUG_AB_TREE
node->result = -3;
#endif
} else {
value = VALUE_DRAW;
}
@ -128,30 +124,15 @@ value_t Evaluation::getValue(Position &dummyPosition, PositionContext *positionC
dummyPosition.context.board.isAllSurrounded(positionContext->turn, dummyPosition.currentRule, positionContext->nPiecesOnBoard, positionContext->turn) &&
dummyPosition.getRule()->isLoseWhenNoWay) {
// 规则要求被“闷”判负,则对手获胜
if (positionContext->turn == PLAYER_1) {
value -= VALUE_WIN;
#ifdef DEBUG_AB_TREE
node->result = -2;
#endif
} else {
value += VALUE_WIN;
#ifdef DEBUG_AB_TREE
node->result = 2;
#endif
}
value_t delta = positionContext->turn == PLAYER_1 ? -VALUE_WIN : VALUE_WIN;
value += delta;
}
// 剩余棋子个数判断
if (positionContext->nPiecesOnBoard[1] < dummyPosition.getRule()->nPiecesAtLeast) {
value -= VALUE_WIN;
#ifdef DEBUG_AB_TREE
node->result = -1;
#endif
} else if (positionContext->nPiecesOnBoard[2] < dummyPosition.getRule()->nPiecesAtLeast) {
value += VALUE_WIN;
#ifdef DEBUG_AB_TREE
node->result = 1;
#endif
}
break;

View File

@ -22,6 +22,7 @@
#include <random>
#include "movegen.h"
#include "player.h"
#include "misc.h"
void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
@ -36,20 +37,14 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
switch (dummyPosition.getPhase()) {
case PHASE_PLACING:
if (dummyPosition.getAction() == ACTION_CAPTURE) {
if (dummyPosition.whosTurn() == PLAYER_1)
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(2));
else
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(1));
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(dummyPosition.context.opponentId));
} else {
newCapacity = static_cast<size_t>(dummyPosition.getPiecesInHandCount(1) + dummyPosition.getPiecesInHandCount(2));
}
break;
case PHASE_MOVING:
if (dummyPosition.getAction() == ACTION_CAPTURE) {
if (dummyPosition.whosTurn() == PLAYER_1)
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(2));
else
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(1));
newCapacity = static_cast<size_t>(dummyPosition.getPiecesOnBoardCount(dummyPosition.context.opponentId));
} else {
newCapacity = 6;
}
@ -70,7 +65,7 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
}
// 对手
player_t opponent = Position::getOpponent(dummyPosition.context.turn);
player_t opponent = Player::getOpponent(dummyPosition.context.turn);
// 列出所有合法的下一招
switch (dummyPosition.context.action) {
@ -110,10 +105,8 @@ void MoveList::generateLegalMoves(MillGameAi_ab &ai_ab, Position &dummyPosition,
continue;
}
if ((dummyPosition.context.turn == PLAYER_1 &&
(dummyPosition.context.nPiecesOnBoard[1] > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces)) ||
(dummyPosition.context.turn == PLAYER_2 &&
(dummyPosition.context.nPiecesOnBoard[2] > dummyPosition.currentRule.nPiecesAtLeast || !dummyPosition.currentRule.allowFlyWhenRemainThreePieces))) {
if (dummyPosition.context.nPiecesOnBoard[dummyPosition.context.turnId] > dummyPosition.currentRule.nPiecesAtLeast ||
!dummyPosition.currentRule.allowFlyWhenRemainThreePieces) {
// 对于棋盘上还有3个子以上或不允许飞子的情况要求必须在着法表中
for (int direction = DIRECTION_CLOCKWISE; direction <= DIRECTION_OUTWARD; direction++) {
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中

View File

@ -149,7 +149,6 @@ struct MillGameAi_ab::Node *MillGameAi_ab::addNode(
newNode->nPiecesNeedRemove = INT_MAX;
newNode->alpha = -VALUE_INFINITE;
newNode->beta = VALUE_INFINITE;
newNode->result = 0;
newNode->visited = false;
int r, s;
@ -233,11 +232,9 @@ void MillGameAi_ab::sortLegalMoves(Node *node)
{
// 这个函数对效率的影响很大,排序好的话,剪枝较早,节省时间,但不能在此函数耗费太多时间
if (dummyPosition.whosTurn() == PLAYER_1) {
std::stable_sort(node->children.begin(), node->children.end(), nodeGreater);
} else {
std::stable_sort(node->children.begin(), node->children.end(), nodeLess);
}
auto cmp = dummyPosition.context.turn == PLAYER_1 ? nodeGreater : nodeLess;
std::stable_sort(node->children.begin(), node->children.end(), cmp);
}
void MillGameAi_ab::deleteTree(Node *node)
@ -490,11 +487,8 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
evaluatedNodeCount++;
// 为争取速胜value 值 +- 深度 (有必要?)
if (positionContext->turn == PLAYER_1) {
node->value += depth;
} else {
node->value -= depth;
}
value_t delta = value_t(positionContext->turn == PLAYER_1 ? depth : -depth);
node->value += delta;
#ifdef DEBUG_AB_TREE
if (requiredQuit) {
@ -505,8 +499,8 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
#ifdef BOOK_LEARNING
// 检索开局库
if (positionContext->phase == GAME_PLACING && findBookHash(hash, hashValue)) {
if (positionContext->turn == PLAYER_2) {
// 是否需对后手扣分 // TODO: 先后手都处理
if (positionContext->turn == ???) {
// TODO:
node->value += 1;
}
}
@ -525,7 +519,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
// 根据演算模型执行 MiniMax 检索,对先手,搜索 Max, 对后手,搜索 Min
minMax = dummyPosition.whosTurn() == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE;
minMax = dummyPosition.context.turn == PLAYER_1 ? -VALUE_INFINITE : VALUE_INFINITE;
for (auto child : node->children) {
// 上下文入栈保存,以便后续撤销着法
@ -556,7 +550,7 @@ value_t MillGameAi_ab::alphaBetaPruning(depth_t depth, value_t alpha, value_t be
dummyPosition.context = contextStack.top();
contextStack.pop();
if (dummyPosition.whosTurn() == PLAYER_1) {
if (dummyPosition.context.turn == PLAYER_1) {
// 为走棋一方的层, 局面对走棋的一方来说是以 α 为评价
// 取最大值
@ -689,12 +683,11 @@ const char* MillGameAi_ab::bestMove()
if (position_.getGiveUpIfMostLose() == true) {
bool isMostLose = true; // 是否必败
player_t whosTurn = position_.whosTurn();
player_t whosTurn = position_.context.turn;
for (auto child : rootNode->children) {
// TODO: 使用常量代替
if ((whosTurn == PLAYER_1 && child->value > -10000) ||
(whosTurn == PLAYER_2 && child->value < 10000)) {
if ((whosTurn == PLAYER_1 && child->value > -VALUE_WIN) ||
(whosTurn == PLAYER_2 && child->value < VALUE_WIN)) {
isMostLose = false;
break;
}
@ -702,12 +695,7 @@ const char* MillGameAi_ab::bestMove()
// 自动认输
if (isMostLose) {
if (whosTurn == PLAYER_1) {
sprintf(cmdline, "Player1 give up!");
} else if (whosTurn == PLAYER_2) {
sprintf(cmdline, "Player2 give up!");
}
sprintf(cmdline, "Player%d give up!", position_.context.turnChar);
return cmdline;
}
}

View File

@ -80,7 +80,6 @@ public:
int nPiecesOnBoardDiff; // 场上棋子个数和对手的差值
int nPiecesInHandDiff; // 手中的棋子个数和对手的差值
int nPiecesNeedRemove; // 手中有多少可去的子,如对手有可去的子则为负数
int result; // 终局结果,-1为负0为未到终局1为胜走棋阶段被闷棋则为 -2/2布局阶段闷棋为 -3
struct Node* root; // 根节点
#ifdef TRANSPOSITION_TABLE_ENABLE
bool isHash; // 是否从 Hash 读取

View File

@ -22,6 +22,7 @@
#include <QTimer>
#include "thread.h"
#include "tt.h"
#include "player.h"
AiThread::AiThread(int id, QObject *parent) :
QThread(parent),
@ -107,7 +108,7 @@ void AiThread::run()
while (!isInterruptionRequested()) {
mutex.lock();
i = Position::playerToId(position_->whosTurn());
i = Player::toId(position_->context.turn);
if (i != id || waiting_) {
pauseCondition.wait(&mutex);

View File

@ -36,6 +36,7 @@ class AiThread : public QThread
Q_OBJECT
public:
explicit AiThread(QObject *parent = nullptr);
explicit AiThread(int id, QObject *parent = nullptr);
~AiThread() override;
@ -90,10 +91,12 @@ public slots:
// 发射着法信号
void emitCommand();
private:
public:
// 玩家ID
int id;
private:
// 发射的指令
const char* strCommand {};

View File

@ -302,10 +302,10 @@ int Board::addMills(const Rule &currentRule, int location)
return n;
}
bool Board::isAllInMills(char ch)
bool Board::isAllInMills(enum player_t player)
{
for (int i = LOCATION_BEGIN; i < LOCATION_END; i++) {
if (locations[i] & ch) {
if (locations[i] & (uint8_t)player) {
if (!inHowManyMills(i)) {
return false;
}
@ -315,29 +315,14 @@ bool Board::isAllInMills(char ch)
return true;
}
bool Board::isAllInMills(enum player_t player)
{
char ch = 0x00;
if (player == PLAYER_1)
ch = 0x10;
else if (player == PLAYER_2)
ch = 0x20;
else
return true;
return isAllInMills(ch);
}
// 判断玩家的棋子周围有几个空位
int Board::getSurroundedEmptyLocationCount(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], int location, bool includeFobidden)
int Board::getSurroundedEmptyLocationCount(int turnId, const Rule &currentRule, int nPiecesOnBoard[],
int location, bool includeFobidden)
{
int count = 0;
if ((turn == PLAYER_1 &&
(nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(turn == PLAYER_2 &&
(nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
int moveLocation;
for (direction_t d = DIRECTION_BEGIN; d < DIRECTIONS_COUNT; d = (direction_t)(d + 1)) {
moveLocation = MoveList::moveTable[location][d];
@ -354,13 +339,11 @@ int Board::getSurroundedEmptyLocationCount(enum player_t turn, const Rule &curre
}
// 判断玩家的棋子是否被围
bool Board::isSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], int location)
bool Board::isSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location)
{
// 判断location处的棋子是否被“闷”
if ((turn == PLAYER_1 &&
(nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(turn == PLAYER_2 &&
(nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
if (nPiecesOnBoard[turnId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
int i, moveLocation;
for (i = 0; i < 4; i++) {
moveLocation = MoveList::moveTable[location][i];
@ -375,17 +358,15 @@ bool Board::isSurrounded(enum player_t turn, const Rule &currentRule, int nPiece
return false;
}
bool Board::isAllSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], char ch)
bool Board::isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], char ch)
{
// 如果摆满
if (nPiecesOnBoard[1] + nPiecesOnBoard[2] >= N_SEATS * N_RINGS)
return true;
// 判断是否可以飞子
if ((turn == PLAYER_1 &&
(nPiecesOnBoard[1] <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces)) ||
(turn == PLAYER_2 &&
(nPiecesOnBoard[2] <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces))) {
if (nPiecesOnBoard[turnId] <= currentRule.nPiecesAtLeast &&
currentRule.allowFlyWhenRemainThreePieces) {
return false;
}
@ -407,32 +388,27 @@ bool Board::isAllSurrounded(enum player_t turn, const Rule &currentRule, int nPi
}
// 判断玩家的棋子是否全部被围
bool Board::isAllSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply)
bool Board::isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply)
{
char t = '\x30';
char t = 0x30 & ply;
if (ply == PLAYER_1)
t &= '\x10';
else if (ply == PLAYER_2)
t &= '\x20';
return isAllSurrounded(turn, currentRule, nPiecesOnBoard, t);
return isAllSurrounded(turnId, currentRule, nPiecesOnBoard, t);
}
#if 0
enum player_t Board::getWhosPiece(int r, int s)
{
int location = polarToLocation(r, s);
if (locations[location] & '\x10')
if (locations[location] & PLAYER_1)
return PLAYER_1;
if (locations[location] & '\x20')
if (locations[location] & PLAYER_2)
return PLAYER_2;
return PLAYER_NOBODY;
}
// Unused
bool Board::getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule &currentRule)
{
int piece;
@ -480,6 +456,7 @@ bool Board::getCurrentPiece(player_t &player, int &number, int location)
return true;
}
#endif
bool Board::isStarLocation(int location)
{

View File

@ -78,27 +78,28 @@ public:
int inHowManyMills(int location);
// 判断玩家的所有棋子是否都处于“三连”状态
bool isAllInMills(char ch);
bool isAllInMills(enum player_t);
// 判断玩家的棋子周围有几个空位
int getSurroundedEmptyLocationCount(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], int location, bool includeFobidden);
int getSurroundedEmptyLocationCount(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location, bool includeFobidden);
// 判断玩家的棋子是否被围
bool isSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], int location);
bool isSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], int location);
// 判断玩家的棋子是否全部被围
bool isAllSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], char ch);
bool isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], char ch);
bool isAllSurrounded(enum player_t turn, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply);
bool isAllSurrounded(int turnId, const Rule &currentRule, int nPiecesOnBoard[], enum player_t ply);
// 三连加入列表
int addMills(const Rule &currentRule, int location);
#if 0
// 获取位置点棋子的归属人
enum player_t getWhosPiece(int r, int s);
bool getPieceRS(const player_t &player, const int &number, int &r, int &s, struct Rule &currentRule);
#endif
// 获取当前棋子
bool getCurrentPiece(player_t &player, int &number, int currentPos);

View File

@ -20,35 +20,66 @@
*****************************************************************************/
#ifndef PLAYER_H
#define PLAYER_H
#include "config.h"
#include "types.h"
#define PLAYER_H
class Player
{
public:
explicit Player();
virtual ~Player();
const player_t getPlayer() const
{
return who;
}
int getId() const
{
return id;
}
inline static int toId(player_t who)
{
return int((int)who >> PLAYER_SHIFT);
}
private:
player_t who;
int id;
};
#endif // PLAYER_H
#include <string>
#include "config.h"
#include "types.h"
class Player
{
public:
explicit Player();
virtual ~Player();
player_t getPlayer() const
{
return who;
}
int getId() const
{
return id;
}
inline static int toId(player_t who)
{
return int(static_cast<int>(who) >> PLAYER_SHIFT);
}
inline static player_t idToPlayer(int id)
{
return player_t(id << PLAYER_SHIFT);
}
inline static char idToCh(int id)
{
return static_cast<char>('0' + id);
}
inline static std::string chToStr(char ch)
{
if (ch == '1') {
return "1";
} else {
return "2";
}
}
inline static player_t getOpponent(player_t player)
{
return player == PLAYER_1 ? PLAYER_2 : PLAYER_1;
}
inline static int getOpponentById(int id)
{
return id == 1 ? 2 : 1;
}
private:
player_t who;
int id;
};
#endif // PLAYER_H

View File

@ -23,6 +23,7 @@
#include "position.h"
#include "search.h"
#include "movegen.h"
#include "player.h"
Position::Position()
{
@ -41,7 +42,7 @@ Position::Position()
setContext(&RULES[1]);
// 比分归零
score_1 = score_2 = score_draw = 0;
score[1] = score[2] = score_draw = 0;
}
Position::~Position() = default;
@ -67,8 +68,8 @@ Position &Position::operator= (const Position &position)
winner = position.winner;
startTime = position.startTime;
currentTime = position.currentTime;
elapsedSeconds_1 = position.elapsedSeconds_1;
elapsedSeconds_2 = position.elapsedSeconds_2;
elapsedSeconds[1] = position.elapsedSeconds[1];
elapsedSeconds[2] = position.elapsedSeconds[2];
move_ = position.move_;
memcpy(cmdline, position.cmdline, sizeof(cmdline));
cmdlist = position.cmdlist;
@ -77,31 +78,6 @@ Position &Position::operator= (const Position &position)
return *this;
}
int Position::playerToId(enum player_t player)
{
if (player == PLAYER_1)
return 1;
else if (player == PLAYER_2)
return 2;
return 0;
}
player_t Position::getOpponent(enum player_t player)
{
switch (player)
{
case PLAYER_1:
return PLAYER_2;
case PLAYER_2:
return PLAYER_1;
default:
break;
}
return PLAYER_NOBODY;
}
// 设置配置
bool Position::configure(bool giveUpIfMostLose, bool randomMove)
{
@ -141,7 +117,7 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int
context.phase = phase;
// 轮流状态标识
context.turn = turn;
setTurn(turn);
// 动作状态标识
context.action = action;
@ -221,7 +197,7 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int
currentLocation = 0;
// 用时置零
elapsedSeconds_1 = elapsedSeconds_2 = 0;
elapsedSeconds[1] = elapsedSeconds[2] = 0;
// 提示
setTips();
@ -244,25 +220,10 @@ bool Position::setContext(const struct Rule *rule, step_t maxStepsLedToDraw, int
return false;
}
void Position::getContext(struct Rule &rule, step_t &step,
phase_t &phase, player_t &turn, action_t &action,
int *&locations, int &nPiecesInHand_1, int &nPiecesInHand_2, int &nPiecesNeedRemove)
{
rule = this->currentRule;
step = this->currentStep;
phase = context.phase;
turn = context.turn;
action = context.action;
boardLocations = locations;
nPiecesInHand_1 = context.nPiecesInHand[1];
nPiecesInHand_2 = context.nPiecesInHand[2];
nPiecesNeedRemove = context.nPiecesNeedRemove;
}
bool Position::reset()
{
if (context.phase == PHASE_NOTSTARTED &&
elapsedSeconds_1 == elapsedSeconds_2 == 0) {
elapsedSeconds[1] == elapsedSeconds[2] == 0) {
return true;
}
@ -273,8 +234,8 @@ bool Position::reset()
// 局面阶段标识
context.phase = PHASE_NOTSTARTED;
// 轮流状态标识
context.turn = PLAYER_1;
// 设置轮流状态
setTurn(PLAYER_1);
// 动作状态标识
context.action = ACTION_PLACE;
@ -303,7 +264,7 @@ bool Position::reset()
currentLocation = 0;
// 用时置零
elapsedSeconds_1 = elapsedSeconds_2 = 0;
elapsedSeconds[1] = elapsedSeconds[2] = 0;
// 哈希归零
context.hash = 0;
@ -386,18 +347,10 @@ bool Position::place(int location, int time_p, int8_t rs)
int n = 0;
if (context.phase == PHASE_PLACING) {
// 先手下
if (context.turn == PLAYER_1) {
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]++;
}
int playerId = Player::toId(context.turn);
piece = (0x01 | context.turn) + currentRule.nTotalPiecesEachSide - context.nPiecesInHand[playerId];
context.nPiecesInHand[playerId]--;
context.nPiecesOnBoard[playerId]++;
boardLocations[location] = piece;
@ -432,9 +385,9 @@ bool Position::place(int location, int time_p, int8_t rs)
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER_2;
setTurn(PLAYER_2);
} else {
context.turn = PLAYER_1;
setTurn(PLAYER_1);
}
// 再决胜负
@ -468,11 +421,8 @@ bool Position::place(int location, int time_p, int8_t rs)
// 对于中局落子 (ontext.phase == GAME_MOVING)
// 如果落子不合法
if ((context.turn == PLAYER_1 &&
(context.nPiecesOnBoard[1] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(context.turn == PLAYER_2 &&
(context.nPiecesOnBoard[2] > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
if (context.nPiecesOnBoard[context.turnId] > currentRule.nPiecesAtLeast ||
!currentRule.allowFlyWhenRemainThreePieces) {
int i;
for (i = 0; i < 4; i++) {
if (location == MoveList::moveTable[currentLocation][i])
@ -576,8 +526,7 @@ bool Position::capture(int location, int time_p, int8_t cp)
// 时间的临时变量
int player_ms = -1;
// 对手
char opponent = context.turn == PLAYER_1 ? 0x20 : 0x10;
player_t opponent = Player::getOpponent(context.turn);
// 判断去子是不是对手棋
if (!(opponent & boardLocations[location]))
@ -600,10 +549,7 @@ bool Position::capture(int location, int time_p, int8_t cp)
boardLocations[location] = '\x00';
}
if (context.turn == PLAYER_1)
context.nPiecesOnBoard[2]--;
else if (context.turn == PLAYER_2)
context.nPiecesOnBoard[1]--;
context.nPiecesOnBoard[context.opponentId]--;
move_ = static_cast<move_t>(-location);
@ -650,9 +596,9 @@ bool Position::capture(int location, int time_p, int8_t cp)
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER_2;
setTurn(PLAYER_2);
} else {
context.turn = PLAYER_1;
setTurn(PLAYER_1);
}
// 再决胜负
@ -706,12 +652,10 @@ bool Position::choose(int location)
if (context.action != ACTION_CHOOSE && context.action != ACTION_PLACE)
return false;
char t = context.turn == PLAYER_1 ? 0x10 : 0x20;
// 判断选子是否可选
if (boardLocations[location] & t) {
if (boardLocations[location] & context.turn) {
// 判断location处的棋子是否被“闷”
if (context.board.isSurrounded(context.turn, currentRule, context.nPiecesOnBoard, location)) {
if (context.board.isSurrounded(context.turnId, currentRule, context.nPiecesOnBoard, location)) {
return false;
}
@ -742,17 +686,14 @@ bool Position::giveup(player_t loser)
context.phase = PHASE_GAMEOVER;
if (loser == PLAYER_1) {
winner = PLAYER_2;
tips = "玩家1投子认负。";
sprintf(cmdline, "Player1 give up!");
score_2++;
} else if (loser == PLAYER_2) {
winner = PLAYER_1;
tips = "玩家2投子认负。";
sprintf(cmdline, "Player2 give up!");
score_1++;
}
int loserId = Player::toId(loser);
char loserCh = Player::idToCh(loserId);
string loserStr = Player::chToStr(loserCh);
winner = Player::getOpponent(loser);
tips = "玩家" + loserStr + "投子认负";
sprintf(cmdline, "Player%d give up!", loserId);
score[Player::toId(winner)]++;
cmdlist.emplace_back(string(cmdline));
@ -818,12 +759,7 @@ bool Position::command(const char *cmd)
args = sscanf(cmd, "Player%1u give up!", &t);
if (args == 1) {
if (t == 1) {
return giveup(PLAYER_1);
}
if (t == 2) {
return giveup(PLAYER_2);
}
return giveup(Player::idToPlayer(t));
}
#ifdef THREEFOLD_REPETITION
@ -865,8 +801,8 @@ bool Position::command(int move)
inline int Position::update(int time_p /*= -1*/)
{
int ret = -1;
time_t *player_ms = (context.turn == PLAYER_1 ? &elapsedSeconds_1 : &elapsedSeconds_2);
time_t playerNext_ms = (context.turn == PLAYER_1 ? elapsedSeconds_2 : elapsedSeconds_1);
time_t *player_ms = &elapsedSeconds[context.turnId];
time_t playerNext_ms = elapsedSeconds[context.opponentId];
// 根据局面调整计时器
@ -879,7 +815,7 @@ inline int Position::update(int time_p /*= -1*/)
// 更新时间
if (time_p >= *player_ms) {
*player_ms = ret = time_p;
startTime = currentTime - (elapsedSeconds_1 + elapsedSeconds_2);
startTime = currentTime - (elapsedSeconds[1] + elapsedSeconds[2]);
} else {
*player_ms = ret = currentTime - startTime - playerNext_ms;
}
@ -914,22 +850,18 @@ bool Position::win(bool forceDraw)
context.phase = PHASE_GAMEOVER;
// 这里不能update更新时间否则会形成循环嵌套
// 如果玩家1超时
if (elapsedSeconds_1 > currentRule.maxTimeLedToLose * 60) {
elapsedSeconds_1 = currentRule.maxTimeLedToLose * 60;
winner = PLAYER_2;
tips = "玩家1超时判负。";
sprintf(cmdline, "Time over. Player2 win!");
}
// 如果玩家2超时
else if (elapsedSeconds_2 > currentRule.maxTimeLedToLose * 60) {
elapsedSeconds_2 = currentRule.maxTimeLedToLose * 60;
winner = PLAYER_1;
tips = "玩家2超时判负。";
sprintf(cmdline, "Time over. Player1 win!");
for (int i = 1; i <= 2; i++)
{
if (elapsedSeconds[i] > currentRule.maxTimeLedToLose * 60) {
elapsedSeconds[i] = currentRule.maxTimeLedToLose * 60;
winner = Player::idToPlayer(Player::getOpponentById(i));
tips = "玩家" + Player::chToStr(Player::idToCh(i)) + "超时判负。";
sprintf(cmdline, "Time over. Player%d win!", Player::getOpponentById(i));
}
}
cmdlist.emplace_back(string(cmdline));
return true;
}
@ -943,25 +875,21 @@ bool Position::win(bool forceDraw)
return true;
}
// 如果玩家1子数小于赛点则玩家2获胜
if (context.nPiecesOnBoard[1] + context.nPiecesInHand[1] < currentRule.nPiecesAtLeast) {
winner = PLAYER_2;
context.phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player2 win!");
cmdlist.emplace_back(string(cmdline));
return true;
}
// 如果玩家2子数小于赛点则玩家1获胜
if (context.nPiecesOnBoard[2] + context.nPiecesInHand[2] < currentRule.nPiecesAtLeast) {
winner = PLAYER_1;
context.phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player1 win!");
cmdlist.emplace_back(string(cmdline));
// 如果玩家子数小于赛点,则对方获胜
for (int i = 1; i <= 2; i++)
{
if (context.nPiecesOnBoard[i] + context.nPiecesInHand[i] < currentRule.nPiecesAtLeast) {
int o = Player::getOpponentById(i);
winner = Player::idToPlayer(o);
context.phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player%d win!", o);
cmdlist.emplace_back(string(cmdline));
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库
MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对"双方"失败都记录到开局库
#endif /* BOOK_LEARNING */
return true;
return true;
}
}
// 如果摆满了,根据规则判断胜负
@ -977,6 +905,7 @@ bool Position::win(bool forceDraw)
}
cmdlist.emplace_back(string(cmdline));
return true;
}
@ -986,26 +915,21 @@ bool Position::win(bool forceDraw)
context.phase = PHASE_GAMEOVER;
if (currentRule.isLoseWhenNoWay) {
if (context.turn == PLAYER_1) {
tips = "玩家1无子可走被闷。";
winner = PLAYER_2;
sprintf(cmdline, "Player1 no way to go. Player2 win!");
cmdlist.emplace_back(string(cmdline));
} else {
tips = "玩家2无子可走被闷。";
winner = PLAYER_1;
sprintf(cmdline, "Player2 no way to go. Player1 win!");
cmdlist.emplace_back(string(cmdline));
tips = "玩家" + Player::chToStr(context.turnChar) + "无子可走被闷";
winner = Player::getOpponent(context.turn);
int winnerId = Player::toId(winner);
sprintf(cmdline, "Player%d no way to go. Player%d win!", context.turnId, winnerId);
cmdlist.emplace_back(string(cmdline));
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookToHashMap(); // 暂时只对后手的失败记录到开局库
MillGameAi_ab::recordOpeningBookToHashMap(); // TODO: 目前是对所有的失败记录到开局库
#endif /* BOOK_LEARNING */
}
return true;
}
// 否则让棋,由对手走
changeTurn();
return false;
}
@ -1064,71 +988,73 @@ void Position::cleanForbiddenLocations()
}
}
enum player_t Position::changeTurn()
void Position::setTurn(player_t player)
{
// 设置轮到谁走
context.turn = (context.turn == PLAYER_1) ? PLAYER_2 : PLAYER_1;
context.turn = player;
return context.turn;
context.turnId = Player::toId(context.turn);
context.turnChar = Player::idToCh(context.turnId);
//context.turnStr = Player::chToStr(context.turnChar);
context.opponent = Player::getOpponent(player);
context.opponentId = Player::toId(context.opponent);
context.opponentChar = Player::idToCh(context.opponentId);
//context.opponentStr = Player::chToStr(context.opponentChar);
}
void Position::changeTurn()
{
setTurn(Player::getOpponent(context.turn));
}
void Position::setTips()
{
string winnerStr, t;
int winnerId;
string turnStr = Player::chToStr(context.turnChar);
switch (context.phase) {
case PHASE_NOTSTARTED:
tips = "轮到玩家1落子剩余" + std::to_string(context.nPiecesInHand[1]) + "" +
" 比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
" 比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw);
break;
case PHASE_PLACING:
if (context.action == ACTION_PLACE) {
if (context.turn == PLAYER_1) {
tips = "轮到玩家1落子剩余" + std::to_string(context.nPiecesInHand[1]) + "";
} else if (context.turn == PLAYER_2) {
tips = "轮到玩家2落子剩余" + std::to_string(context.nPiecesInHand[2]) + "";
}
tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(context.nPiecesInHand[context.turnId]) + "";
} else if (context.action == ACTION_CAPTURE) {
if (context.turn == PLAYER_1) {
tips = "成三轮到玩家1去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
} else if (context.turn == PLAYER_2) {
tips = "成三轮到玩家2去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
break;
case PHASE_MOVING:
if (context.action == ACTION_PLACE || context.action == ACTION_CHOOSE) {
if (context.turn == PLAYER_1) {
tips = "轮到玩家1选子移动";
} else if (context.turn == PLAYER_2) {
tips = "轮到玩家2选子移动";
}
tips = "轮到玩家" + turnStr + "选子移动";
} else if (context.action == ACTION_CAPTURE) {
if (context.turn == PLAYER_1) {
tips = "成三轮到玩家1去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
} else if (context.turn == PLAYER_2) {
tips = "成三轮到玩家2去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
break;
case PHASE_GAMEOVER:
case PHASE_GAMEOVER:
if (winner == PLAYER_DRAW) {
score_draw++;
tips = "双方平局!比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
}
else if (winner == PLAYER_1) {
score_1++;
if (tips.find("无子可走") != string::npos)
tips += "玩家1获胜比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
else
tips = "玩家1获胜比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
} else if (winner == PLAYER_2) {
score_2++;
if (tips.find("无子可走") != string::npos)
tips += "玩家2获胜比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
else
tips = "玩家2获胜比分 " + to_string(score_1) + ":" + to_string(score_2) + ", 和棋 " + to_string(score_draw);
tips = "双方平局!比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw);
break;
}
winnerId = Player::toId(winner);
winnerStr = Player::chToStr(Player::idToCh(winnerId));
score[winnerId]++;
t = "玩家" + winnerStr + "获胜!比分 " + to_string(score[1]) + ":" + to_string(score[2]) + ", 和棋 " + to_string(score_draw);
if (tips.find("无子可走") != string::npos) {
tips += t;
} else {
tips = t;
}
break;
@ -1142,8 +1068,8 @@ void Position::getElapsedTime(time_t &p1_ms, time_t &p2_ms)
{
update();
p1_ms = elapsedSeconds_1;
p2_ms = elapsedSeconds_2;
p1_ms = elapsedSeconds[1];
p2_ms = elapsedSeconds[2];
}
/*

View File

@ -51,6 +51,13 @@ public:
// 轮流状态标识
enum player_t turn;
int turnId;
char turnChar;
//string turnStr;
enum player_t opponent;
int opponentId;
char opponentChar;
//string opponentStr;
// 动作状态标识
enum action_t action
@ -77,14 +84,9 @@ class Position
public:
// 赢盘数
int score_1 {};
int score_2 {};
int score[3];
int score_draw {};
static int playerToId(enum player_t player);
static player_t getOpponent(enum player_t player);
private:
// 创建哈希值
@ -115,12 +117,6 @@ public:
int nPiecesNeedRemove = 0 // 尚待去除的子数
);
// 获取棋局状态和棋盘上下文
void getContext(struct Rule &rule, step_t &step,
phase_t &phase, player_t &turn, action_t &action,
int *&board,
int &nPiecesInHand_1, int &nPiecesInHand_2, int &nPiecesNeedRemove);
// 获取当前规则
const struct Rule *getRule() const
{
@ -169,12 +165,6 @@ public:
return context.phase;
}
// 获取轮流状态标识
enum player_t whosTurn() const
{
return context.turn;
}
// 获取动作状态标识
enum action_t getAction() const
{
@ -272,8 +262,11 @@ public:
// 清除所有禁点
void cleanForbiddenLocations();
// 设置轮流
void setTurn(player_t player);
// 改变轮流
enum player_t changeTurn();
void changeTurn();
// 设置提示
void setTips();
@ -358,11 +351,8 @@ private:
// 当前游戏时间
time_t currentTime {};
// 玩家1用时
time_t elapsedSeconds_1 {};
// 玩家2用时
time_t elapsedSeconds_2 {};
// 玩家用时(秒)
time_t elapsedSeconds[3];
// 当前棋局的字符提示
string tips;

View File

@ -37,15 +37,11 @@
GameController::GameController(GameScene & scene, QObject * parent) :
QObject(parent),
ai1(1),
ai2(2),
scene(scene),
currentPiece(nullptr),
currentRow(-1),
isEditing(false),
isInverted(false),
isAiPlayer_1(false),
isAiPlayer_2(false),
hasAnimation(true),
durationTime(500),
hasSound(true),
@ -64,16 +60,22 @@ GameController::GameController(GameScene & scene, QObject * parent) :
scene.setBackgroundBrush(QColor(239, 239, 239));
#endif /* MOBILE_APP_UI */
isAiPlayer[1] = false,
isAiPlayer[2] = false,
ai[1] = new AiThread(1);
ai[2] = new AiThread(2);
gameReset();
// 关联AI和控制器的着法命令行
connect(&ai1, SIGNAL(command(const QString &, bool)),
connect(ai[1], SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
connect(&ai2, SIGNAL(command(const QString &, bool)),
connect(ai[2], SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
// 关联AI和网络类的着法命令行
connect(ai1.getClient(), SIGNAL(command(const QString &, bool)),
connect(ai[1]->getClient(), SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
// 安装事件过滤器监视scene的各个事件
@ -88,10 +90,13 @@ GameController::~GameController()
killTimer(timeID);
// 停掉线程
ai1.stop();
ai2.stop();
ai1.wait();
ai2.wait();
ai[1]->stop();
ai[2]->stop();
ai[1]->wait();
ai[2]->wait();
delete ai[1];
delete ai[2];
#ifdef BOOK_LEARNING
MillGameAi_ab::recordOpeningBookHashMapToFile();
@ -149,10 +154,10 @@ void GameController::gameReset()
// 停掉线程
if (!isAutoRestart) {
ai1.stop();
ai2.stop();
isAiPlayer_1 = false;
isAiPlayer_2 = false;
ai[1]->stop();
ai[2]->stop();
isAiPlayer[1] = false;
isAiPlayer[2] = false;
}
// 清除棋子
@ -206,10 +211,10 @@ void GameController::gameReset()
// 如果规则不要求计时则time1和time2表示已用时间
if (timeLimit <= 0) {
// 将玩家的已用时间清零
remainingTime1 = remainingTime2 = 0;
remainingTime[1] = remainingTime[2] = 0;
} else {
// 将玩家的剩余时间置为限定时间
remainingTime1 = remainingTime2 = timeLimit * 60;
remainingTime[1] = remainingTime[2] = timeLimit * 60;
}
// 更新棋谱
@ -219,7 +224,7 @@ void GameController::gameReset()
currentRow = 0;
// 发出信号通知主窗口更新LCD显示
QTime qtime = QTime(0, 0, 0, 0).addSecs(remainingTime1);
QTime qtime = QTime(0, 0, 0, 0).addSecs(remainingTime[1]);
emit time1Changed(qtime.toString("hh:mm:ss"));
emit time2Changed(qtime.toString("hh:mm:ss"));
@ -228,8 +233,8 @@ void GameController::gameReset()
emit statusBarChanged(message);
// 更新比分 LCD 显示
emit score1Changed(QString::number(position_.score_1, 10));
emit score2Changed(QString::number(position_.score_2, 10));
emit score1Changed(QString::number(position_.score[1], 10));
emit score2Changed(QString::number(position_.score[2], 10));
emit scoreDrawChanged(QString::number(position_.score_draw, 10));
// 播放音效
@ -282,64 +287,59 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi
gameReset();
}
void GameController::setEngine1(bool arg)
void GameController::setEngine(int id, bool arg)
{
position_.configure(giveUpIfMostLose_, randomMove_);
isAiPlayer_1 = arg;
isAiPlayer[id] = arg;
if (arg) {
ai1.setAi(position_);
if (ai1.isRunning())
ai1.resume();
ai[id]->setAi(position_);
if (ai[id]->isRunning())
ai[id]->resume();
else
ai1.start();
ai[id]->start();
} else {
ai1.stop();
ai[id]->stop();
}
}
void GameController::setEngine1(bool arg)
{
setEngine(1, arg);
}
void GameController::setEngine2(bool arg)
{
position_.configure(giveUpIfMostLose_, randomMove_);
isAiPlayer_2 = arg;
if (arg) {
ai2.setAi(position_);
if (ai2.isRunning())
ai2.resume();
else
ai2.start();
} else {
ai2.stop();
}
setEngine(2, arg);
}
void GameController::setAiDepthTime(depth_t depth1, int time1, depth_t depth2, int time2)
{
if (isAiPlayer_1) {
ai1.stop();
ai1.wait();
if (isAiPlayer[1]) {
ai[1]->stop();
ai[1]->wait();
}
if (isAiPlayer_2) {
ai2.stop();
ai2.wait();
if (isAiPlayer[2]) {
ai[2]->stop();
ai[2]->wait();
}
ai1.setAi(position_, depth1, time1);
ai2.setAi(position_, depth2, time2);
ai[1]->setAi(position_, depth1, time1);
ai[2]->setAi(position_, depth2, time2);
if (isAiPlayer_1) {
ai1.start();
if (isAiPlayer[1]) {
ai[1]->start();
}
if (isAiPlayer_2) {
ai2.start();
if (isAiPlayer[2]) {
ai[2]->start();
}
}
void GameController::getAiDepthTime(depth_t &depth1, int &time1, depth_t &depth2, int &time2)
{
ai1.getDepthTime(depth1, time1);
ai2.getDepthTime(depth2, time2);
ai[1]->getDepthTime(depth1, time1);
ai[2]->getDepthTime(depth2, time2);
}
void GameController::setAnimation(bool arg)
@ -389,13 +389,13 @@ void GameController::setRandomMove(bool arg)
// 上下翻转
void GameController::flip()
{
if (isAiPlayer_1) {
ai1.stop();
ai1.wait();
if (isAiPlayer[1]) {
ai[1]->stop();
ai[1]->wait();
}
if (isAiPlayer_2) {
ai2.stop();
ai2.wait();
if (isAiPlayer[2]) {
ai[2]->stop();
ai[2]->wait();
}
position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
@ -414,28 +414,28 @@ void GameController::flip()
else
phaseChange(currentRow, true);
ai1.setAi(position_);
ai2.setAi(position_);
ai[1]->setAi(position_);
ai[2]->setAi(position_);
if (isAiPlayer_1) {
ai1.start();
if (isAiPlayer[1]) {
ai[1]->start();
}
if (isAiPlayer_2) {
ai2.start();
if (isAiPlayer[2]) {
ai[2]->start();
}
}
// 左右镜像
void GameController::mirror()
{
if (isAiPlayer_1) {
ai1.stop();
ai1.wait();
if (isAiPlayer[1]) {
ai[1]->stop();
ai[1]->wait();
}
if (isAiPlayer_2) {
ai2.stop();
ai2.wait();
if (isAiPlayer[2]) {
ai[2]->stop();
ai[2]->wait();
}
position_.context.board.mirror(position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
@ -456,28 +456,28 @@ void GameController::mirror()
else
phaseChange(currentRow, true);
ai1.setAi(position_);
ai2.setAi(position_);
ai[1]->setAi(position_);
ai[2]->setAi(position_);
if (isAiPlayer_1) {
ai1.start();
if (isAiPlayer[1]) {
ai[1]->start();
}
if (isAiPlayer_2) {
ai2.start();
if (isAiPlayer[2]) {
ai[2]->start();
}
}
// 视图须时针旋转90°
void GameController::turnRight()
{
if (isAiPlayer_1) {
ai1.stop();
ai1.wait();
if (isAiPlayer[1]) {
ai[1]->stop();
ai[1]->wait();
}
if (isAiPlayer_2) {
ai2.stop();
ai2.wait();
if (isAiPlayer[2]) {
ai[2]->stop();
ai[2]->wait();
}
position_.context.board.rotate(-90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
@ -496,28 +496,28 @@ void GameController::turnRight()
else
phaseChange(currentRow, true);
ai1.setAi(position_);
ai2.setAi(position_);
ai[1]->setAi(position_);
ai[2]->setAi(position_);
if (isAiPlayer_1) {
ai1.start();
if (isAiPlayer[1]) {
ai[1]->start();
}
if (isAiPlayer_2) {
ai2.start();
if (isAiPlayer[2]) {
ai[2]->start();
}
}
// 视图逆时针旋转90°
void GameController::turnLeft()
{
if (isAiPlayer_1) {
ai1.stop();
ai1.wait();
if (isAiPlayer[1]) {
ai[1]->stop();
ai[1]->wait();
}
if (isAiPlayer_2) {
ai2.stop();
ai2.wait();
if (isAiPlayer[2]) {
ai[2]->stop();
ai[2]->wait();
}
position_.context.board.rotate(90, position_.cmdlist, position_.cmdline, position_.move_, position_.currentRule, position_.currentLocation);
@ -532,13 +532,13 @@ void GameController::turnLeft()
// 刷新显示
updateScence();
ai1.setAi(position_);
ai2.setAi(position_);
if (isAiPlayer_1) {
ai1.start();
ai[1]->setAi(position_);
ai[2]->setAi(position_);
if (isAiPlayer[1]) {
ai[1]->start();
}
if (isAiPlayer_2) {
ai2.start();
if (isAiPlayer[2]) {
ai[2]->start();
}
}
@ -548,17 +548,17 @@ void GameController::timerEvent(QTimerEvent *event)
static QTime qt1, qt2;
// 玩家的已用时间
position_.getElapsedTime(remainingTime1, remainingTime2);
position_.getElapsedTime(remainingTime[1], remainingTime[2]);
// 如果规则要求计时则time1和time2表示倒计时
if (timeLimit > 0) {
// 玩家的剩余时间
remainingTime1 = timeLimit * 60 - remainingTime1;
remainingTime2 = timeLimit * 60 - remainingTime2;
remainingTime[1] = timeLimit * 60 - remainingTime[1];
remainingTime[2] = timeLimit * 60 - remainingTime[2];
}
qt1 = QTime(0, 0, 0, 0).addSecs(remainingTime1);
qt2 = QTime(0, 0, 0, 0).addSecs(remainingTime2);
qt1 = QTime(0, 0, 0, 0).addSecs(remainingTime[1]);
qt2 = QTime(0, 0, 0, 0).addSecs(remainingTime[2]);
emit time1Changed(qt1.toString("hh:mm:ss"));
emit time2Changed(qt2.toString("hh:mm:ss"));
@ -609,8 +609,7 @@ void GameController::timerEvent(QTimerEvent *event)
bool GameController::isAIsTurn()
{
return ((position_.whosTurn() == PLAYER_1 && isAiPlayer_1) ||
(position_.whosTurn() == PLAYER_2 && isAiPlayer_2));
return isAiPlayer[position_.context.turnId];
}
// 关键槽函数根据QGraphicsScene的信号和状态来执行选子、落子或去子
@ -748,24 +747,24 @@ bool GameController::actionPiece(QPointF pos)
if (&position_ == &(this->position_)) {
// 如果还未决出胜负
if (position_.whoWin() == PLAYER_NOBODY) {
if (position_.whosTurn() == PLAYER_1) {
if (isAiPlayer_1) {
ai1.resume();
if (position_.context.turn == PLAYER_1) {
if (isAiPlayer[1]) {
ai[1]->resume();
}
if (isAiPlayer_2)
ai2.pause();
if (isAiPlayer[2])
ai[2]->pause();
} else {
if (isAiPlayer_1)
ai1.pause();
if (isAiPlayer_2) {
ai2.resume();
if (isAiPlayer[1])
ai[1]->pause();
if (isAiPlayer[2]) {
ai[2]->resume();
}
}
}
// 如果已经决出胜负
else {
ai1.stop();
ai2.stop();
ai[1]->stop();
ai[2]->stop();
// 弹框
//message = QString::fromStdString(position_.getTips());
@ -780,32 +779,28 @@ bool GameController::actionPiece(QPointF pos)
bool GameController::giveUp()
{
bool result = false;
if (position_.whosTurn() == PLAYER_1) {
result = position_.giveup(PLAYER_1);
}
else if (position_.whosTurn() == PLAYER_2) {
result = position_.giveup(PLAYER_2);
}
bool result = position_.giveup(position_.context.turn);
if (result) {
// 将新增的棋谱行插入到ListModel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
for (const auto & i : *(position_.getCmdList())) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
}
if (position_.whoWin() != PLAYER_NOBODY)
playSound(":/sound/resources/sound/loss.wav");
if (!result) {
return false;
}
// 将新增的棋谱行插入到ListModel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
for (const auto & i : *(position_.getCmdList())) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
}
if (position_.whoWin() != PLAYER_NOBODY)
playSound(":/sound/resources/sound/loss.wav");
return result;
}
@ -815,10 +810,10 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
Q_UNUSED(hasSound)
// 防止接收滞后结束的线程发送的指令
if (sender() == &ai1 && !isAiPlayer_1)
if (sender() == ai[1] && !isAiPlayer[1])
return false;
if (sender() == &ai2 && !isAiPlayer_2)
if (sender() == ai[2] && !isAiPlayer[2])
return false;
// 声音
@ -892,34 +887,34 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
if (&position_ == &(this->position_)) {
// 如果还未决出胜负
if (position_.whoWin() == PLAYER_NOBODY) {
if (position_.whosTurn() == PLAYER_1) {
if (isAiPlayer_1) {
ai1.resume();
if (position_.context.turn == PLAYER_1) {
if (isAiPlayer[1]) {
ai[1]->resume();
}
if (isAiPlayer_2)
ai2.pause();
if (isAiPlayer[2])
ai[2]->pause();
} else {
if (isAiPlayer_1)
ai1.pause();
if (isAiPlayer_2) {
ai2.resume();
if (isAiPlayer[1])
ai[1]->pause();
if (isAiPlayer[2]) {
ai[2]->resume();
}
}
}
// 如果已经决出胜负
else {
ai1.stop();
ai2.stop();
ai[1]->stop();
ai[2]->stop();
if (isAutoRestart) {
gameReset();
gameStart();
if (isAiPlayer_1) {
setEngine1(true);
if (isAiPlayer[1]) {
setEngine(1, true);
}
if (isAiPlayer_2) {
setEngine2(true);
if (isAiPlayer[2]) {
setEngine(2, true);
}
}
@ -932,11 +927,11 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
}
// 网络: 将着法放到服务器的发送列表中
if (isAiPlayer_1)
if (isAiPlayer[1])
{
ai1.getServer()->setAction(cmd);
} else if (isAiPlayer_2) {
ai1.getServer()->setAction(cmd); // 注意: 同样是AI1
ai[1]->getServer()->setAction(cmd);
} else if (isAiPlayer[2]) {
ai[1]->getServer()->setAction(cmd); // 注意: 同样是AI1
}
return true;
@ -1106,8 +1101,8 @@ bool GameController::updateScence(Position &game)
animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
// 更新比分 LCD 显示
emit score1Changed(QString::number(game.score_1, 10));
emit score2Changed(QString::number(game.score_2, 10));
emit score1Changed(QString::number(game.score[1], 10));
emit score2Changed(QString::number(game.score[2], 10));
emit scoreDrawChanged(QString::number(game.score_draw, 10));
return true;
@ -1115,6 +1110,6 @@ bool GameController::updateScence(Position &game)
void GameController::showNetworkWindow()
{
ai1.getServer()->show();
ai1.getClient()->show();
ai[1]->getServer()->show();
ai[1]->getClient()->show();
}

View File

@ -140,11 +140,10 @@ public slots:
// 设置黑白反转状态
void setInvert(bool arg = true);
// 让电脑执先手
void setEngine1(bool arg = true);
// 让电脑执后手
void setEngine2(bool arg = true);
// id为1时让电脑执先手, id为2时让的电脑执后手
void setEngine(int id, bool arg = true);
void setEngine1(bool arg);
void setEngine2(bool arg);
// 是否有落子动画
void setAnimation(bool arg = true);
@ -210,7 +209,7 @@ private:
Position dummyPosition;
// 2个AI的线程
AiThread ai1, ai2;
AiThread *ai[3];
// 棋局的场景类
GameScene &scene;
@ -230,11 +229,8 @@ private:
// 是否黑白反转
bool isInverted;
// 是否电脑执先手
bool isAiPlayer_1;
// 是否电脑执后手
bool isAiPlayer_2;
// 电脑执先手时为 true
bool isAiPlayer[3];
// 是否有落子动画
bool hasAnimation;
@ -266,11 +262,8 @@ private:
// 规则限步数
step_t stepsLimit;
// 玩家1剩余时间
time_t remainingTime1;
// 玩家2剩余时间
time_t remainingTime2;
// 玩家剩余时间(秒)
time_t remainingTime[3];
// 用于主窗口状态栏显示的字符串
QString message;