refactor: 将 StateInfo 中大多数成员转移到 Position 类中
This commit is contained in:
parent
0f9ae19661
commit
f864c7bbc4
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,13 +24,50 @@
|
|||
#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:
|
||||
MoveList() = delete;
|
||||
|
||||
MoveList &operator=(const MoveList &) = delete;
|
||||
|
||||
|
||||
// 生成着法表
|
||||
static void create();
|
||||
|
||||
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue