refactor: 将 StateInfo 中大多数成员转移到 Position 类中

This commit is contained in:
Calcitem 2020-05-04 22:57:59 +08:00
parent 0f9ae19661
commit f864c7bbc4
13 changed files with 641 additions and 424 deletions

View File

@ -20,7 +20,7 @@
#include "evaluate.h"
#ifdef ALPHABETA_AI
value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node)
value_t Evaluation::getValue(Position *position, Node *node)
{
// 初始评估值为0对先手有利则增大对后手有利则减小
value_t value = VALUE_ZERO;
@ -82,7 +82,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node)
#ifdef EVALUATE_MOBILITY
// 按棋子活动能力计分
value += st->getMobilityDiff(position->turn, position->nPiecesInHand[BLACK], position->nPiecesInHand[WHITE], false) * 10;
value += st->position->getMobilityDiff(position->turn, position->nPiecesInHand[BLACK], position->nPiecesInHand[WHITE], false) * 10;
#endif /* EVALUATE_MOBILITY */
switch (position->action) {
@ -120,7 +120,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node)
// 走棋阶段被闷判断
else if (position->action == ACTION_CHOOSE &&
st->position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove) &&
position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove) &&
rule.isLoseWhenNoWay) {
// 规则要求被“闷”判负,则对手获胜
value_t delta = position->sideToMove == PLAYER_BLACK ? -VALUE_WIN : VALUE_WIN;
@ -140,7 +140,7 @@ value_t Evaluation::getValue(StateInfo *st, Position *position, Node *node)
break;
}
if (st->position->sideToMove == PLAYER_WHITE) {
if (position->sideToMove == PLAYER_WHITE) {
value = -value;
}

View File

@ -32,7 +32,7 @@ public:
Evaluation &operator=(const Evaluation &) = delete;
static value_t getValue(StateInfo *st, Position *Position, Node *node);
static value_t getValue(Position *Position, Node *node);
// 评估子力
#ifdef EVALUATE_ENABLE

View File

@ -16,7 +16,7 @@
#ifdef MCTS_AI
void StateInfo::doRandomMove(Node *node, mt19937_64 *engine)
void Position::doRandomMove(Node *node, mt19937_64 *engine)
{
assert(hasMoves());
checkInvariant();
@ -43,7 +43,7 @@ void StateInfo::doRandomMove(Node *node, mt19937_64 *engine)
doMove(m);
}
bool StateInfo::hasMoves() const
bool Position::hasMoves() const
{
checkInvariant();
@ -55,7 +55,7 @@ bool StateInfo::hasMoves() const
return true;
}
double StateInfo::getResult(player_t currentSideToMove) const
double Position::getResult(player_t currentSideToMove) const
{
assert(!hasMoves());
checkInvariant();
@ -73,17 +73,17 @@ double StateInfo::getResult(player_t currentSideToMove) const
}
}
void StateInfo::checkInvariant() const
void Position::checkInvariant() const
{
assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE);
assert(sideToMove == PLAYER_BLACK || sideToMove == PLAYER_WHITE);
}
////////////////////////////////////////////////////////////////////////////////////////
Node::Node(StateInfo &state) :
sideToMove(state.position->sideToMove)
Node::Node(Position &position) :
sideToMove(position->sideToMove)
{
state.generateMoves(moves);
position->generateMoves(moves);
}
Node::Node(StateInfo &state, const move_t &m, Node *p) :
@ -91,7 +91,7 @@ Node::Node(StateInfo &state, const move_t &m, Node *p) :
parent(p),
sideToMove(state.position->sideToMove)
{
state.generateMoves(moves);
state.position->generateMoves(moves);
}
void deleteChild(Node *node)
@ -187,9 +187,9 @@ Node *Node::selectChild() const
return nodeMax;
}
Node *Node::addChild(const move_t &move, StateInfo &state)
Node *Node::addChild(const move_t &move, Position &position)
{
auto node = new Node(state, move, this); // TODO: memmgr_alloc
auto node = new Node(position, move, this); // TODO: memmgr_alloc
//children.push_back(node);
children[childrenSize] = node;
@ -260,7 +260,7 @@ string Node::indentString(int indent) const
/////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////
Node *AIAlgorithm::computeTree(StateInfo state,
Node *AIAlgorithm::computeTree(Position position,
const MCTSOptions options,
mt19937_64::result_type initialSeed)
{
@ -275,7 +275,7 @@ Node *AIAlgorithm::computeTree(StateInfo state,
}
// Will support more players later.
assert(state.position->sideToMove == PLAYER_BLACK || state.position->sideToMove == PLAYER_WHITE);
assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE);
Node *root = new Node(state);
@ -288,12 +288,12 @@ Node *AIAlgorithm::computeTree(StateInfo state,
//auto node = root.get();
Node *node = root;
StateInfo st = state;
Position pos = position;
// Select a path through the tree to a leaf node.
while (!node->hasUntriedMoves() && node->hasChildren()) {
node = node->selectChild();
st.doMove(node->move);
pos.doMove(node->move);
}
// If we are not already at the final game, expand the
@ -301,18 +301,18 @@ Node *AIAlgorithm::computeTree(StateInfo state,
if (node->hasUntriedMoves()) {
auto move = node->getUntriedMove(&random_engine);
st.doMove(move);
node = node->addChild(move, st);
node = node->addChild(move, pos);
}
// We now play randomly until the game ends.
while (st.hasMoves()) {
st.doRandomMove(root, &random_engine);
pos.doRandomMove(root, &random_engine);
}
// We have now reached a final game. Backpropagate the result
// up the tree to the root node.
while (node != nullptr) {
node->update(st.getResult(node->sideToMove));
node->update(pos.getResult(node->sideToMove));
node = node->parent;
}
@ -334,17 +334,17 @@ Node *AIAlgorithm::computeTree(StateInfo state,
return root;
}
move_t AIAlgorithm::computeMove(StateInfo state,
move_t AIAlgorithm::computeMove(Position position,
const MCTSOptions options)
{
// Will support more players later.
assert(state.position->sideToMove == PLAYER_BLACK || state.position->sideToMove == PLAYER_WHITE);
assert(position->sideToMove == PLAYER_BLACK || position->sideToMove == PLAYER_WHITE);
// 分段随机打乱着法表
MoveList::shuffle();
Stack<move_t, MOVE_COUNT> moves;
state.generateMoves(moves);
Stack<move_t, MAX_MOVES> moves;
position->generateMoves(moves);
assert(moves.size() > 0);
if (moves.size() == 1) {
return moves[0];
@ -446,9 +446,9 @@ move_t AIAlgorithm::computeMove(StateInfo state,
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
ostream &operator << (ostream &out, StateInfo &state)
ostream &operator << (ostream &out, Position &pos)
{
//state.print(out);
//state.position->print(out);
return out;
}

View File

@ -27,7 +27,7 @@
#include "search.h"
#include "position.h"
void StateInfo::generateChildren(const Stack<move_t, MOVE_COUNT> &moves,
void Position::generateChildren(const Stack<move_t, MAX_MOVES> &moves,
AIAlgorithm *ai,
Node *node
#ifdef TT_MOVE_ENABLE
@ -55,12 +55,12 @@ void StateInfo::generateChildren(const Stack<move_t, MOVE_COUNT> &moves,
}
// 赋值
node->sideToMove = position->sideToMove;
node->sideToMove = sideToMove;
return;
}
int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
int Position::generateMoves(Stack<move_t, MAX_MOVES> &moves)
{
square_t square;
player_t opponent;
@ -68,24 +68,24 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
moves.clear();
// 列出所有合法的下一招
switch (position->action) {
switch (action) {
// 对于选子和落子动作
case ACTION_CHOOSE:
case ACTION_PLACE:
// 对于摆子阶段
if (position->phase & (PHASE_PLACING | PHASE_READY)) {
if (phase & (PHASE_PLACING | PHASE_READY)) {
for (move_t i : MoveList::movePriorityTable) {
square = static_cast<square_t>(i);
// 如果已经有子占据, 继续检索
if (boardLocations[square]) {
if (board.locations[square]) {
continue;
}
#ifdef MCTS_AI
moves.push_back((move_t)square);
#else // MCTS_AI
if (position->phase != PHASE_READY) {
if (phase != PHASE_READY) {
moves.push_back((move_t)square);
} else {
// 若为先手,则抢占星位
@ -103,7 +103,7 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
}
// 对于移子阶段
if (position->phase & PHASE_MOVING) {
if (phase & PHASE_MOVING) {
square_t newSquare, oldSquare;
// 尽量走理论上较差的位置的棋子
@ -114,13 +114,13 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
continue;
}
if (position->nPiecesOnBoard[position->sideId] > rule.nPiecesAtLeast ||
if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast ||
!rule.allowFlyWhenRemainThreePieces) {
// 对于棋盘上还有3个子以上或不允许飞子的情况要求必须在着法表中
for (int direction = DIRECTION_BEGIN; direction < DIRECTIONS_COUNT; direction++) {
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中
newSquare = static_cast<square_t>(MoveList::moveTable[oldSquare][direction]);
if (newSquare && !boardLocations[newSquare]) {
if (newSquare && !board.locations[newSquare]) {
move_t m = move_t((oldSquare << 8) + newSquare);
moves.push_back((move_t)m);
}
@ -128,7 +128,7 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
} else {
// 对于棋盘上还有不到3个字但允许飞子的情况不要求在着法表中是空位就行
for (newSquare = SQ_BEGIN; newSquare < SQ_END; newSquare = static_cast<square_t>(newSquare + 1)) {
if (!boardLocations[newSquare]) {
if (!board.locations[newSquare]) {
move_t m = move_t((oldSquare << 8) + newSquare);
moves.push_back((move_t)m);
}
@ -140,13 +140,13 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
// 对于吃子动作
case ACTION_CAPTURE:
opponent = Player::getOpponent(position->sideToMove);
opponent = Player::getOpponent(sideToMove);
if (position->board.isAllInMills(opponent)) {
if (board.isAllInMills(opponent)) {
// 全成三的情况
for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
square = static_cast<square_t>(MoveList::movePriorityTable[i]);
if (boardLocations[square] & opponent) {
if (board.locations[square] & opponent) {
moves.push_back((move_t)-square);
}
}
@ -156,8 +156,8 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
// 不是全成三的情况
for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
square = static_cast<square_t>(MoveList::movePriorityTable[i]);
if (boardLocations[square] & opponent) {
if (rule.allowRemoveMill || !position->board.inHowManyMills(square, PLAYER_NOBODY)) {
if (board.locations[square] & opponent) {
if (rule.allowRemoveMill || !board.inHowManyMills(square, PLAYER_NOBODY)) {
moves.push_back((move_t)-square);
}
}
@ -172,7 +172,7 @@ int StateInfo::generateMoves(Stack<move_t, MOVE_COUNT> &moves)
return moves.size();
}
int StateInfo::generateNullMove(Stack<move_t, MOVE_COUNT> &moves)
int Position::generateNullMove(Stack<move_t, MAX_MOVES> &moves)
{
moves.clear();
moves.push_back((move_t)SQ_0);
@ -436,3 +436,122 @@ void MoveList::shuffle()
movePriorityTable[i + 16] = movePriorityTable3[i];
}
}
/// generate<LEGAL> generates all the legal moves in the given position
//template<>
ExtMove *generate(/* TODO: const */ Position &position, ExtMove *moveList)
{
square_t square;
player_t opponent;
//moves.clear();
ExtMove *cur = moveList;
// 列出所有合法的下一招
switch (position.action) {
// 对于选子和落子动作
case ACTION_CHOOSE:
case ACTION_PLACE:
// 对于摆子阶段
if (position.phase & (PHASE_PLACING | PHASE_READY)) {
for (move_t i : MoveList::movePriorityTable) {
square = static_cast<square_t>(i);
// 如果已经有子占据, 继续检索
if (position.board.locations[square]) {
continue;
}
#ifdef MCTS_AI
moves.push_back((move_t)square);
#else // MCTS_AI
if (position.phase != PHASE_READY) {
*cur++ = ((move_t)square);
} else {
// 若为先手,则抢占星位
#ifdef FIRST_MOVE_STAR_PREFERRED
if (Board::isStar(square)) {
moves.push_back((move_t)square);
}
#else
*cur++ = ((move_t)square);
#endif
}
#endif // MCTS_AI
}
break;
}
// 对于移子阶段
if (position.phase & PHASE_MOVING) {
square_t newSquare, oldSquare;
// 尽量走理论上较差的位置的棋子
for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
oldSquare = static_cast<square_t>(MoveList::movePriorityTable[i]);
if (!position.choose(oldSquare)) {
continue;
}
if (position.nPiecesOnBoard[position.sideId] > rule.nPiecesAtLeast ||
!rule.allowFlyWhenRemainThreePieces) {
// 对于棋盘上还有3个子以上或不允许飞子的情况要求必须在着法表中
for (int direction = DIRECTION_BEGIN; direction < DIRECTIONS_COUNT; direction++) {
// 对于原有位置,遍历四个方向的着法,如果棋盘上为空位就加到结点列表中
newSquare = static_cast<square_t>(MoveList::moveTable[oldSquare][direction]);
if (newSquare && !position.board.locations[newSquare]) {
move_t m = move_t((oldSquare << 8) + newSquare);
*cur++ = ((move_t)m);
}
}
} else {
// 对于棋盘上还有不到3个字但允许飞子的情况不要求在着法表中是空位就行
for (newSquare = SQ_BEGIN; newSquare < SQ_END; newSquare = static_cast<square_t>(newSquare + 1)) {
if (!position.board.locations[newSquare]) {
move_t m = move_t((oldSquare << 8) + newSquare);
*cur++ = ((move_t)m);
}
}
}
}
}
break;
// 对于吃子动作
case ACTION_CAPTURE:
opponent = Player::getOpponent(position.sideToMove);
if (position.board.isAllInMills(opponent)) {
// 全成三的情况
for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
square = static_cast<square_t>(MoveList::movePriorityTable[i]);
if (position.board.locations[square] & opponent) {
*cur++ = ((move_t)-square);
}
}
break;
}
// 不是全成三的情况
for (int i = Board::MOVE_PRIORITY_TABLE_SIZE - 1; i >= 0; i--) {
square = static_cast<square_t>(MoveList::movePriorityTable[i]);
if (position.board.locations[square] & opponent) {
if (rule.allowRemoveMill || !position.board.inHowManyMills(square, PLAYER_NOBODY)) {
*cur++ = ((move_t)-square);
}
}
}
break;
default:
assert(0);
break;
}
return moveList;
}

View File

@ -24,6 +24,43 @@
#include "position.h"
#include "search.h"
enum GenType
{
CAPTURES,
LEGAL
};
struct ExtMove
{
move_t move;
value_t value;
operator move_t() const
{
return move;
}
void operator = (move_t m)
{
move = m;
}
// Inhibit unwanted implicit conversions to Move
// with an ambiguity that yields to a compile error.
operator float() const = delete;
};
inline bool operator < (const ExtMove &first, const ExtMove &second)
{
return first.value < second.value;
}
//template <GenType>
ExtMove *generate(const Position &pos, ExtMove *moveList);
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
/// in handy to use this class instead of the low level generate() function.
//template<GenType T>
class MoveList
{
public:
@ -41,11 +78,39 @@ public:
inline static move_t moveTable[SQ_EXPANDED_COUNT][DIRECTIONS_COUNT] = { {MOVE_NONE} };
// 着法顺序表, 后续会被打乱
inline static array<move_t, Board::N_RINGS *Board::N_SEATS> movePriorityTable {
inline static array<move_t, Board::N_RINGS *Board::N_SEATS> movePriorityTable{
(move_t)8, (move_t)9, (move_t)10, (move_t)11, (move_t)12, (move_t)13, (move_t)14, (move_t)15,
(move_t)16, (move_t)17, (move_t)18, (move_t)19, (move_t)20, (move_t)21, (move_t)22, (move_t)23,
(move_t)24, (move_t)25, (move_t)26, (move_t)27, (move_t)28, (move_t)29, (move_t)30, (move_t)31,
};
//explicit MoveList(const Position &pos) : last(generate<T>(pos, moveList))
explicit MoveList(const Position &pos) : last(generate(pos, moveList))
{
}
const ExtMove *begin() const
{
return moveList;
}
const ExtMove *end() const
{
return last;
}
size_t size() const
{
return last - moveList;
}
bool contains(move_t move) const
{
return std::find(begin(), end(), move) != end();
}
private:
ExtMove moveList[MAX_MOVES], *last;
};
#endif /* MOVEGEN_H */

View File

@ -123,15 +123,15 @@ depth_t AIAlgorithm::changeDepth(depth_t origDepth)
if (st->position->phase & PHASE_PLACING) {
if (rule.nTotalPiecesEachSide == 12) {
d = placingDepthTable_12[rule.nTotalPiecesEachSide * 2 - st->getPiecesInHandCount(BLACK) - st->getPiecesInHandCount(WHITE)];
d = placingDepthTable_12[rule.nTotalPiecesEachSide * 2 - st->position->getPiecesInHandCount(BLACK) - st->position->getPiecesInHandCount(WHITE)];
} else {
d = placingDepthTable_9[rule.nTotalPiecesEachSide * 2 - st->getPiecesInHandCount(BLACK) - st->getPiecesInHandCount(WHITE)];
d = placingDepthTable_9[rule.nTotalPiecesEachSide * 2 - st->position->getPiecesInHandCount(BLACK) - st->position->getPiecesInHandCount(WHITE)];
}
}
if (st->position->phase & PHASE_MOVING) {
int pb = st->getPiecesOnBoardCount(BLACK);
int pw = st->getPiecesOnBoardCount(WHITE);
int pb = st->position->getPiecesOnBoardCount(BLACK);
int pw = st->position->getPiecesOnBoardCount(WHITE);
int pieces = pb + pw;
int diff = pb - pw;
@ -185,7 +185,7 @@ void AIAlgorithm::buildRoot()
Node *Node::addChild(
const move_t &m,
AIAlgorithm *ai,
StateInfo *st
Position *position
#ifdef TT_MOVE_ENABLE
, const move_t &ttMove
#endif // TT_MOVE_ENABLE
@ -289,7 +289,7 @@ Node *Node::addChild(
}
// 若为走子之前的统计故走棋阶段可能会从 @-0-@ 走成 0-@-@, 并未成三,所以需要传值 sqsrc 进行判断
int nMills = st->position->board.inHowManyMills(sq, st->position->sideToMove, sqsrc);
int nMills = position->board.inHowManyMills(sq, position->sideToMove, sqsrc);
int nopponentMills = 0;
#ifdef SORT_MOVE_WITH_HUMAN_KNOWLEDGES
@ -300,17 +300,17 @@ Node *Node::addChild(
#ifdef ALPHABETA_AI
newNode->rating += static_cast<rating_t>(RATING_ONE_MILL * nMills);
#endif
} else if (st->getPhase() == PHASE_PLACING) {
} else if (position->getPhase() == PHASE_PLACING) {
// 在摆棋阶段, 检测落子点是否能阻止对方成三
nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent);
nopponentMills = position->board.inHowManyMills(sq, position->opponent);
#ifdef ALPHABETA_AI
newNode->rating += static_cast<rating_t>(RATING_BLOCK_ONE_MILL * nopponentMills);
#endif
}
#if 1
else if (st->getPhase() == PHASE_MOVING) {
else if (position->getPhase() == PHASE_MOVING) {
// 在走棋阶段, 检测落子点是否能阻止对方成三
nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent);
nopponentMills = position->board.inHowManyMills(sq, position->opponent);
if (nopponentMills) {
int nPlayerPiece = 0;
@ -318,7 +318,7 @@ Node *Node::addChild(
int nForbidden = 0;
int nEmpty = 0;
st->position->board.getSurroundedPieceCount(sq, st->position->sideId,
position->board.getSurroundedPieceCount(sq, position->sideId,
nPlayerPiece, nOpponentPiece, nForbidden, nEmpty);
#ifdef ALPHABETA_AI
@ -337,7 +337,7 @@ Node *Node::addChild(
// 对于12子棋, 白方第2着走星点的重要性和成三一样重要 (TODO)
#ifdef ALPHABETA_AI
if (rule.nTotalPiecesEachSide == 12 &&
st->getPiecesOnBoardCount(2) < 2 && // patch: 仅当白方第2着时
position->getPiecesOnBoardCount(2) < 2 && // patch: 仅当白方第2着时
Board::isStar(static_cast<square_t>(m))) {
newNode->rating += RATING_STAR_SQUARE;
}
@ -348,7 +348,7 @@ Node *Node::addChild(
int nForbidden = 0;
int nEmpty = 0;
st->position->board.getSurroundedPieceCount(sq, st->position->sideId,
position->board.getSurroundedPieceCount(sq, position->sideId,
nPlayerPiece, nOpponentPiece, nForbidden, nEmpty);
#ifdef ALPHABETA_AI
@ -367,7 +367,7 @@ Node *Node::addChild(
}
// 吃子点处于对方的三连中
nopponentMills = st->position->board.inHowManyMills(sq, st->position->opponent);
nopponentMills = position->board.inHowManyMills(sq, position->opponent);
if (nopponentMills) {
if (nOpponentPiece >= 2) {
// 旁边对方的子较多, 则倾向不吃
@ -604,8 +604,8 @@ int AIAlgorithm::search(depth_t depth)
#ifdef THREEFOLD_REPETITION
static int nRepetition = 0;
if (state->getPhase() == PHASE_MOVING) {
hash_t hash = state->getHash();
if (state->position->getPhase() == PHASE_MOVING) {
hash_t hash = state->position->getHash();
if (std::find(moveHistory.begin(), moveHistory.end(), hash) != moveHistory.end()) {
nRepetition++;
@ -618,7 +618,7 @@ int AIAlgorithm::search(depth_t depth)
}
}
if (state->getPhase() == PHASE_PLACING) {
if (state->position->getPhase() == PHASE_PLACING) {
moveHistory.clear();
}
#endif // THREEFOLD_REPETITION
@ -715,7 +715,7 @@ int AIAlgorithm::search(depth_t depth)
if (gameOptions.getIDSEnabled()) {
#ifdef IDS_WINDOW
value_t window = state->getPhase() == PHASE_PLACING ? VALUE_PLACING_WINDOW : VALUE_MOVING_WINDOW;
value_t window = state->position.getPhase() == PHASE_PLACING ? VALUE_PLACING_WINDOW : VALUE_MOVING_WINDOW;
alpha = value - window;
beta = value + window;
#else
@ -786,7 +786,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
#if defined (TRANSPOSITION_TABLE_ENABLE) || defined(ENDGAME_LEARNING)
// 获取哈希值
hash_t hash = st->getHash();
hash_t hash = st->position->getHash();
#endif
#ifdef ENDGAME_LEARNING
@ -889,7 +889,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
depth <= 0 ||
unlikely(requiredQuit)) {
// 局面评估
node->value = Evaluation::getValue(st, position, node);
node->value = Evaluation::getValue(position, node);
evaluatedNodeCount++;
// 为争取速胜value 值 +- 深度
@ -947,9 +947,9 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
// 生成子节点树,即生成每个合理的着法
if (node->childrenSize == 0) {
int moveCount = st->generateMoves(moves);
int moveCount = st->position->generateMoves(moves);
st->generateChildren(moves, this, node
st->position->generateChildren(moves, this, node
#ifdef TT_MOVE_ENABLE
, ttMove
#endif // TT_MOVE_ENABLE
@ -971,7 +971,7 @@ value_t AIAlgorithm::search(depth_t depth, value_t alpha, value_t beta, Node *no
#ifdef TRANSPOSITION_TABLE_ENABLE
#ifdef PREFETCH_SUPPORT
for (int i = 0; i < nchild; i++) {
TT::prefetchHash(st->getNextMainHash(node->children[i]->move));
TT::prefetchHash(st->position->getNextMainHash(node->children[i]->move));
}
#ifdef PREFETCH_DEBUG
@ -1104,7 +1104,7 @@ void AIAlgorithm::stashPosition()
void AIAlgorithm::doMove(move_t move)
{
// 执行着法
st->doMove(move);
st->position->doMove(move);
}
void AIAlgorithm::undoMove()
@ -1118,13 +1118,13 @@ void AIAlgorithm::undoMove()
void AIAlgorithm::doNullMove()
{
// 执行空着
st->doNullMove();
st->position->doNullMove();
}
void AIAlgorithm::undoNullMove()
{
// 执行空着
st->undoNullMove();
st->position->undoNullMove();
}
#ifdef ALPHABETA_AI

View File

@ -57,10 +57,10 @@ class Position;
using namespace std;
using namespace CTSL;
// 注意:StateInfo类不是线程安全的!
// 所以不能在ai类中修改StateInfo类的静态成员变量,切记!
// 另外AI类是StateInfo类的友元类,可以访问其私有变量
// 尽量不要使用StateInfo的操作函数,因为有参数安全性检测和不必要的赋值,影响效率
// 注意:Position 类不是线程安全的!
// 所以不能在ai类中修改 Position 类的静态成员变量,切记!
// 另外AI类是 Position 类的友元类,可以访问其私有变量
// 尽量不要使用 Position 的操作函数,因为有参数安全性检测和不必要的赋值,影响效率
class Node
{
@ -69,8 +69,8 @@ public:
~Node();
#ifdef MCTS_AI
Node(StateInfo &state);
Node(StateInfo &state, const move_t &move, Node *parent);
Node(Position &position);
Node(Position &position, const move_t &move, Node *parent);
#endif // MCTS_AI
bool hasChildren() const;
@ -78,7 +78,7 @@ public:
Node *addChild(
const move_t &move,
AIAlgorithm *ai,
StateInfo *st
Position *position
#ifdef TT_MOVE_ENABLE
, const move_t &ttMove
#endif // TT_MOVE_ENABLE
@ -90,14 +90,14 @@ public:
move_t getUntriedMove(RandomEngine *engine) const;
Node *bestChildren() const;
Node *selectChild() const;
Node *addChild(const move_t &move, StateInfo &state);
Node *addChild(const move_t &move, Position &position);
void update(double result);
string toString();
string treeToString(int max_depth = 1000000, int indent = 0) const;
string indentString(int indent) const;
#endif // MCTS_AI
static const int NODE_CHILDREN_SIZE = MOVE_COUNT;
static const int NODE_CHILDREN_SIZE = MAX_MOVES;
Node *children[NODE_CHILDREN_SIZE];
Node *parent { nullptr };
@ -212,10 +212,10 @@ public:
#ifdef MCTS_AI
// TODO: 分离到 MCTS 算法类
Node *computeTree(StateInfo state,
Node *computeTree(Position position,
const MCTSOptions options,
mt19937_64::result_type initialSeed);
move_t AIAlgorithm::computeMove(StateInfo state,
move_t AIAlgorithm::computeMove(Position position,
const MCTSOptions options);
#endif
@ -308,7 +308,7 @@ private:
Stack<Position> positionStack;
#endif /* USE_STD_STACK */
Stack<move_t, MOVE_COUNT> moves;
Stack<move_t, MAX_MOVES> moves;
// 标识,用于跳出剪枝算法,立即返回
bool requiredQuit {false};

View File

@ -98,7 +98,7 @@ void AiThread::emitCommand()
}
#ifdef MCTS_AI
move_t computeMove(StateInfo state,
move_t computeMove(Position position,
const MCTSOptions options);
#endif // MCTS_AI

View File

@ -34,10 +34,10 @@ StateInfo::StateInfo()
position = new Position();
//memset(position, 0, sizeof(Position));
}
// 单独提出 boardLocations 等数据,免得每次都写 boardLocations;
boardLocations = position->board.locations;
Position::Position()
{
// 创建哈希数据
constructHash();
@ -57,7 +57,10 @@ StateInfo::~StateInfo()
delete position;
position = nullptr;
}
}
Position::~Position()
{
cmdlist.clear();
}
@ -73,6 +76,11 @@ StateInfo::StateInfo(const StateInfo &state)
*this = state;
}
Position::Position(const Position &pos)
{
*this = pos;
}
StateInfo::StateInfo(StateInfo &state)
{
if (position != nullptr) {
@ -85,22 +93,32 @@ StateInfo::StateInfo(StateInfo &state)
*this = state;
}
Position::Position(Position &pos)
{
*this = pos;
}
StateInfo &StateInfo::operator= (const StateInfo &state)
{
memcpy(position, state.position, sizeof(Position));
currentStep = state.currentStep;
moveStep = state.moveStep;
boardLocations = position->board.locations;
currentSquare = state.currentSquare;
winner = state.winner;
startTime = state.startTime;
currentTime = state.currentTime;
elapsedSeconds[BLACK] = state.elapsedSeconds[BLACK];
elapsedSeconds[WHITE] = state.elapsedSeconds[WHITE];
move = state.move;
memcpy(cmdline, state.cmdline, sizeof(cmdline));
cmdlist = state.cmdlist;
tips = state.tips;
return *this;
}
Position &Position::operator= (const Position &pos)
{
currentStep = pos.currentStep;
moveStep = pos.moveStep;
memcpy(board.locations, pos.board.locations, sizeof(board.locations));
currentSquare = pos.currentSquare;
winner = pos.winner;
startTime = pos.startTime;
currentTime = pos.currentTime;
elapsedSeconds[BLACK] = pos.elapsedSeconds[BLACK];
elapsedSeconds[WHITE] = pos.elapsedSeconds[WHITE];
move = pos.move;
memcpy(cmdline, pos.cmdline, sizeof(cmdline));
cmdlist = pos.cmdlist;
tips = pos.tips;
return *this;
}
@ -108,37 +126,43 @@ StateInfo &StateInfo::operator= (const StateInfo &state)
StateInfo &StateInfo::operator= (StateInfo &state)
{
memcpy(position, state.position, sizeof(Position));
currentStep = state.currentStep;
moveStep = state.moveStep;
boardLocations = position->board.locations;
currentSquare = state.currentSquare;
winner = state.winner;
startTime = state.startTime;
currentTime = state.currentTime;
elapsedSeconds[BLACK] = state.elapsedSeconds[BLACK];
elapsedSeconds[WHITE] = state.elapsedSeconds[WHITE];
move = state.move;
memcpy(cmdline, state.cmdline, sizeof(cmdline));
cmdlist = state.cmdlist;
tips = state.tips;
return *this;
}
Position &Position::operator= (Position &pos)
{
currentStep = pos.currentStep;
moveStep = pos.moveStep;
memcpy(board.locations, pos.board.locations, sizeof(board.locations));
currentSquare = pos.currentSquare;
winner = pos.winner;
startTime = pos.startTime;
currentTime = pos.currentTime;
elapsedSeconds[BLACK] = pos.elapsedSeconds[BLACK];
elapsedSeconds[WHITE] = pos.elapsedSeconds[WHITE];
move = pos.move;
memcpy(cmdline, pos.cmdline, sizeof(cmdline));
cmdlist = pos.cmdlist;
tips = pos.tips;
return *this;
}
int StateInfo::countPiecesOnBoard()
int Position::countPiecesOnBoard()
{
position->nPiecesOnBoard[BLACK] = position->nPiecesOnBoard[WHITE] = 0;
nPiecesOnBoard[BLACK] = nPiecesOnBoard[WHITE] = 0;
for (int r = 1; r < Board::N_RINGS + 2; r++) {
for (int s = 0; s < Board::N_SEATS; s++) {
square_t square = static_cast<square_t>(r * Board::N_SEATS + s);
if (boardLocations[square] & PIECE_BLACK) {
position->nPiecesOnBoard[BLACK]++;
} else if (boardLocations[square] & PIECE_WHITE) {
position->nPiecesOnBoard[WHITE]++;
if (board.locations[square] & PIECE_BLACK) {
nPiecesOnBoard[BLACK]++;
} else if (board.locations[square] & PIECE_WHITE) {
nPiecesOnBoard[WHITE]++;
}
#if 0
else if (boardLocations[square] & PIECE_FORBIDDEN) {
else if (board.locations[square] & PIECE_FORBIDDEN) {
// 不计算盘面子数
}
#endif
@ -146,24 +170,24 @@ int StateInfo::countPiecesOnBoard()
}
// 设置玩家盘面剩余子数和未放置子数
if (position->nPiecesOnBoard[BLACK] > rule.nTotalPiecesEachSide ||
position->nPiecesOnBoard[WHITE] > rule.nTotalPiecesEachSide) {
if (nPiecesOnBoard[BLACK] > rule.nTotalPiecesEachSide ||
nPiecesOnBoard[WHITE] > rule.nTotalPiecesEachSide) {
return -1;
}
return position->nPiecesOnBoard[BLACK] + position->nPiecesOnBoard[WHITE];
return nPiecesOnBoard[BLACK] + nPiecesOnBoard[WHITE];
}
int StateInfo::countPiecesInHand()
int Position::countPiecesInHand()
{
position->nPiecesInHand[BLACK] = rule.nTotalPiecesEachSide - position->nPiecesOnBoard[BLACK];
position->nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide - position->nPiecesOnBoard[WHITE];
nPiecesInHand[BLACK] = rule.nTotalPiecesEachSide - nPiecesOnBoard[BLACK];
nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide - nPiecesOnBoard[WHITE];
return position->nPiecesInHand[BLACK] + position->nPiecesInHand[WHITE];
return nPiecesInHand[BLACK] + nPiecesInHand[WHITE];
}
// 设置棋局状态和棋盘数据,用于初始化
bool StateInfo::setPosition(const struct Rule *newRule,
bool Position::setPosition(const struct Rule *newRule,
step_t initialStep,
phase_t phase, player_t side, action_t action,
const char *locations,
@ -179,20 +203,20 @@ bool StateInfo::setPosition(const struct Rule *newRule,
this->moveStep = initialStep;
// 局面阶段标识
position->phase = phase;
phase = phase;
// 轮流状态标识
setSideToMove(side);
// 动作状态标识
position->action = action;
action = action;
// 当前棋局3×8
if (locations == nullptr) {
memset(boardLocations, 0, sizeof(position->board.locations));
position->hash = 0;
memset(board.locations, 0, sizeof(board.locations));
hash = 0;
} else {
memcpy(boardLocations, locations, sizeof(position->board.locations));
memcpy(board.locations, locations, sizeof(board.locations));
}
if (countPiecesOnBoard() == -1) {
@ -204,14 +228,14 @@ bool StateInfo::setPosition(const struct Rule *newRule,
// 设置去子状态时的剩余尚待去除子数
if (action == ACTION_CAPTURE) {
if (0 <= nPiecesNeedRemove && nPiecesNeedRemove < 3) {
position->nPiecesNeedRemove = nPiecesNeedRemove;
nPiecesNeedRemove = nPiecesNeedRemove;
}
} else {
position->nPiecesNeedRemove = 0;
nPiecesNeedRemove = 0;
}
// 清空成三记录
position->board.millListSize = 0;
board.millListSize = 0;
// 胜负标识
winner = PLAYER_NOBODY;
@ -220,7 +244,7 @@ bool StateInfo::setPosition(const struct Rule *newRule,
MoveList::create();
// 生成成三表
position->board.createMillTable();
board.createMillTable();
// 不选中棋子
currentSquare = SQ_0;
@ -249,9 +273,9 @@ bool StateInfo::setPosition(const struct Rule *newRule,
return false;
}
bool StateInfo::reset()
bool Position::reset()
{
if (position->phase == PHASE_READY &&
if (phase == PHASE_READY &&
elapsedSeconds[BLACK] == elapsedSeconds[WHITE] == 0) {
return true;
}
@ -261,31 +285,31 @@ bool StateInfo::reset()
moveStep = 0;
// 局面阶段标识
position->phase = PHASE_READY;
phase = PHASE_READY;
// 设置轮流状态
setSideToMove(PLAYER_BLACK);
// 动作状态标识
position->action = ACTION_PLACE;
action = ACTION_PLACE;
// 胜负标识
winner = PLAYER_NOBODY;
// 当前棋局3×8
memset(boardLocations, 0, sizeof(position->board));
memset(board.locations, 0, sizeof(board.locations));
// 盘面子数归零
position->nPiecesOnBoard[BLACK] = position->nPiecesOnBoard[WHITE] = 0;
nPiecesOnBoard[BLACK] = nPiecesOnBoard[WHITE] = 0;
// 设置玩家盘面剩余子数和未放置子数
position->nPiecesInHand[BLACK] = position->nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide;
nPiecesInHand[BLACK] = nPiecesInHand[WHITE] = rule.nTotalPiecesEachSide;
// 设置去子状态时的剩余尚待去除子数
position->nPiecesNeedRemove = 0;
nPiecesNeedRemove = 0;
// 清空成三记录
position->board.millListSize = 0;
board.millListSize = 0;
// 不选中棋子
currentSquare = SQ_0;
@ -294,7 +318,7 @@ bool StateInfo::reset()
elapsedSeconds[BLACK] = elapsedSeconds[WHITE] = 0;
// 哈希归零
position->hash = 0;
hash = 0;
// 提示
setTips();
@ -326,9 +350,9 @@ bool StateInfo::reset()
return false;
}
bool StateInfo::start()
bool Position::start()
{
switch (position->phase) {
switch (phase) {
// 如果游戏已经开始则返回false
case PHASE_PLACING:
case PHASE_MOVING:
@ -342,29 +366,29 @@ bool StateInfo::start()
// 启动计时器
startTime = time(NULL);
// 进入开局状态
position->phase = PHASE_PLACING;
phase = PHASE_PLACING;
return true;
default:
return false;
}
}
bool StateInfo::place(square_t square, int8_t updateCmdlist)
bool Position::place(square_t square, int8_t updateCmdlist)
{
// 如果局面为“结局”返回false
if (position->phase == PHASE_GAMEOVER)
if (phase == PHASE_GAMEOVER)
return false;
// 如果局面为“未开局”,则开局
if (position->phase == PHASE_READY)
if (phase == PHASE_READY)
start();
// 如非“落子”状态返回false
if (position->action != ACTION_PLACE)
if (action != ACTION_PLACE)
return false;
// 如果落子位置在棋盘外、已有子点或禁点返回false
if (!position->board.onBoard[square] || boardLocations[square])
if (!board.onBoard[square] || board.locations[square])
return false;
// 格式转换
@ -379,13 +403,13 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
int piece = '\x00';
int n = 0;
if (position->phase == PHASE_PLACING) {
int playerId = Player::toId(position->sideToMove);
piece = (0x01 | position->sideToMove) + rule.nTotalPiecesEachSide - position->nPiecesInHand[playerId];
position->nPiecesInHand[playerId]--;
position->nPiecesOnBoard[playerId]++;
if (phase == PHASE_PLACING) {
int playerId = Player::toId(sideToMove);
piece = (0x01 | sideToMove) + rule.nTotalPiecesEachSide - nPiecesInHand[playerId];
nPiecesInHand[playerId]--;
nPiecesOnBoard[playerId]++;
boardLocations[square] = piece;
board.locations[square] = piece;
updateHash(square);
@ -401,24 +425,24 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
currentSquare = square;
n = position->board.addMills(currentSquare);
n = board.addMills(currentSquare);
// 开局阶段未成三
if (n == 0) {
assert(position->nPiecesInHand[BLACK] >= 0 && position->nPiecesInHand[WHITE] >= 0);
assert(nPiecesInHand[BLACK] >= 0 && nPiecesInHand[WHITE] >= 0);
// 如果双方都无未放置的棋子
if (position->nPiecesInHand[BLACK] == 0 && position->nPiecesInHand[WHITE] == 0) {
if (nPiecesInHand[BLACK] == 0 && nPiecesInHand[WHITE] == 0) {
// 决胜负
if (checkGameOverCondition()) {
goto out;
}
// 进入中局阶段
position->phase = PHASE_MOVING;
phase = PHASE_MOVING;
// 进入选子状态
position->action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenLocations();
@ -444,10 +468,10 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
// 如果成三
else {
// 设置去子数目
position->nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1;
nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
position->action = ACTION_CAPTURE;
action = ACTION_CAPTURE;
}
goto out;
@ -461,7 +485,7 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
// 对于中局落子 (ontext.phase == GAME_MOVING)
// 如果落子不合法
if (position->nPiecesOnBoard[position->sideId] > rule.nPiecesAtLeast ||
if (nPiecesOnBoard[sideId] > rule.nPiecesAtLeast ||
!rule.allowFlyWhenRemainThreePieces) {
int i;
for (i = 0; i < 4; i++) {
@ -487,20 +511,20 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
moveStep++;
}
boardLocations[square] = boardLocations[currentSquare];
board.locations[square] = board.locations[currentSquare];
updateHash(square);
revertHash(currentSquare);
boardLocations[currentSquare] = '\x00';
board.locations[currentSquare] = '\x00';
currentSquare = square;
n = position->board.addMills(currentSquare);
n = board.addMills(currentSquare);
// 中局阶段未成三
if (n == 0) {
// 进入选子状态
position->action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 设置轮到谁走
changeSideToMove();
@ -513,10 +537,10 @@ bool StateInfo::place(square_t square, int8_t updateCmdlist)
// 中局阶段成三
else {
// 设置去子数目
position->nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1;
nPiecesNeedRemove = rule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
position->action = ACTION_CAPTURE;
action = ACTION_CAPTURE;
}
out:
@ -527,7 +551,7 @@ out:
return true;
}
bool StateInfo::_place(int r, int s)
bool Position::_place(int r, int s)
{
// 转换为 square
square_t square = Board::polarToSquare(r, s);
@ -535,7 +559,7 @@ bool StateInfo::_place(int r, int s)
return place(square, true);
}
bool StateInfo::_capture(int r, int s)
bool Position::_capture(int r, int s)
{
// 转换为 square
square_t square = Board::polarToSquare(r, s);
@ -543,18 +567,18 @@ bool StateInfo::_capture(int r, int s)
return capture(square, 1);
}
bool StateInfo::capture(square_t square, int8_t updateCmdlist)
bool Position::capture(square_t square, int8_t updateCmdlist)
{
// 如果局面为"未开局"或“结局”返回false
if (position->phase & PHASE_NOTPLAYING)
if (phase & PHASE_NOTPLAYING)
return false;
// 如非“去子”状态返回false
if (position->action != ACTION_CAPTURE)
if (action != ACTION_CAPTURE)
return false;
// 如果去子完成返回false
if (position->nPiecesNeedRemove <= 0)
if (nPiecesNeedRemove <= 0)
return false;
// 格式转换
@ -565,30 +589,30 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
// 时间的临时变量
int seconds = -1;
player_t opponent = Player::getOpponent(position->sideToMove);
player_t opponent = Player::getOpponent(sideToMove);
// 判断去子是不是对手棋
if (!(opponent & boardLocations[square]))
if (!(opponent & board.locations[square]))
return false;
// 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中
if (!rule.allowRemoveMill &&
position->board.inHowManyMills(square, PLAYER_NOBODY) &&
!position->board.isAllInMills(opponent)) {
board.inHowManyMills(square, PLAYER_NOBODY) &&
!board.isAllInMills(opponent)) {
return false;
}
// 去子(设置禁点)
if (rule.hasForbiddenLocations && position->phase == PHASE_PLACING) {
if (rule.hasForbiddenLocations && phase == PHASE_PLACING) {
revertHash(square);
boardLocations[square] = '\x0f';
board.locations[square] = '\x0f';
updateHash(square);
} else { // 去子
revertHash(square);
boardLocations[square] = '\x00';
board.locations[square] = '\x00';
}
position->nPiecesOnBoard[position->opponentId]--;
nPiecesOnBoard[opponentId]--;
move = static_cast<move_t>(-square);
@ -601,7 +625,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
}
currentSquare = SQ_0;
position->nPiecesNeedRemove--;
nPiecesNeedRemove--;
//updateHash(square); // TODO: 多余? 若去掉.则评估点的数量和哈希命中数量上升, 未剪枝?
// 去子完成
@ -612,7 +636,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
}
// 还有其余的子要去吗
if (position->nPiecesNeedRemove > 0) {
if (nPiecesNeedRemove > 0) {
// 继续去子
return true;
}
@ -620,15 +644,15 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
// 所有去子都完成了
// 开局阶段
if (position->phase == PHASE_PLACING) {
if (phase == PHASE_PLACING) {
// 如果双方都无未放置的棋子
if (position->nPiecesInHand[BLACK] == 0 && position->nPiecesInHand[WHITE] == 0) {
if (nPiecesInHand[BLACK] == 0 && nPiecesInHand[WHITE] == 0) {
// 进入中局阶段
position->phase = PHASE_MOVING;
phase = PHASE_MOVING;
// 进入选子状态
position->action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenLocations();
@ -648,7 +672,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
// 如果双方还有子
else {
// 进入落子状态
position->action = ACTION_PLACE;
action = ACTION_PLACE;
// 设置轮到谁走
changeSideToMove();
@ -662,7 +686,7 @@ bool StateInfo::capture(square_t square, int8_t updateCmdlist)
// 中局阶段
else {
// 进入选子状态
position->action = ACTION_CHOOSE;
action = ACTION_CHOOSE;
// 设置轮到谁走
changeSideToMove();
@ -681,23 +705,23 @@ out:
return true;
}
bool StateInfo::choose(square_t square)
bool Position::choose(square_t square)
{
// 如果局面不是"中局”返回false
if (position->phase != PHASE_MOVING)
if (phase != PHASE_MOVING)
return false;
// 如非“选子”或“落子”状态返回false
if (position->action != ACTION_CHOOSE && position->action != ACTION_PLACE)
if (action != ACTION_CHOOSE && action != ACTION_PLACE)
return false;
// 判断选子是否可选
if (boardLocations[square] & position->sideToMove) {
if (board.locations[square] & sideToMove) {
// 选子
currentSquare = square;
// 选子完成,进入落子状态
position->action = ACTION_PLACE;
action = ACTION_PLACE;
return true;
}
@ -705,19 +729,19 @@ bool StateInfo::choose(square_t square)
return false;
}
bool StateInfo::choose(int r, int s)
bool Position::choose(int r, int s)
{
return choose(Board::polarToSquare(r, s));
}
bool StateInfo::giveup(player_t loser)
bool Position::giveup(player_t loser)
{
if (position->phase & PHASE_NOTPLAYING ||
position->phase == PHASE_NONE) {
if (phase & PHASE_NOTPLAYING ||
phase == PHASE_NONE) {
return false;
}
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
int loserId = Player::toId(loser);
char loserCh = Player::idToCh(loserId);
@ -734,7 +758,7 @@ bool StateInfo::giveup(player_t loser)
}
// 打算用个C++的命令行解析库的,简单的没必要,但中文编码有极小概率出问题
bool StateInfo::command(const char *cmd)
bool Position::command(const char *cmd)
{
int r;
unsigned t;
@ -801,7 +825,7 @@ bool StateInfo::command(const char *cmd)
}
if (!strcmp(cmd, "draw")) {
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
winner = PLAYER_DRAW;
score_draw++;
tips = "三次重复局面判和。";
@ -814,7 +838,7 @@ bool StateInfo::command(const char *cmd)
return false;
}
bool StateInfo::doMove(move_t m)
bool Position::doMove(move_t m)
{
if (m < 0) {
return capture(static_cast<square_t>(-m));
@ -831,21 +855,21 @@ bool StateInfo::doMove(move_t m)
return false;
}
player_t StateInfo::getWinner() const
player_t Position::getWinner() const
{
return winner;
}
int StateInfo::update()
int Position::update()
{
int ret = -1;
int timePoint = -1;
time_t *seconds = &elapsedSeconds[position->sideId];
time_t opponentSeconds = elapsedSeconds[position->opponentId];
time_t *seconds = &elapsedSeconds[sideId];
time_t opponentSeconds = elapsedSeconds[opponentId];
// 根据局面调整计时器
if (!(position->phase & PHASE_PLAYING)) {
if (!(phase & PHASE_PLAYING)) {
return -1;
}
@ -868,15 +892,15 @@ int StateInfo::update()
}
// 是否分出胜负
bool StateInfo::checkGameOverCondition()
bool Position::checkGameOverCondition()
{
if (position->phase & PHASE_NOTPLAYING) {
if (phase & PHASE_NOTPLAYING) {
return true;
}
// 如果有时间限定
if (rule.maxTimeLedToLose > 0) {
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
// 这里不能update更新时间否则会形成循环嵌套
for (int i = 1; i <= 2; i++)
@ -898,7 +922,7 @@ bool StateInfo::checkGameOverCondition()
if (rule.maxStepsLedToDraw > 0 &&
moveStep > rule.maxStepsLedToDraw) {
winner = PLAYER_DRAW;
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
sprintf(cmdline, "Steps over. In draw!");
cmdlist.emplace_back(string(cmdline));
return true;
@ -907,10 +931,10 @@ bool StateInfo::checkGameOverCondition()
// 如果玩家子数小于赛点,则对方获胜
for (int i = 1; i <= 2; i++)
{
if (position->nPiecesOnBoard[i] + position->nPiecesInHand[i] < rule.nPiecesAtLeast) {
if (nPiecesOnBoard[i] + nPiecesInHand[i] < rule.nPiecesAtLeast) {
int o = Player::getOpponentById(i);
winner = Player::idToPlayer(o);
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player%d win!", o);
cmdlist.emplace_back(string(cmdline));
@ -920,10 +944,10 @@ bool StateInfo::checkGameOverCondition()
#ifdef MCTS_AI
#if 0
int diff = position->nPiecesOnBoard[BLACK] - position->nPiecesOnBoard[WHITE];
int diff = nPiecesOnBoard[BLACK] - nPiecesOnBoard[WHITE];
if (diff > 4) {
winner = PLAYER_BLACK;
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player1 win!");
cmdlist.emplace_back(string(cmdline));
@ -932,7 +956,7 @@ bool StateInfo::checkGameOverCondition()
if (diff < -4) {
winner = PLAYER_WHITE;
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
sprintf(cmdline, "Player2 win!");
cmdlist.emplace_back(string(cmdline));
@ -942,8 +966,8 @@ bool StateInfo::checkGameOverCondition()
#endif
// 如果摆满了,根据规则判断胜负
if (position->nPiecesOnBoard[BLACK] + position->nPiecesOnBoard[WHITE] >= Board::N_SEATS * Board::N_RINGS) {
position->phase = PHASE_GAMEOVER;
if (nPiecesOnBoard[BLACK] + nPiecesOnBoard[WHITE] >= Board::N_SEATS * Board::N_RINGS) {
phase = PHASE_GAMEOVER;
if (rule.isStartingPlayerLoseWhenBoardFull) {
winner = PLAYER_WHITE;
@ -959,15 +983,15 @@ bool StateInfo::checkGameOverCondition()
}
// 如果中局被“闷”
if (position->phase == PHASE_MOVING && position->action == ACTION_CHOOSE && position->board.isAllSurrounded(position->sideId, position->nPiecesOnBoard, position->sideToMove)) {
if (phase == PHASE_MOVING && action == ACTION_CHOOSE && board.isAllSurrounded(sideId, nPiecesOnBoard, sideToMove)) {
// 规则要求被“闷”判负,则对手获胜 // TODO: 应该转移到下面的分支中
position->phase = PHASE_GAMEOVER;
phase = PHASE_GAMEOVER;
if (rule.isLoseWhenNoWay) {
tips = "玩家" + Player::chToStr(position->chSide) + "无子可走被闷";
winner = Player::getOpponent(position->sideToMove);
tips = "玩家" + Player::chToStr(chSide) + "无子可走被闷";
winner = Player::getOpponent(sideToMove);
int winnerId = Player::toId(winner);
sprintf(cmdline, "Player%d no way to go. Player%d win!", position->sideId, winnerId);
sprintf(cmdline, "Player%d no way to go. Player%d win!", sideId, winnerId);
cmdlist.emplace_back(string(cmdline));
return true;
@ -983,17 +1007,17 @@ bool StateInfo::checkGameOverCondition()
}
// 计算玩家1和玩家2的棋子活动能力之差
int StateInfo::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool includeFobidden)
int Position::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool includeFobidden)
{
// TODO: 处理规则无禁点的情况
location_t *locations = boardLocations;
location_t *locations = board.locations;
int mobilityBlack = 0;
int mobilityWhite = 0;
int diff = 0;
int n = 0;
for (square_t i = SQ_BEGIN; i < SQ_END; i = static_cast<square_t>(i + 1)) {
n = position->board.getSurroundedEmptyLocationCount(turn, nPiecesOnBoard, i, includeFobidden);
n = board.getSurroundedEmptyLocationCount(turn, nPiecesOnBoard, i, includeFobidden);
if (locations[i] & PIECE_BLACK) {
mobilityBlack += n;
@ -1007,7 +1031,7 @@ int StateInfo::getMobilityDiff(player_t turn, int nPiecesOnBoard[], bool include
return diff;
}
void StateInfo::cleanForbiddenLocations()
void Position::cleanForbiddenLocations()
{
if (!rule.hasForbiddenLocations) {
return;
@ -1019,75 +1043,75 @@ void StateInfo::cleanForbiddenLocations()
for (int s = 0; s < Board::N_SEATS; s++) {
square = static_cast<square_t>(r * Board::N_SEATS + s);
if (boardLocations[square] == '\x0f') {
if (board.locations[square] == '\x0f') {
revertHash(square);
boardLocations[square] = '\x00';
board.locations[square] = '\x00';
}
}
}
}
void StateInfo::setSideToMove(player_t player)
void Position::setSideToMove(player_t player)
{
// 设置轮到谁走
position->sideToMove = player;
sideToMove = player;
position->sideId = Player::toId(position->sideToMove);
position->chSide = Player::idToCh(position->sideId);
sideId = Player::toId(sideToMove);
chSide = Player::idToCh(sideId);
position->opponent = Player::getOpponent(player);
opponent = Player::getOpponent(player);
position->opponentId = Player::toId(position->opponent);
position->chOpponent = Player::idToCh(position->opponentId);
opponentId = Player::toId(opponent);
chOpponent = Player::idToCh(opponentId);
}
player_t StateInfo::getSideToMove()
player_t Position::getSideToMove()
{
return position->sideToMove;
return sideToMove;
}
void StateInfo::changeSideToMove()
void Position::changeSideToMove()
{
setSideToMove(Player::getOpponent(position->sideToMove));
setSideToMove(Player::getOpponent(sideToMove));
}
bool StateInfo::doNullMove()
bool Position::doNullMove()
{
changeSideToMove();
return true;
}
bool StateInfo::undoNullMove()
bool Position::undoNullMove()
{
changeSideToMove();
return true;
}
void StateInfo::setTips()
void Position::setTips()
{
string winnerStr, t;
int winnerId;
string turnStr = Player::chToStr(position->chSide);
string turnStr = Player::chToStr(chSide);
switch (position->phase) {
switch (phase) {
case PHASE_READY:
tips = "轮到玩家1落子剩余" + std::to_string(position->nPiecesInHand[BLACK]) + "" +
tips = "轮到玩家1落子剩余" + std::to_string(nPiecesInHand[BLACK]) + "" +
" 比分 " + to_string(score[BLACK]) + ":" + to_string(score[WHITE]) + ", 和棋 " + to_string(score_draw);
break;
case PHASE_PLACING:
if (position->action == ACTION_PLACE) {
tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(position->nPiecesInHand[position->sideId]) + "";
} else if (position->action == ACTION_CAPTURE) {
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(position->nPiecesNeedRemove) + "";
if (action == ACTION_PLACE) {
tips = "轮到玩家" + turnStr + "落子,剩余" + std::to_string(nPiecesInHand[sideId]) + "";
} else if (action == ACTION_CAPTURE) {
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(nPiecesNeedRemove) + "";
}
break;
case PHASE_MOVING:
if (position->action == ACTION_PLACE || position->action == ACTION_CHOOSE) {
if (action == ACTION_PLACE || action == ACTION_CHOOSE) {
tips = "轮到玩家" + turnStr + "选子移动";
} else if (position->action == ACTION_CAPTURE) {
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(position->nPiecesNeedRemove) + "";
} else if (action == ACTION_CAPTURE) {
tips = "成三!轮到玩家" + turnStr + "去子,需去" + std::to_string(nPiecesNeedRemove) + "";
}
break;
@ -1118,77 +1142,77 @@ void StateInfo::setTips()
}
}
time_t StateInfo::getElapsedTime(int playerId)
time_t Position::getElapsedTime(int playerId)
{
return elapsedSeconds[playerId];
}
void StateInfo::constructHash()
void Position::constructHash()
{
position->hash = 0;
hash = 0;
}
hash_t StateInfo::getHash()
hash_t Position::getHash()
{
// TODO: 每次获取哈希值时更新 hash 值剩余8位放在此处调用不优雅
return updateHashMisc();
}
hash_t StateInfo::updateHash(square_t square)
hash_t Position::updateHash(square_t square)
{
// PieceType is boardLocations[square]
// PieceType is board.locations[square]
// 0b00 表示空白0b01 = 1 表示先手棋子0b10 = 2 表示后手棋子0b11 = 3 表示禁点
int pieceType = Player::toId(position->board.locationToPlayer(square));
int pieceType = Player::toId(board.locationToPlayer(square));
// 清除或者放置棋子
position->hash ^= zobrist[square][pieceType];
hash ^= zobrist[square][pieceType];
return position->hash;
return hash;
}
hash_t StateInfo::revertHash(square_t square)
hash_t Position::revertHash(square_t square)
{
return updateHash(square);
}
hash_t StateInfo::updateHashMisc()
hash_t Position::updateHashMisc()
{
const int HASH_MISC_BIT = 8;
// 清除标记位
position->hash = position->hash << HASH_MISC_BIT >> HASH_MISC_BIT;
hash = hash << HASH_MISC_BIT >> HASH_MISC_BIT;
hash_t hi = 0;
// 置位
if (position->sideToMove == PLAYER_WHITE) {
if (sideToMove == PLAYER_WHITE) {
hi |= 1U;
}
if (position->action == ACTION_CAPTURE) {
if (action == ACTION_CAPTURE) {
hi |= 1U << 1;
}
hi |= static_cast<hash_t>(position->nPiecesNeedRemove) << 2;
hi |= static_cast<hash_t>(position->nPiecesInHand[BLACK]) << 4; // TODO: 或许换 position->phase 也可以?
hi |= static_cast<hash_t>(nPiecesNeedRemove) << 2;
hi |= static_cast<hash_t>(nPiecesInHand[BLACK]) << 4; // TODO: 或许换 phase 也可以?
position->hash = position->hash | (hi << (CHAR_BIT * sizeof(hash_t) - HASH_MISC_BIT));
hash = hash | (hi << (CHAR_BIT * sizeof(hash_t) - HASH_MISC_BIT));
return position->hash;
return hash;
}
hash_t StateInfo::getNextMainHash(move_t m)
hash_t Position::getNextMainHash(move_t m)
{
hash_t nextMainHash = position->hash /* << 8 >> 8 */;
hash_t nextMainHash = hash /* << 8 >> 8 */;
square_t sq = SQ_0;
if (m < 0) {
sq = static_cast<square_t>(-m);
int pieceType = Player::getOpponentById(Player::toId(position->sideToMove));
int pieceType = Player::getOpponentById(Player::toId(sideToMove));
nextMainHash ^= zobrist[sq][pieceType];
if (rule.hasForbiddenLocations && position->phase == PHASE_PLACING) {
if (rule.hasForbiddenLocations && phase == PHASE_PLACING) {
nextMainHash ^= zobrist[sq][PIECETYPE_FORBIDDEN];
}
@ -1196,7 +1220,7 @@ hash_t StateInfo::getNextMainHash(move_t m)
}
sq = static_cast<square_t>(m & 0x00ff);
int pieceType = Player::toId(position->sideToMove);
int pieceType = Player::toId(sideToMove);
nextMainHash ^= zobrist[sq][pieceType];
if (m & 0x1f00) {

View File

@ -44,6 +44,17 @@ class Node;
class Position
{
public:
Position();
virtual ~Position();
// 拷贝构造函数
Position(Position &);
Position(const Position &);
// 运算符重载
Position &operator=(const Position &);
Position &operator=(Position &);
Board board;
// 局面的哈希值
@ -75,28 +86,8 @@ public:
// 尚待去除的子数
int nPiecesNeedRemove {0};
};
// 棋类(在数据模型内,玩家只分先后手,不分黑白)
// 注意StateInfo 类不是线程安全的!
// 所以不能跨线程修改 StateInfo 类的静态成员变量,切记!
class StateInfo
{
// AI友元类
friend class AIAlgorithm;
public:
StateInfo();
virtual ~StateInfo();
// 拷贝构造函数
StateInfo(StateInfo &);
StateInfo(const StateInfo &);
// 运算符重载
StateInfo &operator=(const StateInfo &);
StateInfo &operator=(StateInfo &);
//////////////////////////////////////
// 设置棋局状态和棋局,用于初始化
bool setPosition(const struct Rule *rule,
@ -109,7 +100,7 @@ public:
// 获取棋盘数据
location_t *getBoardLocations() const
{
return boardLocations;
return (location_t *)board.locations;
}
// 获取当前棋子位置点
@ -133,13 +124,13 @@ public:
// 获取局面阶段标识
enum phase_t getPhase() const
{
return position->phase;
return phase;
}
// 获取动作状态标识
enum action_t getAction() const
{
return position->action;
return action;
}
// 玩家1或玩家2的用时
@ -178,19 +169,19 @@ public:
// 玩家剩余未放置子数
int getPiecesInHandCount(int playerId) const
{
return position->nPiecesInHand[playerId];
return nPiecesInHand[playerId];
}
// 玩家盘面剩余子数
int getPiecesOnBoardCount(int playerId) const
{
return position->nPiecesOnBoard[playerId];
return nPiecesOnBoard[playerId];
}
// 尚待去除的子数
int getNum_NeedRemove() const
{
return position->nPiecesNeedRemove;
return nPiecesNeedRemove;
}
// 计算玩家1和玩家2的棋子活动能力之差
@ -239,17 +230,17 @@ public:
void setTips();
// 着法生成
void generateChildren(const Stack<move_t, MOVE_COUNT> &moves,
void generateChildren(const Stack<move_t, MAX_MOVES> &moves,
AIAlgorithm *ai,
Node *node
#ifdef TT_MOVE_ENABLE
, move_t ttMove
, move_t ttMove
#endif // TT_MOVE_ENABLE
);
// 着法生成
int generateMoves(Stack<move_t, MOVE_COUNT> &moves);
int generateNullMove(Stack<move_t, MOVE_COUNT> &moves);
int generateMoves(Stack<move_t, MAX_MOVES> &moves);
int generateNullMove(Stack<move_t, MAX_MOVES> &moves);
bool doNullMove();
bool undoNullMove();
@ -272,7 +263,7 @@ public:
#ifdef MCTS_AI
// MCTS 相关
Stack<move_t, MOVE_COUNT> moves;
Stack<move_t, MAX_MOVES> moves;
//template<typename RandomEngine>
//void doRandomMove(RandomEngine *engine);
@ -290,17 +281,11 @@ public:
int tm { -1 };
// 棋局
Position *position {nullptr};
// 棋局中的棋盘数据,单独提出来
location_t *boardLocations {nullptr};
// 棋谱
vector <string> cmdlist;
// 着法命令行用于棋谱的显示和解析, 当前着法的命令行指令,即一招棋谱
char cmdline[64] {'\0'};
char cmdline[64]{ '\0' };
/*
AI会用到
@ -322,10 +307,10 @@ public:
| / | \ |
29 ----- 28 ----- 27
*/
move_t move {MOVE_NONE};
move_t move{ MOVE_NONE };
// 选中的棋子在board中的位置
square_t currentSquare {};
square_t currentSquare{};
private:
@ -342,16 +327,16 @@ private:
player_t winner;
// 当前步数
step_t currentStep {};
step_t currentStep{};
// 从走子阶段开始或上次吃子起的步数
int moveStep {};
int moveStep{};
// 游戏起始时间
time_t startTime {};
// 游戏起始时间
time_t startTime{};
// 当前游戏时间
time_t currentTime {};
time_t currentTime{};
// 玩家用时(秒)
time_t elapsedSeconds[COLOR_COUNT];
@ -360,4 +345,28 @@ private:
string tips;
};
// 棋类(在数据模型内,玩家只分先后手,不分黑白)
// 注意StateInfo 类不是线程安全的!
// 所以不能跨线程修改 StateInfo 类的静态成员变量,切记!
class StateInfo
{
// AI友元类
friend class AIAlgorithm;
public:
StateInfo();
virtual ~StateInfo();
// 拷贝构造函数
StateInfo(StateInfo &);
StateInfo(const StateInfo &);
// 运算符重载
StateInfo &operator=(const StateInfo &);
StateInfo &operator=(StateInfo &);
Position *position { nullptr };
};
#endif /* POSITION_H */

View File

@ -39,7 +39,7 @@ enum move_t : int32_t
//MOVE_NULL = 65
};
static const int MOVE_COUNT = 40;
constexpr int MAX_MOVES = 40;
enum color_t : uint8_t
{

View File

@ -148,7 +148,7 @@ extern deque<int> openingBookDequeBak;
void GameController::gameStart()
{
state.start();
state.position->start();
st = state;
// 每隔100毫秒调用一次定时器处理函数
@ -177,7 +177,7 @@ void GameController::gameReset()
timeID = 0;
// 重置游戏
state.reset();
state.position->reset();
st = state;
// 停掉线程
@ -247,7 +247,7 @@ void GameController::gameReset()
// 更新棋谱
manualListModel.removeRows(0, manualListModel.rowCount());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), state.getCmdLine());
manualListModel.setData(manualListModel.index(0), state.position->getCmdLine());
currentRow = 0;
// 发出信号通知主窗口更新LCD显示
@ -256,22 +256,22 @@ void GameController::gameReset()
emit time2Changed(qtime.toString("hh:mm:ss"));
// 发信号更新状态栏
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
emit statusBarChanged(message);
// 更新比分 LCD 显示
emit nGamesPlayedChanged(QString::number(state.nPlayed, 10));
emit score1Changed(QString::number(state.score[BLACK], 10));
emit score2Changed(QString::number(state.score[WHITE], 10));
emit scoreDrawChanged(QString::number(state.score_draw, 10));
emit nGamesPlayedChanged(QString::number(state.position->nPlayed, 10));
emit score1Changed(QString::number(state.position->score[BLACK], 10));
emit score2Changed(QString::number(state.position->score[WHITE], 10));
emit scoreDrawChanged(QString::number(state.position->score_draw, 10));
// 更新胜率 LCD 显示
state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw;
state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw;
int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0;
if (state.nPlayed != 0) {
winningRate_1 = state.score[BLACK] * 10000 / state.nPlayed;
winningRate_2 = state.score[WHITE] * 10000 / state.nPlayed;
winningRate_draw = state.score_draw * 10000 / state.nPlayed;
if (state.position->nPlayed != 0) {
winningRate_1 = state.position->score[BLACK] * 10000 / state.position->nPlayed;
winningRate_2 = state.position->score[WHITE] * 10000 / state.position->nPlayed;
winningRate_draw = state.position->score_draw * 10000 / state.position->nPlayed;
}
emit winningRate1Changed(QString::number(winningRate_1, 10));
@ -326,7 +326,7 @@ void GameController::setRule(int ruleNo, step_t stepLimited /*= -1*/, int timeLi
}
// 设置模型规则,重置游戏
state.setPosition(&RULES[ruleNo]);
state.position->setPosition(&RULES[ruleNo]);
st = state;
// 重置游戏
@ -558,13 +558,13 @@ void GameController::flip()
#ifndef TRAINING_MODE
stopAndWaitAiThreads();
state.position->board.mirror(state.cmdlist, state.cmdline, state.move, state.currentSquare);
state.position->board.rotate(180, state.cmdlist, state.cmdline, state.move, state.currentSquare);
state.position->board.mirror(state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare);
state.position->board.rotate(180, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare);
st = state;
// 更新棋谱
int row = 0;
for (const auto &str : *(state.getCmdList())) {
for (const auto &str : *(state.position->getCmdList())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
@ -585,13 +585,13 @@ void GameController::mirror()
#ifndef TRAINING_MODE
stopAndWaitAiThreads();
state.position->board.mirror(state.cmdlist, state.cmdline, state.move, state.currentSquare);
state.position->board.mirror(state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare);
st = state;
// 更新棋谱
int row = 0;
for (const auto &str : *(state.getCmdList())) {
for (const auto &str : *(state.position->getCmdList())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
@ -614,13 +614,13 @@ void GameController::turnRight()
#ifndef TRAINING_MODE
stopAndWaitAiThreads();
state.position->board.rotate(-90, state.cmdlist, state.cmdline, state.move, state.currentSquare);
state.position->board.rotate(-90, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare);
st = state;
// 更新棋谱
int row = 0;
for (const auto &str : *(state.getCmdList())) {
for (const auto &str : *(state.position->getCmdList())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
@ -641,12 +641,12 @@ void GameController::turnLeft()
#ifndef TRAINING_MODE
stopAndWaitAiThreads();
state.position->board.rotate(90, state.cmdlist, state.cmdline, state.move, state.currentSquare);
state.position->board.rotate(90, state.position->cmdlist, state.position->cmdline, state.position->move, state.position->currentSquare);
st = state;
// 更新棋谱
int row = 0;
for (const auto &str : *(state.getCmdList())) {
for (const auto &str : *(state.position->getCmdList())) {
manualListModel.setData(manualListModel.index(row++), str.c_str());
}
@ -664,9 +664,9 @@ void GameController::timerEvent(QTimerEvent *event)
static QTime qt1, qt2;
// 玩家的已用时间
state.update();
remainingTime[BLACK] = state.getElapsedTime(BLACK);
remainingTime[WHITE] = state.getElapsedTime(WHITE);
state.position->update();
remainingTime[BLACK] = state.position->getElapsedTime(BLACK);
remainingTime[WHITE] = state.position->getElapsedTime(WHITE);
// 如果规则要求计时则time1和time2表示倒计时
if (timeLimit > 0) {
@ -682,7 +682,7 @@ void GameController::timerEvent(QTimerEvent *event)
emit time2Changed(qt2.toString("hh:mm:ss"));
// 如果胜负已分
player_t winner = state.getWinner();
player_t winner = state.position->getWinner();
if (winner != PLAYER_NOBODY) {
// 停止计时
killTimer(timeID);
@ -692,7 +692,7 @@ void GameController::timerEvent(QTimerEvent *event)
#ifndef TRAINING_MODE
// 发信号更新状态栏
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
emit statusBarChanged(message);
// 弹框
@ -768,13 +768,13 @@ bool GameController::actionPiece(QPointF pos)
manualListModel.removeRows(currentRow + 1, manualListModel.rowCount() - currentRow - 1);
// 如果再决出胜负后悔棋,则重新启动计时
if (state.getWinner() == PLAYER_NOBODY) {
if (state.position->getWinner() == PLAYER_NOBODY) {
// 重新启动计时
timeID = startTimer(100);
// 发信号更新状态栏
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
emit statusBarChanged(message);
#ifndef MOBILE_APP_UI
}
@ -785,7 +785,7 @@ bool GameController::actionPiece(QPointF pos)
}
// 如果未开局则开局
if (state.getPhase() == PHASE_READY)
if (state.position->getPhase() == PHASE_READY)
gameStart();
// 判断执行选子、落子或去子
@ -793,15 +793,15 @@ bool GameController::actionPiece(QPointF pos)
PieceItem *piece = nullptr;
QGraphicsItem *item = scene.itemAt(pos, QTransform());
switch (state.getAction()) {
switch (state.position->getAction()) {
case ACTION_PLACE:
if (state._place(r, s)) {
if (state.getAction() == ACTION_CAPTURE) {
if (state.position->_place(r, s)) {
if (state.position->getAction() == ACTION_CAPTURE) {
// 播放成三音效
playSound(GAME_SOUND_MILL, state.getSideToMove());
playSound(GAME_SOUND_MILL, state.position->getSideToMove());
} else {
// 播放移动棋子音效
playSound(GAME_SOUND_DROG, state.getSideToMove());
playSound(GAME_SOUND_DROG, state.position->getSideToMove());
}
result = true;
break;
@ -814,24 +814,24 @@ bool GameController::actionPiece(QPointF pos)
piece = qgraphicsitem_cast<PieceItem *>(item);
if (!piece)
break;
if (state.choose(r, s)) {
if (state.position->choose(r, s)) {
// 播放选子音效
playSound(GAME_SOUND_CHOOSE, state.getSideToMove());
playSound(GAME_SOUND_CHOOSE, state.position->getSideToMove());
result = true;
} else {
// 播放禁止音效
playSound(GAME_SOUND_FORBIDDEN, state.getSideToMove());
playSound(GAME_SOUND_FORBIDDEN, state.position->getSideToMove());
}
break;
case ACTION_CAPTURE:
if (state._capture(r, s)) {
if (state.position->_capture(r, s)) {
// 播放音效
playSound(GAME_SOUND_CAPTURE, state.getSideToMove());
playSound(GAME_SOUND_CAPTURE, state.position->getSideToMove());
result = true;
} else {
// 播放禁止音效
playSound(GAME_SOUND_FORBIDDEN, state.getSideToMove());
playSound(GAME_SOUND_FORBIDDEN, state.position->getSideToMove());
}
break;
@ -842,7 +842,7 @@ bool GameController::actionPiece(QPointF pos)
if (result) {
// 发信号更新状态栏
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
emit statusBarChanged(message);
// 将新增的棋谱行插入到ListModel
@ -850,7 +850,7 @@ bool GameController::actionPiece(QPointF pos)
int k = 0;
// 输出命令行
for (const auto & i : *(state.getCmdList())) {
for (const auto & i : *(state.position->getCmdList())) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
@ -860,7 +860,7 @@ bool GameController::actionPiece(QPointF pos)
// 播放胜利或失败音效
#ifndef DONOT_PLAY_WIN_SOUND
player_t winner = state.getWinner();
player_t winner = state.position->getWinner();
if (winner != PLAYER_NOBODY &&
(manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over."))
playSound(GAME_SOUND_WIN, winner);
@ -868,7 +868,7 @@ bool GameController::actionPiece(QPointF pos)
// AI设置
// 如果还未决出胜负
if (state.getWinner() == PLAYER_NOBODY) {
if (state.position->getWinner() == PLAYER_NOBODY) {
resumeAiThreads(state.position->sideToMove);
}
// 如果已经决出胜负
@ -887,7 +887,7 @@ bool GameController::actionPiece(QPointF pos)
bool GameController::giveUp()
{
bool result = state.giveup(state.position->sideToMove);
bool result = state.position->giveup(state.position->sideToMove);
if (!result) {
return false;
@ -900,7 +900,7 @@ bool GameController::giveUp()
int k = 0;
// 输出命令行
for (const auto & i : *(state.getCmdList())) {
for (const auto & i : *(state.position->getCmdList())) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
@ -908,8 +908,8 @@ bool GameController::giveUp()
manualListModel.setData(manualListModel.index(currentRow), i.c_str());
}
if (state.getWinner() != PLAYER_NOBODY)
playSound(GAME_SOUND_GIVE_UP, state.getSideToMove());
if (state.position->getWinner() != PLAYER_NOBODY)
playSound(GAME_SOUND_GIVE_UP, state.position->getSideToMove());
#endif // TRAINING_MODE
@ -934,7 +934,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
// 声音
sound_t soundType = GAME_SOUND_NONE;
switch (state.getAction()) {
switch (state.position->getAction()) {
case ACTION_CHOOSE:
case ACTION_PLACE:
soundType = GAME_SOUND_DROG;
@ -948,45 +948,45 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
#endif
// 如果未开局则开局
if (state.getPhase() == PHASE_READY) {
if (state.position->getPhase() == PHASE_READY) {
gameStart();
}
if (!state.command(cmd.toStdString().c_str()))
if (!state.position->command(cmd.toStdString().c_str()))
return false;
#ifndef TRAINING_MODE
if (soundType == GAME_SOUND_DROG && state.getAction() == ACTION_CAPTURE) {
if (soundType == GAME_SOUND_DROG && state.position->getAction() == ACTION_CAPTURE) {
soundType = GAME_SOUND_MILL;
}
if (update) {
playSound(soundType, state.getSideToMove());
playSound(soundType, state.position->getSideToMove());
updateScence(state);
}
// 发信号更新状态栏
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
emit statusBarChanged(message);
// 对于新开局
if (state.getCmdList()->size() <= 1) {
if (state.position->getCmdList()->size() <= 1) {
manualListModel.removeRows(0, manualListModel.rowCount());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), state.getCmdLine());
manualListModel.setData(manualListModel.index(0), state.position->getCmdLine());
currentRow = 0;
}
// 对于当前局
else {
currentRow = manualListModel.rowCount() - 1;
// 跳过已添加行,迭代器不支持+运算符,只能一个个++
auto i = (state.getCmdList()->begin());
for (int r = 0; i != (state.getCmdList())->end(); i++) {
auto i = (state.position->getCmdList()->begin());
for (int r = 0; i != (state.position->getCmdList())->end(); i++) {
if (r++ > currentRow)
break;
}
// 将新增的棋谱行插入到ListModel
while (i != state.getCmdList()->end()) {
while (i != state.position->getCmdList()->end()) {
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), (*i++).c_str());
}
@ -994,7 +994,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
// 播放胜利或失败音效
#ifndef DONOT_PLAY_WIN_SOUND
player_t winner = state.getWinner();
player_t winner = state.position->getWinner();
if (winner != PLAYER_NOBODY &&
(manualListModel.data(manualListModel.index(currentRow - 1))).toString().contains("Time over.")) {
playSound(GAME_SOUND_WIN, winner);
@ -1004,7 +1004,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
// AI设置
// 如果还未决出胜负
if (state.getWinner() == PLAYER_NOBODY) {
if (state.position->getWinner() == PLAYER_NOBODY) {
resumeAiThreads(state.position->sideToMove);
}
// 如果已经决出胜负
@ -1077,7 +1077,7 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
#ifdef MESSAGEBOX_ENABLE
// 弹框
message = QString::fromStdString(state.getTips());
message = QString::fromStdString(state.position->getTips());
QMessageBox::about(NULL, "游戏结果", message);
#endif
}
@ -1113,11 +1113,11 @@ bool GameController::phaseChange(int row, bool forceUpdate)
for (int i = 0; i <= row; i++) {
loggerDebug("%s\n", mlist.at(i).toStdString().c_str());
st.command(mlist.at(i).toStdString().c_str());
st.position->command(mlist.at(i).toStdString().c_str());
}
// 下面这步关键,会让悔棋者承担时间损失
st.setStartTime(static_cast<int>(state.getStartTimeb()));
st.position->setStartTime(static_cast<int>(state.position->getStartTimeb()));
// 刷新棋局场景
updateScence(st);
@ -1138,7 +1138,7 @@ bool GameController::updateScence()
bool GameController::updateScence(StateInfo &g)
{
#ifndef TRAINING_MODE
const location_t *board = g.getBoardLocations();
const location_t *board = g.position->getBoardLocations();
QPointF pos;
// game类中的棋子代码
@ -1192,10 +1192,10 @@ bool GameController::updateScence(StateInfo &g)
if (j == (Board::N_SEATS) * (Board::N_RINGS + 1)) {
// 判断是被吃掉的子,还是未安放的子
if (key & PIECE_BLACK) {
pos = (key - 0x11 < nTotalPieces / 2 - g.getPiecesInHandCount(BLACK)) ?
pos = (key - 0x11 < nTotalPieces / 2 - g.position->getPiecesInHandCount(BLACK)) ?
scene.pos_p2_g : scene.pos_p1;
} else {
pos = (key - 0x21 < nTotalPieces / 2 - g.getPiecesInHandCount(WHITE)) ?
pos = (key - 0x21 < nTotalPieces / 2 - g.position->getPiecesInHandCount(WHITE)) ?
scene.pos_p1_g : scene.pos_p2;
}
@ -1204,7 +1204,7 @@ bool GameController::updateScence(StateInfo &g)
deletedPiece = piece;
#ifdef GAME_PLACING_SHOW_CAPTURED_PIECES
if (state.getPhase() == GAME_MOVING) {
if (state.position->getPhase() == GAME_MOVING) {
#endif
QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos");
animation->setDuration(durationTime);
@ -1222,7 +1222,7 @@ bool GameController::updateScence(StateInfo &g)
}
// 添加摆棋阶段禁子点
if (rule.hasForbiddenLocations && g.getPhase() == PHASE_PLACING) {
if (rule.hasForbiddenLocations && g.position->getPhase() == PHASE_PLACING) {
for (int j = SQ_BEGIN; j < SQ_END; j++) {
if (board[j] == PIECE_FORBIDDEN) {
pos = scene.rs2pos(j / Board::N_SEATS, j % Board::N_SEATS + 1);
@ -1241,7 +1241,7 @@ bool GameController::updateScence(StateInfo &g)
}
// 走棋阶段清除禁子点
if (rule.hasForbiddenLocations && g.getPhase() != PHASE_PLACING) {
if (rule.hasForbiddenLocations && g.position->getPhase() != PHASE_PLACING) {
while (nTotalPieces < static_cast<int>(pieceList.size())) {
delete pieceList.at(pieceList.size() - 1);
pieceList.pop_back();
@ -1249,9 +1249,9 @@ bool GameController::updateScence(StateInfo &g)
}
// 选中当前棋子
int ipos = g.getCurrentSquare();
int ipos = g.position->getCurrentSquare();
if (ipos) {
key = board[g.getCurrentSquare()];
key = board[g.position->getCurrentSquare()];
ipos = key & PIECE_BLACK ? (key - PIECE_B1) * 2 : (key - PIECE_W1) * 2 + 1;
if (ipos >= 0 && ipos < nTotalPieces) {
currentPiece = pieceList.at(static_cast<size_t>(ipos));
@ -1267,17 +1267,17 @@ bool GameController::updateScence(StateInfo &g)
animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
// 更新比分 LCD 显示
emit score1Changed(QString::number(g.score[BLACK], 10));
emit score2Changed(QString::number(g.score[WHITE], 10));
emit scoreDrawChanged(QString::number(g.score_draw, 10));
emit score1Changed(QString::number(g.position->score[BLACK], 10));
emit score2Changed(QString::number(g.position->score[WHITE], 10));
emit scoreDrawChanged(QString::number(g.position->score_draw, 10));
// 更新胜率 LCD 显示
state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw;
state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw;
int winningRate_1 = 0, winningRate_2 = 0, winningRate_draw = 0;
if (state.nPlayed != 0) {
winningRate_1 = state.score[BLACK] * 10000 / state.nPlayed;
winningRate_2 = state.score[WHITE] * 10000 / state.nPlayed;
winningRate_draw = state.score_draw * 10000 / state.nPlayed;
if (state.position->nPlayed != 0) {
winningRate_1 = state.position->score[BLACK] * 10000 / state.position->nPlayed;
winningRate_2 = state.position->score[WHITE] * 10000 / state.position->nPlayed;
winningRate_draw = state.position->score_draw * 10000 / state.position->nPlayed;
}
emit winningRate1Changed(QString::number(winningRate_1, 10));
@ -1303,7 +1303,7 @@ void GameController::showTestWindow()
void GameController::humanGiveUp()
{
if (state.getWinner() == PLAYER_NOBODY) {
if (state.position->getWinner() == PLAYER_NOBODY) {
giveUp();
}
}
@ -1354,16 +1354,16 @@ void GameController::saveScore()
textStream << "" << endl;
state.nPlayed = state.score[BLACK] + state.score[WHITE] + state.score_draw;
state.position->nPlayed = state.position->score[BLACK] + state.position->score[WHITE] + state.position->score_draw;
if (state.nPlayed == 0) {
if (state.position->nPlayed == 0) {
goto out;
}
textStream << "Sum\t" + QString::number(state.nPlayed) << endl;
textStream << "Black\t" + QString::number(state.score[BLACK]) + "\t" + QString::number(state.score[BLACK] * 10000 / state.nPlayed) << endl;
textStream << "White\t" + QString::number(state.score[WHITE]) + "\t" + QString::number(state.score[WHITE] * 10000 / state.nPlayed) << endl;
textStream << "Draw\t" + QString::number(state.score_draw) + "\t" + QString::number(state.score_draw * 10000 / state.nPlayed) << endl;
textStream << "Sum\t" + QString::number(state.position->nPlayed) << endl;
textStream << "Black\t" + QString::number(state.position->score[BLACK]) + "\t" + QString::number(state.position->score[BLACK] * 10000 / state.position->nPlayed) << endl;
textStream << "White\t" + QString::number(state.position->score[WHITE]) + "\t" + QString::number(state.position->score[WHITE] * 10000 / state.position->nPlayed) << endl;
textStream << "Draw\t" + QString::number(state.position->score_draw) + "\t" + QString::number(state.position->score_draw * 10000 / state.position->nPlayed) << endl;
out:
file.flush();

View File

@ -608,7 +608,7 @@ void MillGameWindow::on_actionNew_N_triggered()
QString strDate = QDateTime::currentDateTime().toString("yyyy-MM-dd");
QString whoWin;
switch (gameController->getState().getWinner()) {
switch (gameController->getState().position->getWinner()) {
case PLAYER_BLACK:
whoWin = "Black-Win";
break;