Sanmill/NineChess/src/ninechess.cpp

2310 lines
68 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/****************************************************************************
** by liuweilhy, 2013.01.14
** Mail: liuweilhy@163.com
** This file is part of the NineChess game.
****************************************************************************/
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include <algorithm>
#include "ninechess.h"
// 对静态常量数组的定义要放在类外,不要放在头文件
// 预定义的4套规则
const struct NineChess::Rule NineChess::RULES[N_RULES] = {
{
"成三棋", // 成三棋
// 规则说明
"1. 双方各9颗子开局依次摆子\n"
"2. 凡出现三子相连,就提掉对手一子;\n"
"3. 不能提对手的“三连”子,除非无子可提;\n"
"4. 同时出现两个“三连”只能提一子;\n"
"5. 摆完后依次走子,每次只能往相邻位置走一步;\n"
"6. 把对手棋子提到少于3颗时胜利\n"
"7. 走棋阶段不能行动(被“闷”)算负。",
9, // 双方各9子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
false, // 先摆棋者先行棋
true, // 可以重复成三
false, // 多个“三连”只能提一子
false, // 不能提对手的“三连”子,除非无子可提;
true, // 摆棋满子闷棋只有12子棋才出现算先手负
true, // 走棋阶段不能行动(被“闷”)算负
false, // 剩三子时不可以飞棋
0, // 不计步数
0 // 不计时
},
{
"打三棋(12连棋)", // 打三棋
// 规则说明
"1. 双方各12颗子棋盘有斜线\n"
"2. 摆棋阶段被提子的位置不能再摆子,直到走棋阶段;\n"
"3. 摆棋阶段,摆满棋盘算先手负;\n"
"4. 走棋阶段,后摆棋的一方先走;\n"
"5. 同时出现两个“三连”只能提一子;\n"
"6. 其它规则与成三棋基本相同。",
12, // 双方各12子
3, // 赛点子数为3
true, // 有斜线
true, // 有禁点,摆棋阶段被提子的点不能再摆子
true, // 后摆棋者先行棋
true, // 可以重复成三
false, // 多个“三连”只能提一子
true, // 可以提对手的“三连”子
true, // 摆棋满子闷棋只有12子棋才出现算先手负
true, // 走棋阶段不能行动(被“闷”)算负
false, // 剩三子时不可以飞棋
0, // 不计步数
0 // 不计时
},
{
"九连棋", // 九连棋
// 规则说明
"1. 规则与成三棋基本相同,只是它的棋子有序号,\n"
"2. 相同序号、位置的“三连”不能重复提子;\n"
"3. 走棋阶段不能行动(被“闷”),则由对手继续走棋;\n"
"4. 一步出现几个“三连”就可以提几个子。",
9, // 双方各9子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
false, // 先摆棋者先行棋
false, // 不可以重复成三
true, // 出现几个“三连”就可以提几个子
false, // 不能提对手的“三连”子,除非无子可提;
true, // 摆棋满子闷棋只有12子棋才出现算先手负
false, // 走棋阶段不能行动(被“闷”),则由对手继续走棋
false, // 剩三子时不可以飞棋
0, // 不计步数
0 // 不计时
},
{
"莫里斯九子棋", // 莫里斯九子棋
// 规则说明
"规则与成三棋基本相同只是在走子阶段当一方仅剩3子时他可以飞子到任意空位。",
9, // 双方各9子
3, // 赛点子数为3
false, // 没有斜线
false, // 没有禁点,摆棋阶段被提子的点可以再摆子
false, // 先摆棋者先行棋
true, // 可以重复成三
false, // 多个“三连”只能提一子
false, // 不能提对手的“三连”子,除非无子可提;
true, // 摆棋满子闷棋只有12子棋才出现算先手负
true, // 走棋阶段不能行动(被“闷”)算负
true, // 剩三子时可以飞棋
0, // 不计步数
0 // 不计时
}
};
// 名义上是个数组,实际上相当于一个判断是否在棋盘上的函数
const int NineChess::onBoard[(N_RINGS + 2) * N_SEATS] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 着法表
int NineChess::moveTable[N_POINTS][N_MOVE_DIRECTIONS] = { 0 };
// 成三表
int NineChess::millTable[N_POINTS][N_DIRECTIONS][N_RINGS - 1] = { 0 };
NineChess::NineChess()
{
// 单独提出 board 等数据,免得每次都写 context.board;
board_ = context.board;
//hash_ = &context.hash;
//zobrist_ = &context.zobrist;
// 创建哈希数据
constructHash();
// 默认选择第1号规则即“打三棋”
setContext(&RULES[1]);
}
NineChess::NineChess(const NineChess &chess)
{
currentRule = chess.currentRule;
context = chess.context;
currentStep = chess.currentStep;
board_ = context.board;
currentPos = chess.currentPos;
winner = chess.winner;
startTimeb = chess.startTimeb;
currentTimeb = chess.currentTimeb;
elapsedMS_1 = chess.elapsedMS_1;
elapsedMS_2 = chess.elapsedMS_2;
move_ = chess.move_;
memcpy(cmdline, chess.cmdline, sizeof(cmdline));
cmdlist = chess.cmdlist;
tips = chess.tips;
}
const NineChess &NineChess::operator=(const NineChess &chess)
{
if (this == &chess)
return *this;
currentRule = chess.currentRule;
context = chess.context;
currentStep = chess.currentStep;
board_ = context.board;
currentPos = chess.currentPos;
winner = chess.winner;
startTimeb = chess.startTimeb;
currentTimeb = chess.currentTimeb;
elapsedMS_1 = chess.elapsedMS_1;
elapsedMS_2 = chess.elapsedMS_2;
move_ = chess.move_;
memcpy(cmdline, chess.cmdline, sizeof(cmdline));
cmdlist = chess.cmdlist;
tips = chess.tips;
return *this;
}
NineChess::~NineChess()
{
}
void NineChess::constructHash()
{
context.hash = 0;
#if 0
gameMovingHash = rand64();
player2sTurnHash = rand64();
uint64_t zobrist[N_POINTS][POINT_TYPE_COUNT];
for (int p = 0; p < N_POINTS; p++) {
for (int t = NineChess::POINT_TYPE_EMPTY; t <= NineChess::POINT_TYPE_FORBIDDEN; t++) {
zobrist[p][t] = rand64();
}
}
#endif
}
NineChess::Player NineChess::getOpponent(NineChess::Player player)
{
switch (player)
{
case PLAYER1:
return PLAYER2;
break;
case PLAYER2:
return PLAYER1;
break;
default:
return NOBODY;
break;
}
return NOBODY;
}
void NineChess::createMoveTable()
{
for (int r = 1; r <= N_RINGS; r++) {
for (int s = 0; s < N_SEATS; s++) {
// 顺时针走一步的位置
moveTable[r * N_SEATS + s][MOVE_DIRECTION_CLOCKWISE] = r * N_SEATS + (s + 1) % N_SEATS;
// 逆时针走一步的位置
moveTable[r * N_SEATS + s][MOVE_DIRECTION_ANTICLOCKWISE] = r * N_SEATS + (s + N_SEATS - 1) % N_SEATS;
// 如果是 0、2、4、6位偶数位或是有斜线
if (!(s & 1) || this->currentRule.hasObliqueLines) {
if (r > 1) {
// 向内走一步的位置
moveTable[r * N_SEATS + s][MOVE_DIRECTION_INWARD] = (r - 1) * N_SEATS + s;
}
if (r < N_RINGS) {
// 向外走一步的位置
moveTable[r * N_SEATS + s][MOVE_DIRECTION_OUTWARD] = (r + 1) * N_SEATS + s;
}
}
#if 0
// 对于无斜线情况下的1、3、5、7位奇数位则都设为棋盘外点默认'\x00'
else {
// 向内走一步的位置设为随便棋盘外一点
moveTable[i * SEAT + j][2] = '\x00';
// 向外走一步的位置设为随便棋盘外一点
moveTable[i * SEAT + j][3] = '\x00';
}
#endif
}
}
}
void NineChess::createMillTable()
{
for (int i = 0; i < N_SEATS; i++) {
// 内外方向的“成三”
// 如果是0、2、4、6位偶数位或是有斜线
if (!(i & 1) || this->currentRule.hasObliqueLines) {
millTable[1 * N_SEATS + i][0][0] = 2 * N_SEATS + i;
millTable[1 * N_SEATS + i][0][1] = 3 * N_SEATS + i;
millTable[2 * N_SEATS + i][0][0] = 1 * N_SEATS + i;
millTable[2 * N_SEATS + i][0][1] = 3 * N_SEATS + i;
millTable[3 * N_SEATS + i][0][0] = 1 * N_SEATS + i;
millTable[3 * N_SEATS + i][0][1] = 2 * N_SEATS + i;
}
// 对于无斜线情况下的1、3、5、7位奇数位
else {
// 置空该组“成三”
millTable[1 * N_SEATS + i][0][0] = 0;
millTable[1 * N_SEATS + i][0][1] = 0;
millTable[2 * N_SEATS + i][0][0] = 0;
millTable[2 * N_SEATS + i][0][1] = 0;
millTable[3 * N_SEATS + i][0][0] = 0;
millTable[3 * N_SEATS + i][0][1] = 0;
}
// 当前圈上的“成三”
// 如果是0、2、4、6位
if (!(i & 1)) {
millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + 1) % N_SEATS;
millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + 1) % N_SEATS;
millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + 1) % N_SEATS;
millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
// 置空另一组“成三”
millTable[1 * N_SEATS + i][2][0] = 0;
millTable[1 * N_SEATS + i][2][1] = 0;
millTable[2 * N_SEATS + i][2][0] = 0;
millTable[2 * N_SEATS + i][2][1] = 0;
millTable[3 * N_SEATS + i][2][0] = 0;
millTable[3 * N_SEATS + i][2][1] = 0;
}
// 对于1、3、5、7位奇数位
else {
// 当前圈上逆时针的“成三”
millTable[1 * N_SEATS + i][1][0] = 1 * N_SEATS + (i + N_SEATS - 2) % N_SEATS;
millTable[1 * N_SEATS + i][1][1] = 1 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
millTable[2 * N_SEATS + i][1][0] = 2 * N_SEATS + (i + N_SEATS - 2) % N_SEATS;
millTable[2 * N_SEATS + i][1][1] = 2 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
millTable[3 * N_SEATS + i][1][0] = 3 * N_SEATS + (i + N_SEATS - 2) % N_SEATS;
millTable[3 * N_SEATS + i][1][1] = 3 * N_SEATS + (i + N_SEATS - 1) % N_SEATS;
// 当前圈上顺时针的“成三”
millTable[1 * N_SEATS + i][2][0] = 1 * N_SEATS + (i + 1) % N_SEATS;
millTable[1 * N_SEATS + i][2][1] = 1 * N_SEATS + (i + 2) % N_SEATS;
millTable[2 * N_SEATS + i][2][0] = 2 * N_SEATS + (i + 1) % N_SEATS;
millTable[2 * N_SEATS + i][2][1] = 2 * N_SEATS + (i + 2) % N_SEATS;
millTable[3 * N_SEATS + i][2][0] = 3 * N_SEATS + (i + 1) % N_SEATS;
millTable[3 * N_SEATS + i][2][1] = 3 * N_SEATS + (i + 2) % N_SEATS;
}
}
}
// 设置棋局状态和棋盘数据,用于初始化
bool NineChess::setContext(const struct Rule *rule, int maxStepsLedToDraw, int maxTimeLedToLose,
int initialStep, int flags, const char *board,
int nPiecesInHand_1, int nPiecesInHand_2, int nPiecesNeedRemove, uint64_t hash)
{
// 有效性判断
if (maxStepsLedToDraw < 0 || maxTimeLedToLose < 0 || initialStep < 0 ||
nPiecesInHand_1 < 0 || nPiecesInHand_2 < 0 || nPiecesNeedRemove < 0) {
return false;
}
// 根据规则
this->currentRule = *rule;
this->currentRule.maxStepsLedToDraw = maxStepsLedToDraw;
this->currentRule.maxTimeLedToLose = maxTimeLedToLose;
// 设置棋局数据
{
// 设置步数
this->currentStep = initialStep;
// 局面阶段标识
if (flags & GAME_NOTSTARTED) {
context.stage = GAME_NOTSTARTED;
}
else if (flags & GAME_PLACING) {
context.stage = GAME_PLACING;
}
else if (flags & GAME_MOVING) {
context.stage = GAME_MOVING;
//context.hash ^= // TODO
}
else if (flags & GAME_OVER) {
context.stage = GAME_OVER;
}
else {
return false;
}
// 轮流状态标识
if (flags & PLAYER1) {
// if (context.turn == PLAYER2) {
// context.hash ^= player2sTurnHash;
// }
context.turn = PLAYER1;
}
else if (flags & PLAYER2) {
// if (context.turn == PLAYER1) {
// context.hash ^= player2sTurnHash;
// }
context.turn = PLAYER2;
}
else {
return false;
}
// 动作状态标识
if (flags & ACTION_CHOOSE)
context.action = ACTION_CHOOSE;
else if (flags & ACTION_PLACE)
context.action = ACTION_PLACE;
else if (flags & ACTION_CAPTURE)
context.action = ACTION_CAPTURE;
else
return false;
// 当前棋局3×8
if (board == nullptr) {
memset(context.board, 0, sizeof(context.board));
context.hash = 0;
} else {
memcpy(context.board, board, sizeof(context.board));
context.hash = hash;
}
// 计算盘面子数
// 棋局抽象为一个5×8的数组上下两行留空
/*
0x00 代表无棋子
0x0F 代表禁点
0x110x1C 代表先手第 112 子
0x210x2C 代表后手第 112 子
判断棋子是先手的用 (board[i] & 0x10)
判断棋子是后手的用 (board[i] & 0x20)
*/
context.nPiecesOnBoard_1 = context.nPiecesOnBoard_2 = 0;
for (int r = 1; r < N_RINGS + 2; r++) {
for (int s = 0; s < N_SEATS; s++) {
int pos = r * N_SEATS + s;
if (context.board[pos] & '\x10') {
context.nPiecesOnBoard_1++;
}
else if (context.board[pos] & '\x20') {
context.nPiecesOnBoard_2++;
}
else if (context.board[pos] & '\x0F') {
// 不计算盘面子数
}
//updateHash(pos);
}
}
// 设置玩家盘面剩余子数和未放置子数
if (context.nPiecesOnBoard_1 > rule->nTotalPiecesEachSide ||
context.nPiecesOnBoard_2 > rule->nTotalPiecesEachSide) {
return false;
}
if (nPiecesInHand_1 < 0 || nPiecesInHand_2 < 0) {
return false;
}
context.nPiecesInHand_1 = rule->nTotalPiecesEachSide - context.nPiecesOnBoard_1;
context.nPiecesInHand_2 = rule->nTotalPiecesEachSide - context.nPiecesOnBoard_2;
context.nPiecesInHand_1 = std::min(nPiecesInHand_1, context.nPiecesInHand_1);
context.nPiecesInHand_2 = std::min(nPiecesInHand_2, context.nPiecesInHand_2);
// 设置去子状态时的剩余尚待去除子数
if (flags & ACTION_CAPTURE) {
if (0 <= nPiecesNeedRemove && nPiecesNeedRemove < 3)
context.nPiecesNeedRemove = nPiecesNeedRemove;
} else {
context.nPiecesNeedRemove = 0;
}
// 清空成三记录
context.millList.clear();
}
// 胜负标识
winner = NOBODY;
// 生成着法表
createMoveTable();
// 生成成三表
createMillTable();
// 不选中棋子
currentPos = 0;
// 用时置零
elapsedMS_1 = elapsedMS_2 = 0;
// 提示
setTips();
// 计棋谱
cmdlist.clear();
int r;
for (r = 0; r < N_RULES; r++) {
if (strcmp(this->currentRule.name, RULES[r].name) == 0)
break;
}
if (sprintf(cmdline, "r%1u s%03u t%02u", r + 1, maxStepsLedToDraw, maxTimeLedToLose) > 0) {
cmdlist.push_back(string(cmdline));
return true;
} else {
cmdline[0] = '\0';
return false;
}
//return true;
}
void NineChess::getContext(struct Rule &rule, int &step, int &flags,
int *&board, int &nPiecesInHand_1, int &nPiecesInHand_2, int &num_NeedRemove,
uint64_t &hash)
{
rule = this->currentRule;
step = this->currentStep;
flags = context.stage | context.turn | context.action;
this->board_ = board;
nPiecesInHand_1 = context.nPiecesInHand_1;
nPiecesInHand_2 = context.nPiecesInHand_2;
num_NeedRemove = context.nPiecesNeedRemove;
hash = context.hash;
}
bool NineChess::reset()
{
if (context.stage == GAME_NOTSTARTED && elapsedMS_1 == elapsedMS_2 == 0)
return true;
// 步数归零
currentStep = 0;
// 局面阶段标识
context.stage = GAME_NOTSTARTED;
// 轮流状态标识
context.turn = PLAYER1;
// 动作状态标识
context.action = ACTION_PLACE;
// 胜负标识
winner = NOBODY;
// 当前棋局3×8
memset(board_, 0, sizeof(context.board));
// 盘面子数归零
context.nPiecesOnBoard_1 = context.nPiecesOnBoard_2 = 0;
// 设置玩家盘面剩余子数和未放置子数
context.nPiecesInHand_1 = context.nPiecesInHand_2 = currentRule.nTotalPiecesEachSide;
// 设置去子状态时的剩余尚待去除子数
context.nPiecesNeedRemove = 0;
// 清空成三记录
context.millList.clear();
// 不选中棋子
currentPos = 0;
// 用时置零
elapsedMS_1 = elapsedMS_2 = 0;
// 哈希归零
context.hash = 0;
// 提示
setTips();
// 计棋谱
cmdlist.clear();
int i;
for (i = 0; i < N_RULES; i++) {
if (strcmp(this->currentRule.name, RULES[i].name) == 0)
break;
}
if (sprintf(cmdline, "r%1u s%03u t%02u", i + 1, currentRule.maxStepsLedToDraw, currentRule.maxTimeLedToLose) > 0) {
cmdlist.push_back(string(cmdline));
return true;
} else {
cmdline[0] = '\0';
return false;
}
return true;
}
bool NineChess::start()
{
switch (context.stage) {
// 如果游戏已经开始则返回false
case GAME_PLACING:
case GAME_MOVING:
return false;
// 如果游戏结束,则重置游戏,进入未开始状态
case GAME_OVER:
reset(); // 这里不要break;
// 如果游戏处于未开始状态
case GAME_NOTSTARTED:
// 启动计时器
ftime(&startTimeb);
// 进入开局状态
context.stage = GAME_PLACING;
return true;
default:
return false;
}
}
bool NineChess::getPieceCP(const Player &player, const int &number, int &c, int &p)
{
int piece;
if (player == PLAYER1)
piece = 0x10;
else if (player == PLAYER2)
piece = 0x20;
else
return false;
if (number > 0 && number <= currentRule.nTotalPiecesEachSide)
piece &= number;
else
return false;
for (int i = POS_BEGIN; i < POS_END; i++) {
if (board_[i] == piece) {
pos2cp(i, c, p);
return true;
}
}
return false;
}
// 获取当前棋子
bool NineChess::getCurrentPiece(Player &player, int &number)
{
if (!onBoard[currentPos])
return false;
if (board_[currentPos] & 0x10) {
player = PLAYER1;
number = board_[currentPos] - 0x10;
} else if (board_[currentPos] & 0x20) {
player = PLAYER2;
number = board_[currentPos] - 0x20;
} else
return false;
return true;
}
bool NineChess::pos2cp(const int pos, int &c, int &p)
{
if (pos < POS_BEGIN || POS_END <= pos)
return false;
c = pos / N_SEATS;
p = pos % N_SEATS + 1;
return true;
}
int NineChess::cp2pos(int c, int p)
{
if (c < 1 || c > N_RINGS || p < 1 || p > N_SEATS)
return 0;
return c * N_SEATS + p - 1;
}
bool NineChess::place(int c, int p, long time_p /* = -1*/)
{
// 如果局面为“结局”返回false
if (context.stage == GAME_OVER)
return false;
// 如果局面为“未开局”,则开局
if (context.stage == GAME_NOTSTARTED)
start();
// 如非“落子”状态返回false
if (context.action != ACTION_PLACE)
return false;
// 转换为 pos
int pos = cp2pos(c, p);
// 如果落子位置在棋盘外、已有子点或禁点返回false
if (!onBoard[pos] || board_[pos])
return false;
// 时间的临时变量
long player_ms = -1;
// 对于开局落子
int piece = '\x00';
int n = 0;
if (context.stage == GAME_PLACING) {
// 先手下
if (context.turn == PLAYER1) {
piece = '\x11' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_1;
context.nPiecesInHand_1--;
context.nPiecesOnBoard_1++;
}
// 后手下
else {
piece = '\x21' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_2;
context.nPiecesInHand_2--;
context.nPiecesOnBoard_2++;
}
board_[pos] = piece;
updateHash(pos);
move_ = pos;
player_ms = update(time_p);
sprintf(cmdline, "(%1u,%1u) %02u:%02u.%03u",
c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
currentPos = pos;
currentStep++;
// 如果决出胜负
if (win()) {
setTips();
return true;
}
n = addMills(currentPos);
// 开局阶段未成三
if (n == 0) {
// 如果双方都无未放置的棋子
if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) {
// 进入中局阶段
context.stage = GAME_MOVING;
// 进入选子状态
context.action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenPoints();
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER2;
} else {
context.turn = PLAYER1;
}
// 再决胜负
if (win()) {
setTips();
return true;
}
}
// 如果双方还有子
else {
// 设置轮到谁走
changeTurn();
}
}
// 如果成三
else {
// 设置去子数目
context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
context.action = ACTION_CAPTURE;
}
setTips();
return true;
}
// 对于中局落子
else if (context.stage == GAME_MOVING) {
// 如果落子不合法
if ((context.turn == PLAYER1 &&
(context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(context.turn == PLAYER2 &&
(context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
int i;
for (i = 0; i < 4; i++) {
if (pos == moveTable[currentPos][i])
break;
}
// 不在着法表中
if (i == 4)
return false;
}
// 移子
move_ = (currentPos << 8) + pos;
player_ms = update(time_p);
sprintf(cmdline, "(%1u,%1u)->(%1u,%1u) %02u:%02u.%03u", currentPos / N_SEATS, currentPos % N_SEATS + 1,
c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
board_[pos] = board_[currentPos];
updateHash(pos);
board_[currentPos] = '\x00';
updateHash(currentPos);
currentPos = pos;
currentStep++;
n = addMills(currentPos);
// 中局阶段未成三
if (n == 0) {
// 进入选子状态
context.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
setTips();
return true;
}
}
// 中局阶段成三
else {
// 设置去子数目
context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
context.action = ACTION_CAPTURE;
setTips();
}
setTips();
return true;
}
return false;
}
bool NineChess::capture(int c, int p, long time_p /* = -1*/)
{
// 如果局面为"未开局"或“结局”返回false
if (context.stage == GAME_NOTSTARTED || context.stage == GAME_OVER)
return false;
// 如非“去子”状态返回false
if (context.action != ACTION_CAPTURE)
return false;
// 如果去子完成返回false
if (context.nPiecesNeedRemove <= 0)
return false;
// 时间的临时变量
long player_ms = -1;
int pos = cp2pos(c, p);
// 对手
char opponent = context.turn == PLAYER1 ? 0x20 : 0x10;
// 判断去子是不是对手棋
if (!(opponent & board_[pos]))
return false;
// 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中
if (currentRule.allowRemoveMill == false &&
isInMills(pos) && !isAllInMills(opponent)) {
return false;
}
// 去子(设置禁点)
if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING)
board_[pos] = '\x0f';
else // 去子
board_[pos] = '\x00';
if (context.turn == PLAYER1)
context.nPiecesOnBoard_2--;
else if (context.turn == PLAYER2)
context.nPiecesOnBoard_1--;
move_ = -pos;
player_ms = update(time_p);
sprintf(cmdline, "-(%1u,%1u) %02u:%02u.%03u", c, p, player_ms / 60000, (player_ms % 60000) / 1000, player_ms % 1000);
cmdlist.push_back(string(cmdline));
currentPos = 0;
context.nPiecesNeedRemove--;
currentStep++;
updateHash(pos);
// 去子完成
// 如果决出胜负
if (win()) {
setTips();
return true;
}
// 还有其余的子要去吗
if (context.nPiecesNeedRemove > 0) {
// 继续去子
return true;
}
// 所有去子都完成了
else {
// 开局阶段
if (context.stage == GAME_PLACING) {
// 如果双方都无未放置的棋子
if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) {
// 进入中局阶段
context.stage = GAME_MOVING;
// 进入选子状态
context.action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenPoints();
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER2;
} else {
context.turn = PLAYER1;
}
// 再决胜负
if (win()) {
setTips();
return true;
}
}
// 如果双方还有子
else {
// 进入落子状态
context.action = ACTION_PLACE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
setTips();
return true;
}
}
}
// 中局阶段
else {
// 进入选子状态
context.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
setTips();
return true;
}
}
}
setTips();
return true;
}
bool NineChess::choose(int c, int p)
{
// 如果局面不是"中局”返回false
if (context.stage != GAME_MOVING)
return false;
// 如非“选子”或“落子”状态返回false
if (context.action != ACTION_CHOOSE && context.action != ACTION_PLACE)
return false;
int pos = cp2pos(c, p);
// 根据先后手,判断可选子
char t = '\0';
if (context.turn == PLAYER1)
t = '\x10';
else if (context.turn == PLAYER2)
t = '\x20';
// 判断选子是否可选
if (board_[pos] & t) {
// 判断pos处的棋子是否被“闷”
if (isSurrounded(pos)) {
return false;
}
// 选子
currentPos = pos;
// 选子完成,进入落子状态
context.action = ACTION_PLACE;
return true;
}
return false;
}
bool NineChess::place(int pos)
{
// 如果局面为“结局”返回false
if (context.stage == GAME_OVER)
return false;
// 如果局面为“未开局”,则开局
if (context.stage == GAME_NOTSTARTED)
start();
// 如非“落子”状态返回false
if (context.action != ACTION_PLACE)
return false;
// 如果落子位置在棋盘外、已有子点或禁点返回false
if (!onBoard[pos] || board_[pos])
return false;
// 对于开局落子
int piece = '\x00';
int n = 0;
if (context.stage == GAME_PLACING) {
// 先手下
if (context.turn == PLAYER1) {
piece = '\x11' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_1;
context.nPiecesInHand_1--;
context.nPiecesOnBoard_1++;
}
// 后手下
else {
piece = '\x21' + currentRule.nTotalPiecesEachSide - context.nPiecesInHand_2;
context.nPiecesInHand_2--;
context.nPiecesOnBoard_2++;
}
board_[pos] = piece;
updateHash(pos);
move_ = pos;
currentPos = pos;
//step++;
// 如果决出胜负
if (win()) {
//setTip();
return true;
}
n = addMills(currentPos);
// 开局阶段未成三
if (n == 0) {
// 如果双方都无未放置的棋子
if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) {
// 进入中局阶段
context.stage = GAME_MOVING;
// 进入选子状态
context.action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenPoints();
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER2;
} else {
context.turn = PLAYER1;
}
// 再决胜负
if (win()) {
//setTip();
return true;
}
}
// 如果双方还有子
else {
// 设置轮到谁走
changeTurn();
}
}
// 如果成三
else {
// 设置去子数目
context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
context.action = ACTION_CAPTURE;
}
setTips();
return true;
}
// 对于中局落子
else if (context.stage == GAME_MOVING) {
// 如果落子不合法
if ((context.turn == PLAYER1 &&
(context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(context.turn == PLAYER2 &&
(context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
int i;
for (i = 0; i < 4; i++) {
if (pos == moveTable[currentPos][i])
break;
}
// 不在着法表中
if (i == 4)
return false;
}
// 移子
move_ = (currentPos << 8) + pos;
board_[pos] = board_[currentPos];
updateHash(pos);
board_[currentPos] = '\x00';
updateHash(currentPos);
currentPos = pos;
//step++;
n = addMills(currentPos);
// 中局阶段未成三
if (n == 0) {
// 进入选子状态
context.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
//setTip();
return true;
}
}
// 中局阶段成三
else {
// 设置去子数目
context.nPiecesNeedRemove = currentRule.allowRemoveMultiPieces ? n : 1;
// 进入去子状态
context.action = ACTION_CAPTURE;
//setTip();
}
//setTip();
return true;
}
return false;
}
bool NineChess::capture(int pos)
{
// 如果局面为"未开局"或“结局”返回false
if (context.stage == GAME_NOTSTARTED || context.stage == GAME_OVER)
return false;
// 如非“去子”状态返回false
if (context.action != ACTION_CAPTURE)
return false;
// 如果去子完成返回false
if (context.nPiecesNeedRemove <= 0)
return false;
// 对手
char opponent = context.turn == PLAYER1 ? 0x20 : 0x10;
// 判断去子是不是对手棋
if (!(opponent & board_[pos]))
return false;
// 如果当前子是否处于“三连”之中,且对方还未全部处于“三连”之中
if (currentRule.allowRemoveMill == false &&
isInMills(pos) && !isAllInMills(opponent)) {
return false;
}
// 去子(设置禁点)
if (currentRule.hasForbiddenPoint && context.stage == GAME_PLACING)
board_[pos] = '\x0f';
else // 去子
board_[pos] = '\x00';
if (context.turn == PLAYER1)
context.nPiecesOnBoard_2--;
else if (context.turn == PLAYER2)
context.nPiecesOnBoard_1--;
move_ = -pos;
currentPos = 0;
context.nPiecesNeedRemove--;
updateHash(pos);
//step++;
// 去子完成
// 如果决出胜负
if (win()) {
//setTip();
return true;
}
// 还有其余的子要去吗
if (context.nPiecesNeedRemove > 0) {
// 继续去子
return true;
}
// 所有去子都完成了
else {
// 开局阶段
if (context.stage == GAME_PLACING) {
// 如果双方都无未放置的棋子
if (context.nPiecesInHand_1 == 0 && context.nPiecesInHand_2 == 0) {
// 进入中局阶段
context.stage = GAME_MOVING;
// 进入选子状态
context.action = ACTION_CHOOSE;
// 清除禁点
cleanForbiddenPoints();
// 设置轮到谁走
if (currentRule.isDefenderMoveFirst) {
context.turn = PLAYER2;
} else {
context.turn = PLAYER1;
}
// 再决胜负
if (win()) {
//setTip();
return true;
}
}
// 如果双方还有子
else {
// 进入落子状态
context.action = ACTION_PLACE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
//setTip();
return true;
}
}
}
// 中局阶段
else {
// 进入选子状态
context.action = ACTION_CHOOSE;
// 设置轮到谁走
changeTurn();
// 如果决出胜负
if (win()) {
//setTip();
return true;
}
}
}
//setTip();
return true;
}
bool NineChess::choose(int pos)
{
// 如果局面不是"中局”返回false
if (context.stage != GAME_MOVING)
return false;
// 如非“选子”或“落子”状态返回false
if (context.action != ACTION_CHOOSE && context.action != ACTION_PLACE)
return false;
char t = context.turn == PLAYER1 ? 0x10 : 0x20;
// 判断选子是否可选
if (board_[pos] & t) {
// 判断pos处的棋子是否被“闷”
if (isSurrounded(pos)) {
return false;
}
// 选子
currentPos = pos;
// 选子完成,进入落子状态
context.action = ACTION_PLACE;
return true;
}
return false;
}
uint64_t NineChess::getHash()
{
return context.hash;
}
// hash函数对应可重复去子的规则
uint64_t NineChess::updateHash(int pos)
{
/*
* hash各数据位详解名为hash但实际并无冲突是算法用到的棋局数据的完全表示
* 56-63位空白不用全为0
* 55位轮流标识0为先手1为后手
* 54位动作标识落子选子移动为01为去子
* 6-53位共48位从棋盘第一个位置点到最后一个位置点的棋子每个点用2个二进制位表示共24个位置点即48位。
* 0b00表示空白0b01表示先手棋子0b10表示后手棋子0b11表示禁点
* 4-5位共2位待去子数最大为3用2个二进制位表示即可
* 0-3位player1的手棋数不需要player2的可计算出
*/
#if 0
uint64_t hash = 0ull;
for (int i = POS_BEGIN; i < POS_END; i++) {
hash |= board_[i] & 0x30;
hash <<= 2;
}
#endif
uint64_t temp = board_[pos] & 0x30 >> 4;
context.hash |= (temp) << ((pos - 8) * 2 + 6);
if (context.turn == PLAYER2)
context.hash |= 1ull << 55;
if (context.action == ACTION_CAPTURE)
context.hash |= 1ull << 54;
context.hash |= (uint64_t)context.nPiecesNeedRemove << 4;
context.hash |= context.nPiecesInHand_1;
return context.hash;
}
bool NineChess::giveup(Player loser)
{
if (context.stage == GAME_MOVING || context.stage == GAME_PLACING) {
if (loser == PLAYER1) {
context.stage = GAME_OVER;
winner = PLAYER2;
tips = "玩家1投子认负玩家2获胜";
sprintf(cmdline, "Player1 give up!");
cmdlist.push_back(string(cmdline));
return true;
} else if (loser == PLAYER2) {
context.stage = GAME_OVER;
winner = PLAYER1;
tips = "玩家2投子认负玩家1获胜";
sprintf(cmdline, "Player2 give up!");
cmdlist.push_back(string(cmdline));
return true;
}
}
return false;
}
// 打算用个C++的命令行解析库的,简单的没必要,但中文编码有极小概率出问题
bool NineChess::command(const char *cmd)
{
int r, s, t;
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
long tm = -1;
// 设置规则
if (sscanf(cmd, "r%1u s%3u t%2u", &r, &s, &t) == 3) {
if (r <= 0 || r > N_RULES)
return false;
return setContext(&NineChess::RULES[r - 1], s, t);
}
// 选子移动
args = sscanf(cmd, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
if (args == 7) {
if (mm >= 0 && ss >= 0 && mss >= 0)
tm = mm * 60000 + ss * 1000 + mss;
}
if (choose(c1, p1))
return place(c2, p2, tm);
else
return false;
}
// 去子
args = sscanf(cmd, "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (args == 5) {
if (mm >= 0 && ss >= 0 && mss >= 0)
tm = mm * 60000 + ss * 1000 + mss;
}
return capture(c1, p1, tm);
}
// 落子
args = sscanf(cmd, "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (args == 5) {
if (mm >= 0 && ss >= 0 && mss >= 0)
tm = mm * 60000 + ss * 1000 + mss;
}
return place(c1, p1, tm);
}
// 认输
args = sscanf(cmd, "Players%1u give up!", &t);
if (args == 1) {
if (t == 1) {
return giveup(PLAYER1);
} else if (t == 2) {
return giveup(PLAYER2);
}
}
return false;
}
bool NineChess::command(int move)
{
if (move < 0) {
return capture(-move);
} else if (move & 0x1f00) {
if (choose(move >> 8))
return place(move & 0x00ff);
} else {
return place(move & 0x00ff);
}
return false;
}
inline long NineChess::update(long time_p /*= -1*/)
{
long ret = -1;
long *player_ms = (context.turn == PLAYER1 ? &elapsedMS_1 : &elapsedMS_2);
long playerNext_ms = (context.turn == PLAYER1 ? elapsedMS_2 : elapsedMS_1);
// 根据局面调整计时器
switch (context.stage) {
case NineChess::GAME_PLACING:
case NineChess::GAME_MOVING:
ftime(&currentTimeb);
// 更新时间
if (time_p >= *player_ms) {
*player_ms = ret = time_p;
long t = elapsedMS_1 + elapsedMS_2;
if (t % 1000 <= currentTimeb.millitm) {
startTimeb.time = currentTimeb.time - (t / 1000);
startTimeb.millitm = currentTimeb.millitm - (t % 1000);
} else {
startTimeb.time = currentTimeb.time - (t / 1000) - 1;
startTimeb.millitm = currentTimeb.millitm + 1000 - (t % 1000);
}
} else {
*player_ms = ret = (long)(currentTimeb.time - startTimeb.time) * 1000
+ (currentTimeb.millitm - startTimeb.millitm) - playerNext_ms;
}
// 有限时要求则判断胜负
if (currentRule.maxTimeLedToLose > 0)
win();
return ret;
case NineChess::GAME_NOTSTARTED:
return ret;
case NineChess::GAME_OVER:
return ret;
default:
return ret;
}
}
// 是否分出胜负
bool NineChess::win()
{
if (context.stage == GAME_OVER)
return true;
if (context.stage == GAME_NOTSTARTED)
return false;
// 如果有时间限定
if (currentRule.maxTimeLedToLose > 0) {
// 这里不能update更新时间否则会形成循环嵌套
// 如果玩家1超时
if (elapsedMS_1 > currentRule.maxTimeLedToLose * 60000) {
elapsedMS_1 = currentRule.maxTimeLedToLose * 60000;
winner = PLAYER2;
context.stage = GAME_OVER;
tips = "玩家1超时玩家2获胜";
sprintf(cmdline, "Time over. Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果玩家2超时
else if (elapsedMS_2 > currentRule.maxTimeLedToLose * 60000) {
elapsedMS_2 = currentRule.maxTimeLedToLose * 60000;
winner = PLAYER1;
context.stage = GAME_OVER;
tips = "玩家2超时玩家1获胜";
sprintf(cmdline, "Time over. Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
}
}
// 如果有步数限定
if (currentRule.maxStepsLedToDraw > 0) {
if (currentStep > currentRule.maxStepsLedToDraw) {
winner = DRAW;
context.stage = GAME_OVER;
sprintf(cmdline, "Steps over. In draw!");
cmdlist.push_back(string(cmdline));
return true;
}
}
// 如果玩家1子数小于赛点则玩家2获胜
if (context.nPiecesOnBoard_1 + context.nPiecesInHand_1 < currentRule.nPiecesAtLeast) {
winner = PLAYER2;
context.stage = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果玩家2子数小于赛点则玩家1获胜
else if (context.nPiecesOnBoard_2 + context.nPiecesInHand_2 < currentRule.nPiecesAtLeast) {
winner = PLAYER1;
context.stage = GAME_OVER;
sprintf(cmdline, "Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果摆满了,根据规则判断胜负
else if (context.nPiecesOnBoard_1 + context.nPiecesOnBoard_2 >= N_SEATS * N_RINGS) {
if (currentRule.isStartingPlayerLoseWhenBoardFull) {
winner = PLAYER2;
context.stage = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
} else {
winner = DRAW;
context.stage = GAME_OVER;
sprintf(cmdline, "Full. In draw!");
cmdlist.push_back(string(cmdline));
return true;
}
}
// 如果中局被“闷”
else if (context.stage == GAME_MOVING && context.action == ACTION_CHOOSE && isAllSurrounded(context.turn)) {
// 规则要求被“闷”判负,则对手获胜
if (currentRule.isLoseWhenNoWay) {
if (context.turn == PLAYER1) {
tips = "玩家1无子可走玩家2获胜";
winner = PLAYER2;
context.stage = GAME_OVER;
sprintf(cmdline, "Player1 no way to go. Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
} else {
tips = "玩家2无子可走玩家1获胜";
winner = PLAYER1;
context.stage = GAME_OVER;
sprintf(cmdline, "Player2 no way to go. Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
}
}
// 否则让棋,由对手走
else {
changeTurn();
return false;
}
}
return false;
}
int NineChess::isInMills(int pos)
{
int n = 0;
int pos1, pos2;
char m = board_[pos] & '\x30';
for (int i = 0; i < 3; i++) {
pos1 = millTable[pos][i][0];
pos2 = millTable[pos][i][1];
if (m & board_[pos1] & board_[pos2])
n++;
}
return n;
}
int NineChess::addMills(int pos)
{
// 成三用一个64位整数了规则如下
// 0x 00 00 00 00 00 00 00 00
// unused unused piece1 pos1 piece2 pos2 piece3 pos3
// piece1、piece2、piece3按照序号从小到大顺序排放
uint64_t mill = 0;
int n = 0;
int p[3], min, temp;
char m = board_[pos] & '\x30';
for (int i = 0; i < 3; i++) {
p[0] = pos;
p[1] = millTable[pos][i][0];
p[2] = millTable[pos][i][1];
// 如果成三
if (m & board_[p[1]] & board_[p[2]]) {
// 排序
for (int j = 0; j < 2; j++) {
min = j;
for (int k = j + 1; k < 3; k++) {
if (p[min] > p[k])
min = k;
}
if (min != j) {
temp = p[min];
p[min] = p[j];
p[j] = temp;
}
}
// 成三
mill = (((uint64_t)board_[p[0]]) << 40)
+ (((uint64_t)p[0]) << 32)
+ (((uint64_t)board_[p[1]]) << 24)
+ (((uint64_t)p[1]) << 16)
+ (((uint64_t)board_[p[2]]) << 8)
+ (uint64_t)p[2];
// 如果允许相同三连反复去子
if (currentRule.allowRemovePiecesRepeatedly) {
n++;
}
// 如果不允许相同三连反复去子
else {
// 迭代器
list<uint64_t>::iterator iter;
// 遍历
for (iter = context.millList.begin(); iter != context.millList.end(); iter++) {
if (mill == *iter)
break;
}
// 如果没找到历史项
if (iter == context.millList.end()) {
n++;
context.millList.push_back(mill);
}
}
}
}
return n;
}
bool NineChess::isAllInMills(char ch)
{
for (int i = POS_BEGIN; i < POS_END; i++) {
if (board_[i] & ch) {
if (!isInMills(i)) {
return false;
}
}
}
return true;
}
bool NineChess::isAllInMills(enum Player player)
{
char ch = '\x00';
if (player == PLAYER1)
ch = '\x10';
else if (player == PLAYER2)
ch = '\x20';
else
return true;
return isAllInMills(ch);
}
// 判断玩家的棋子是否被围
bool NineChess::isSurrounded(int pos)
{
// 判断pos处的棋子是否被“闷”
if ((context.turn == PLAYER1 &&
(context.nPiecesOnBoard_1 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces)) ||
(context.turn == PLAYER2 &&
(context.nPiecesOnBoard_2 > currentRule.nPiecesAtLeast || !currentRule.allowFlyWhenRemainThreePieces))) {
int i, movePos;
for (i = 0; i < 4; i++) {
movePos = moveTable[pos][i];
if (movePos && !board_[movePos])
break;
}
// 被围住
if (i == 4)
return true;
}
// 没被围住
return false;
}
bool NineChess::isAllSurrounded(char ch)
{
// 如果摆满
if (context.nPiecesOnBoard_1 + context.nPiecesOnBoard_2 >= N_SEATS * N_RINGS)
return true;
// 判断是否可以飞子
if ((context.turn == PLAYER1 &&
(context.nPiecesOnBoard_1 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces)) ||
(context.turn == PLAYER2 &&
(context.nPiecesOnBoard_2 <= currentRule.nPiecesAtLeast && currentRule.allowFlyWhenRemainThreePieces))) {
return false;
}
// 查询整个棋盘
int movePos;
for (int i = 1; i < N_SEATS * (N_RINGS + 1); i++) {
if (ch & board_[i]) {
for (int k = 0; k < 4; k++) {
movePos = moveTable[i][k];
if (movePos && !board_[movePos])
return false;
}
}
}
return true;
}
// 判断玩家的棋子是否全部被围
bool NineChess::isAllSurrounded(enum Player ply)
{
char t = '\x30';
if (ply == PLAYER1)
t &= '\x10';
else if (ply == PLAYER2)
t &= '\x20';
return isAllSurrounded(t);
}
void NineChess::cleanForbiddenPoints()
{
int pos = 0;
for (int i = 1; i <= N_RINGS; i++) {
for (int j = 0; j < N_SEATS; j++) {
pos = i * N_SEATS + j;
if (board_[pos] == '\x0f') {
board_[pos] = '\x00';
updateHash(pos);
}
}
}
}
enum NineChess::Player NineChess::changeTurn()
{
// 设置轮到谁走
context.turn = (context.turn == PLAYER1) ? PLAYER2 : PLAYER1;
return context.turn;
}
void NineChess::setTips()
{
switch (context.stage) {
case NineChess::GAME_NOTSTARTED:
tips = "轮到玩家1落子剩余" + std::to_string(context.nPiecesInHand_1) + "";
break;
case NineChess::GAME_PLACING:
if (context.action == ACTION_PLACE) {
if (context.turn == PLAYER1) {
tips = "轮到玩家1落子剩余" + std::to_string(context.nPiecesInHand_1) + "";
} else if (context.turn == PLAYER2) {
tips = "轮到玩家2落子剩余" + std::to_string(context.nPiecesInHand_2) + "";
}
} else if (context.action == ACTION_CAPTURE) {
if (context.turn == PLAYER1) {
tips = "成三轮到玩家1去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
} else if (context.turn == PLAYER2) {
tips = "成三轮到玩家2去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
}
break;
case NineChess::GAME_MOVING:
if (context.action == ACTION_PLACE || context.action == ACTION_CHOOSE) {
if (context.turn == PLAYER1) {
tips = "轮到玩家1选子移动";
} else if (context.turn == PLAYER2) {
tips = "轮到玩家2选子移动";
}
} else if (context.action == ACTION_CAPTURE) {
if (context.turn == PLAYER1) {
tips = "成三轮到玩家1去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
} else if (context.turn == PLAYER2) {
tips = "成三轮到玩家2去子需去" + std::to_string(context.nPiecesNeedRemove) + "";
}
}
break;
case NineChess::GAME_OVER:
if (winner == DRAW)
tips = "超出限定步数,双方平局";
else if (winner == PLAYER1) {
if (tips.find("无子可走") != tips.npos)
tips += "玩家1获胜";
else
tips = "玩家1获胜";
} else if (winner == PLAYER2) {
if (tips.find("无子可走") != tips.npos)
tips += "玩家2获胜";
else
tips = "玩家2获胜";
}
break;
default:
break;
}
}
enum NineChess::Player NineChess::getWhosPiece(int c, int p)
{
int pos = cp2pos(c, p);
if (board_[pos] & '\x10')
return PLAYER1;
else if (board_[pos] & '\x20')
return PLAYER2;
return NOBODY;
}
void NineChess::getElapsedTimeMS(int &p1_ms, int &p2_ms)
{
update();
p1_ms = elapsedMS_1;
p2_ms = elapsedMS_2;
}
void NineChess::mirror(bool cmdChange /*= true*/)
{
int ch;
int i, j;
for (i = 1; i <= N_RINGS; i++) {
for (j = 1; j < N_SEATS / 2; j++) {
ch = board_[i * N_SEATS + j];
board_[i * N_SEATS + j] = board_[(i + 1) * N_SEATS - j];
//updateHash(i * N_SEATS + j);
board_[(i + 1) * N_SEATS - j] = ch;
//updateHash((i + 1) * N_SEATS - j);
}
}
uint64_t llp1, llp2, llp3;
if (move_ < 0) {
i = (-move_) / N_SEATS;
j = (-move_) % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
move_ = -(i * N_SEATS + j);
} else {
llp1 = move_ >> 8;
llp2 = move_ & 0x00ff;
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
llp2 = i * N_SEATS + j;
move_ = (int16_t)((llp1 << 8) | llp2);
}
if (currentPos != 0) {
i = currentPos / N_SEATS;
j = currentPos % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
currentPos = i * N_SEATS + j;
}
if (currentRule.allowRemovePiecesRepeatedly) {
for (auto mill = context.millList.begin(); mill != context.millList.end(); mill++) {
llp1 = (*mill & 0x000000ff00000000) >> 32;
llp2 = (*mill & 0x0000000000ff0000) >> 16;
llp3 = (*mill & 0x00000000000000ff);
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
llp2 = i * N_SEATS + j;
i = (int)llp3 / N_SEATS;
j = (int)llp3 % N_SEATS;
j = (N_SEATS - j) % N_SEATS;
llp3 = i * N_SEATS + j;
*mill &= 0xffffff00ff00ff00;
*mill |= (llp1 << 32) | (llp2 << 16) | llp3;
}
}
// 命令行解析
if (cmdChange) {
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
p2 = (N_SEATS - p2 + 1) % N_SEATS;
cmdline[3] = '1' + (char)p1;
cmdline[10] = '1' + (char)p2;
} else {
args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
cmdline[4] = '1' + (char)p1;
} else {
args = sscanf(cmdline, "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
cmdline[3] = '1' + (char)p1;
}
}
}
for (auto iter = cmdlist.begin(); iter != cmdlist.end(); iter++) {
args = sscanf((*iter).c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
p2 = (N_SEATS - p2 + 1) % N_SEATS;
(*iter)[3] = '1' + (char)p1;
(*iter)[10] = '1' + (char)p2;
} else {
args = sscanf((*iter).c_str(), "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
(*iter)[4] = '1' + (char)p1;
} else {
args = sscanf((*iter).c_str(), "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (N_SEATS - p1 + 1) % N_SEATS;
(*iter)[3] = '1' + (char)p1;
}
}
}
}
}
}
void NineChess::turn(bool cmdChange /*= true*/)
{
int ch;
int i, j;
for (i = 0; i < N_SEATS; i++) {
ch = board_[N_SEATS + i];
board_[N_SEATS + i] = board_[N_SEATS * N_RINGS + i];
//updateHash(N_SEATS + i);
board_[N_SEATS * N_RINGS + i] = ch;
//updateHash(N_SEATS * N_RINGS + i);
}
uint64_t llp1, llp2, llp3;
if (move_ < 0) {
i = (-move_) / N_SEATS;
j = (-move_) % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
move_ = -(i * N_SEATS + j);
} else {
llp1 = move_ >> 8;
llp2 = move_ & 0x00ff;
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
llp2 = i * N_SEATS + j;
move_ = (int16_t)((llp1 << 8) | llp2);
}
if (currentPos != 0) {
i = currentPos / N_SEATS;
j = currentPos % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
currentPos = i * N_SEATS + j;
}
if (currentRule.allowRemovePiecesRepeatedly) {
for (auto mill = context.millList.begin(); mill != context.millList.end(); mill++) {
llp1 = (*mill & 0x000000ff00000000) >> 32;
llp2 = (*mill & 0x0000000000ff0000) >> 16;
llp3 = (*mill & 0x00000000000000ff);
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
llp2 = i * N_SEATS + j;
i = (int)llp3 / N_SEATS;
j = (int)llp3 % N_SEATS;
if (i == 1)
i = N_RINGS;
else if (i == N_RINGS)
i = 1;
llp3 = i * N_SEATS + j;
*mill &= 0xffffff00ff00ff00;
*mill |= (llp1 << 32) | (llp2 << 16) | llp3;
}
}
// 命令行解析
if (cmdChange) {
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u",
&c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
if (c2 == 1)
c2 = N_RINGS;
else if (c2 == N_RINGS)
c2 = 1;
cmdline[1] = '0' + (char)c1;
cmdline[8] = '0' + (char)c2;
} else {
args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
cmdline[2] = '0' + (char)c1;
} else {
args = sscanf(cmdline, "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
cmdline[1] = '0' + (char)c1;
}
}
}
for (auto iter = cmdlist.begin(); iter != cmdlist.end(); iter++) {
args = sscanf((*iter).c_str(),
"(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u",
&c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
if (c2 == 1)
c2 = N_RINGS;
else if (c2 == N_RINGS)
c2 = 1;
(*iter)[1] = '0' + (char)c1;
(*iter)[8] = '0' + (char)c2;
} else {
args = sscanf((*iter).c_str(), "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
(*iter)[2] = '0' + (char)c1;
} else {
args = sscanf((*iter).c_str(), "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
if (c1 == 1)
c1 = N_RINGS;
else if (c1 == N_RINGS)
c1 = 1;
(*iter)[1] = '0' + (char)c1;
}
}
}
}
}
}
void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
{
// 将degrees转化为0~359之间的数
degrees = degrees % 360;
if (degrees < 0)
degrees += 360;
if (degrees == 0 || degrees % 90)
return;
else
degrees /= 45;
int ch1, ch2;
int i, j;
if (degrees == 2) {
for (i = 1; i <= N_RINGS; i++) {
ch1 = board_[i * N_SEATS];
ch2 = board_[i * N_SEATS + 1];
for (j = 0; j < N_SEATS - 2; j++) {
board_[i * N_SEATS + j] = board_[i * N_SEATS + j + 2];
//updateHash(i * N_SEATS + j);
}
board_[i * N_SEATS + 6] = ch1;
//updateHash(i * N_SEATS + 6);
board_[i * N_SEATS + 7] = ch2;
//updateHash(i * N_SEATS + 7);
}
} else if (degrees == 6) {
for (i = 1; i <= N_RINGS; i++) {
ch1 = board_[i * N_SEATS + 7];
ch2 = board_[i * N_SEATS + 6];
for (j = N_SEATS - 1; j >= 2; j--) {
board_[i * N_SEATS + j] = board_[i * N_SEATS + j - 2];
//updateHash(i * N_SEATS + j);
}
board_[i * N_SEATS + 1] = ch1;
//updateHash(i * N_SEATS + 1);
board_[i * N_SEATS] = ch2;
//updateHash(i * N_SEATS);
}
} else if (degrees == 4) {
for (i = 1; i <= N_RINGS; i++) {
for (j = 0; j < N_SEATS / 2; j++) {
ch1 = board_[i * N_SEATS + j];
board_[i * N_SEATS + j] = board_[i * N_SEATS + j + 4];
//updateHash(i * N_SEATS + j);
board_[i * N_SEATS + j + 4] = ch1;
//updateHash(i * N_SEATS + j + 4);
}
}
} else
return;
uint64_t llp1, llp2, llp3;
if (move_ < 0) {
i = (-move_) / N_SEATS;
j = (-move_) % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
move_ = -(i * N_SEATS + j);
} else {
llp1 = move_ >> 8;
llp2 = move_ & 0x00ff;
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
llp2 = i * N_SEATS + j;
move_ = (int16_t)((llp1 << 8) | llp2);
}
if (currentPos != 0) {
i = currentPos / N_SEATS;
j = currentPos % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
currentPos = i * N_SEATS + j;
}
if (currentRule.allowRemovePiecesRepeatedly) {
for (auto mill = context.millList.begin(); mill != context.millList.end(); mill++) {
llp1 = (*mill & 0x000000ff00000000) >> 32;
llp2 = (*mill & 0x0000000000ff0000) >> 16;
llp3 = (*mill & 0x00000000000000ff);
i = (int)llp1 / N_SEATS;
j = (int)llp1 % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
llp1 = i * N_SEATS + j;
i = (int)llp2 / N_SEATS;
j = (int)llp2 % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
llp2 = i * N_SEATS + j;
i = (int)llp3 / N_SEATS;
j = (int)llp3 % N_SEATS;
j = (j + N_SEATS - degrees) % N_SEATS;
llp3 = i * N_SEATS + j;
*mill &= 0xffffff00ff00ff00;
*mill |= (llp1 << 32) | (llp2 << 16) | llp3;
}
}
// 命令行解析
if (cmdChange) {
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
p2 = (p2 - 1 + N_SEATS - degrees) % N_SEATS;
cmdline[3] = '1' + (char)p1;
cmdline[10] = '1' + (char)p2;
} else {
args = sscanf(cmdline, "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
cmdline[4] = '1' + (char)p1;
} else {
args = sscanf(cmdline, "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
cmdline[3] = '1' + (char)p1;
}
}
}
for (auto iter = cmdlist.begin(); iter != cmdlist.end(); iter++) {
args = sscanf((*iter).c_str(), "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
p2 = (p2 - 1 + N_SEATS - degrees) % N_SEATS;
(*iter)[3] = '1' + (char)p1;
(*iter)[10] = '1' + (char)p2;
} else {
args = sscanf((*iter).c_str(), "-(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
(*iter)[4] = '1' + (char)p1;
} else {
args = sscanf((*iter).c_str(), "(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &mm, &ss, &mss);
if (args >= 2) {
p1 = (p1 - 1 + N_SEATS - degrees) % N_SEATS;
(*iter)[3] = '1' + (char)p1;
}
}
}
}
}
}