refactor
This commit is contained in:
parent
08ba141658
commit
961250d466
|
@ -4,10 +4,13 @@
|
|||
AiThread::AiThread(int id, QObject *parent) : QThread(parent), waiting_(false), aiDepth(8), aiTime(10)
|
||||
{
|
||||
this->id = id;
|
||||
|
||||
// 连接定时器启动,减去118毫秒的返回时间
|
||||
connect(this, &AiThread::calcStarted, this, [=]() {timer.start(aiTime * 1000 - 118); }, Qt::QueuedConnection);
|
||||
|
||||
// 连接定时器停止
|
||||
connect(this, &AiThread::calcFinished, this, [=]() {timer.stop(); }, Qt::QueuedConnection);
|
||||
|
||||
// 连接定时器处理函数
|
||||
connect(&timer, &QTimer::timeout, this, &AiThread::act, Qt::QueuedConnection);
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ public:
|
|||
signals:
|
||||
// 招法信号
|
||||
void command(const QString &cmdline, bool update = true);
|
||||
|
||||
// 开始计算的信号
|
||||
void calcStarted();
|
||||
|
||||
// 计算结束的信号
|
||||
void calcFinished();
|
||||
|
||||
|
@ -31,6 +33,7 @@ public:
|
|||
// AI设置
|
||||
void setAi(const NineChess &chess);
|
||||
void setAi(const NineChess &chess, int depth, int time);
|
||||
|
||||
// 深度和限时
|
||||
void getDepthTime(int &depth, int &time)
|
||||
{
|
||||
|
@ -41,31 +44,41 @@ public:
|
|||
public slots:
|
||||
// 强制出招,不退出线程
|
||||
void act();
|
||||
|
||||
// 线程暂停
|
||||
void pause();
|
||||
|
||||
// 线程继续
|
||||
void resume();
|
||||
|
||||
// 退出线程
|
||||
void stop();
|
||||
|
||||
private:
|
||||
// 玩家ID
|
||||
int id;
|
||||
|
||||
// 互斥锁
|
||||
QMutex mutex;
|
||||
|
||||
// 线程等待标识,这里没用到,留着以后扩展用
|
||||
bool waiting_;
|
||||
|
||||
// 等待条件,这里没用到,留着以后扩展用
|
||||
QWaitCondition pauseCondition;
|
||||
|
||||
// 主线程棋对象的引用
|
||||
const NineChess *chess_;
|
||||
|
||||
// Alpha-Beta剪枝算法类
|
||||
NineChessAi_ab ai_ab;
|
||||
|
||||
// AI的层数
|
||||
int aiDepth;
|
||||
|
||||
// AI的限时
|
||||
int aiTime;
|
||||
|
||||
// 定时器
|
||||
QTimer timer;
|
||||
};
|
||||
|
|
|
@ -65,19 +65,24 @@ void BoardItem::paint(QPainter *painter,
|
|||
Q_UNUSED(option)
|
||||
Q_UNUSED(widget)
|
||||
|
||||
// 填充阴影
|
||||
painter->fillRect(boundingRect(), QBrush(QColor(64, 64, 64)));
|
||||
// 填充阴影
|
||||
painter->fillRect(boundingRect(), QBrush(QColor(64, 64, 64)));
|
||||
|
||||
// 填充图片
|
||||
painter->drawPixmap(-size / 2, -size / 2, size, size, QPixmap(":/image/resources/image/board.png"));
|
||||
|
||||
// 黑色实线画笔
|
||||
QPen pen(QBrush(Qt::black), LINE_WEIGHT, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin);
|
||||
painter->setPen(pen);
|
||||
|
||||
// 空画刷
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
|
||||
for (int i = 0; i < RING; i++) {
|
||||
// 画3个方框
|
||||
painter->drawPolygon(position + i * SEAT, SEAT);
|
||||
}
|
||||
|
||||
// 画4条纵横线
|
||||
painter->drawLine(position[0], position[(RING - 1) * SEAT]);
|
||||
painter->drawLine(position[2], position[(RING - 1) * SEAT + 2]);
|
||||
|
@ -96,6 +101,7 @@ QPointF BoardItem::nearestPosition(QPointF const pos)
|
|||
{
|
||||
// 初始最近点设为(0,0)点
|
||||
QPointF nearestPos = QPointF(0, 0);
|
||||
|
||||
// 寻找最近的落子点
|
||||
for (int i = 0; i < RING * SEAT; i++) {
|
||||
// 如果鼠标点距离落子点在棋子半径内
|
||||
|
@ -123,5 +129,6 @@ bool BoardItem::pos2cp(QPointF pos, int &c, int &p)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -18,31 +18,40 @@ public:
|
|||
{
|
||||
Type = UserType + 1
|
||||
};
|
||||
|
||||
int type() const
|
||||
{
|
||||
return Type;
|
||||
}
|
||||
|
||||
// 设置有无斜线
|
||||
void setDiagonal(bool arg = true);
|
||||
|
||||
// 返回最近的落子点
|
||||
QPointF nearestPosition(QPointF const pos);
|
||||
|
||||
// 将模型的圈、位转化为落子点坐标
|
||||
QPointF cp2pos(int c, int p);
|
||||
|
||||
// 将落子点坐标转化为模型用的圈、位
|
||||
bool pos2cp(QPointF pos, int &c, int &p);
|
||||
|
||||
// 3圈,禁止修改!
|
||||
static const int RING = 3;
|
||||
|
||||
// 8位,禁止修改!
|
||||
static const int SEAT = 8;
|
||||
|
||||
private:
|
||||
// 棋盘尺寸
|
||||
qreal size;
|
||||
|
||||
// 影子尺寸
|
||||
qreal sizeShadow;
|
||||
|
||||
// 24个落子点
|
||||
QPointF position[RING * SEAT];
|
||||
|
||||
// 是否有斜线
|
||||
bool hasObliqueLine;
|
||||
};
|
||||
|
|
|
@ -57,6 +57,7 @@ GameController::~GameController()
|
|||
// 停止计时器
|
||||
if (timeID != 0)
|
||||
killTimer(timeID);
|
||||
|
||||
// 停掉线程
|
||||
ai1.stop();
|
||||
ai2.stop();
|
||||
|
@ -69,11 +70,11 @@ const QMap<int, QStringList> GameController::getActions()
|
|||
// 主窗口更新菜单栏
|
||||
// 之所以不用信号和槽的模式,是因为发信号的时候槽还来不及关联
|
||||
QMap<int, QStringList> actions;
|
||||
for (int i = 0; i < NineChess::RULENUM; i++) {
|
||||
for (int i = 0; i < NineChess::N_RULES; i++) {
|
||||
// QMap的key存放int索引值,value存放规则名称和规则提示
|
||||
QStringList strlist;
|
||||
strlist.append(tr(NineChess::RULES[i].name));
|
||||
strlist.append(tr(NineChess::RULES[i].info));
|
||||
strlist.append(tr(NineChess::RULES[i].description));
|
||||
actions.insert(i, strlist);
|
||||
}
|
||||
return actions;
|
||||
|
@ -83,6 +84,7 @@ void GameController::gameStart()
|
|||
{
|
||||
chess_.start();
|
||||
chessTemp = chess_;
|
||||
|
||||
// 每隔100毫秒调用一次定时器处理函数
|
||||
if (timeID == 0) {
|
||||
timeID = startTimer(100);
|
||||
|
@ -94,8 +96,10 @@ void GameController::gameReset()
|
|||
// 停止计时器
|
||||
if (timeID != 0)
|
||||
killTimer(timeID);
|
||||
|
||||
// 定时器ID为0
|
||||
timeID = 0;
|
||||
|
||||
// 重置游戏
|
||||
chess_.reset();
|
||||
chessTemp = chess_;
|
||||
|
@ -110,8 +114,9 @@ void GameController::gameReset()
|
|||
qDeleteAll(pieceList);
|
||||
pieceList.clear();
|
||||
currentPiece = nullptr;
|
||||
|
||||
// 重新绘制棋盘
|
||||
scene.setDiagonal(chess_.getRule()->hasObliqueLine);
|
||||
scene.setDiagonal(chess_.getRule()->hasObliqueLines);
|
||||
|
||||
// 绘制所有棋子,放在起始位置
|
||||
// 0: 先手第1子; 1:后手第1子
|
||||
|
@ -119,16 +124,19 @@ void GameController::gameReset()
|
|||
// ......
|
||||
PieceItem::Models md;
|
||||
PieceItem *newP;
|
||||
for (int i = 0; i < chess_.getRule()->numOfChess; i++) {
|
||||
|
||||
for (int i = 0; i < chess_.getRule()->nTotalPiecesEachSide; i++) {
|
||||
// 先手的棋子
|
||||
md = isInverted ? PieceItem::whitePiece : PieceItem::blackPiece;
|
||||
newP = new PieceItem;
|
||||
newP->setModel(md);
|
||||
newP->setPos(scene.pos_p1);
|
||||
newP->setNum(i + 1);
|
||||
|
||||
// 如果重复三连不可用,则显示棋子序号,九连棋专用玩法
|
||||
if (!(chess_.getRule()->canRepeated))
|
||||
if (!(chess_.getRule()->allowRemovePiecesRepeatedly))
|
||||
newP->setShowNum(true);
|
||||
|
||||
pieceList.append(newP);
|
||||
scene.addItem(newP);
|
||||
|
||||
|
@ -138,15 +146,18 @@ void GameController::gameReset()
|
|||
newP->setModel(md);
|
||||
newP->setPos(scene.pos_p2);
|
||||
newP->setNum(i + 1);
|
||||
|
||||
// 如果重复三连不可用,则显示棋子序号,九连棋专用玩法
|
||||
if (!(chess_.getRule()->canRepeated))
|
||||
if (!(chess_.getRule()->allowRemovePiecesRepeatedly))
|
||||
newP->setShowNum(true);
|
||||
|
||||
pieceList.append(newP);
|
||||
scene.addItem(newP);
|
||||
}
|
||||
|
||||
// 读取规则限时要求
|
||||
timeLimit = chess_.getRule()->maxTime;
|
||||
timeLimit = chess_.getRule()->maxTimeLedToLose;
|
||||
|
||||
// 如果规则不要求计时,则time1和time2表示已用时间
|
||||
if (timeLimit <= 0) {
|
||||
// 将玩家的已用时间清零
|
||||
|
@ -155,18 +166,22 @@ void GameController::gameReset()
|
|||
// 将玩家的剩余时间置为限定时间
|
||||
remainingTime1 = remainingTime2 = timeLimit * 60000;
|
||||
}
|
||||
|
||||
// 更新棋谱
|
||||
manualListModel.removeRows(0, manualListModel.rowCount());
|
||||
manualListModel.insertRow(0);
|
||||
manualListModel.setData(manualListModel.index(0), chess_.getCmdLine());
|
||||
currentRow = 0;
|
||||
|
||||
// 发出信号通知主窗口更新LCD显示
|
||||
QTime qtime = QTime(0, 0, 0, 0).addMSecs(remainingTime1);
|
||||
emit time1Changed(qtime.toString("mm:ss.zzz"));
|
||||
emit time2Changed(qtime.toString("mm:ss.zzz"));
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(chess_.getTip());
|
||||
message = QString::fromStdString(chess_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 播放音效
|
||||
playSound(":/sound/resources/sound/newgame.wav");
|
||||
}
|
||||
|
@ -179,15 +194,18 @@ void GameController::setEditing(bool arg)
|
|||
void GameController::setInvert(bool arg)
|
||||
{
|
||||
isInverted = arg;
|
||||
|
||||
// 遍历所有棋子
|
||||
for (PieceItem *p : pieceList) {
|
||||
if (p) {
|
||||
// 黑子变白
|
||||
if (p->getModel() == PieceItem::blackPiece)
|
||||
p->setModel(PieceItem::whitePiece);
|
||||
|
||||
// 白子变黑
|
||||
else if (p->getModel() == PieceItem::whitePiece)
|
||||
p->setModel(PieceItem::blackPiece);
|
||||
|
||||
// 刷新棋子显示
|
||||
p->update();
|
||||
}
|
||||
|
@ -197,7 +215,7 @@ void GameController::setInvert(bool arg)
|
|||
void GameController::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimited /*= -1*/)
|
||||
{
|
||||
// 更新规则,原限时和限步不变
|
||||
if (ruleNo < 0 || ruleNo >= NineChess::RULENUM)
|
||||
if (ruleNo < 0 || ruleNo >= NineChess::N_RULES)
|
||||
return;
|
||||
this->ruleNo_ = ruleNo;
|
||||
|
||||
|
@ -206,7 +224,7 @@ void GameController::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimit
|
|||
timeLimit = timeLimited;
|
||||
}
|
||||
// 设置模型规则,重置游戏
|
||||
chess_.setData(&NineChess::RULES[ruleNo], stepsLimit, timeLimit);
|
||||
chess_.setContext(&NineChess::RULES[ruleNo], stepsLimit, timeLimit);
|
||||
chessTemp = chess_;
|
||||
|
||||
// 重置游戏
|
||||
|
@ -272,6 +290,7 @@ void GameController::getAiDepthTime(int &depth1, int &time1, int &depth2, int &t
|
|||
void GameController::setAnimation(bool arg)
|
||||
{
|
||||
hasAnimation = arg;
|
||||
|
||||
// 默认动画时间250ms
|
||||
if (hasAnimation)
|
||||
durationTime = 250;
|
||||
|
@ -306,11 +325,13 @@ void GameController::flip()
|
|||
chess_.mirror();
|
||||
chess_.rotate(180);
|
||||
chessTemp = chess_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (auto str : *(chess_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
// 刷新显示
|
||||
if (currentRow == row - 1)
|
||||
updateScence();
|
||||
|
@ -319,6 +340,7 @@ void GameController::flip()
|
|||
|
||||
ai1.setAi(chess_);
|
||||
ai2.setAi(chess_);
|
||||
|
||||
if (isEngine1) {
|
||||
ai1.start();
|
||||
}
|
||||
|
@ -341,12 +363,15 @@ void GameController::mirror()
|
|||
|
||||
chess_.mirror();
|
||||
chessTemp = chess_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (auto str : *(chess_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
qDebug() << "list: " << row;
|
||||
|
||||
// 刷新显示
|
||||
if (currentRow == row - 1)
|
||||
updateScence();
|
||||
|
@ -377,11 +402,13 @@ void GameController::turnRight()
|
|||
|
||||
chess_.rotate(-90);
|
||||
chessTemp = chess_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (auto str : *(chess_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
// 刷新显示
|
||||
if (currentRow == row - 1)
|
||||
updateScence();
|
||||
|
@ -412,11 +439,13 @@ void GameController::turnLeft()
|
|||
|
||||
chess_.rotate(90);
|
||||
chessTemp = chess_;
|
||||
|
||||
// 更新棋谱
|
||||
int row = 0;
|
||||
for (auto str : *(chess_.getCmdList())) {
|
||||
manualListModel.setData(manualListModel.index(row++), str.c_str());
|
||||
}
|
||||
|
||||
// 刷新显示
|
||||
updateScence();
|
||||
|
||||
|
@ -433,34 +462,41 @@ void GameController::turnLeft()
|
|||
void GameController::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
Q_UNUSED(event)
|
||||
static QTime qt1, qt2;
|
||||
static QTime qt1, qt2;
|
||||
|
||||
// 玩家的已用时间
|
||||
chess_.getPlayer_TimeMS(remainingTime1, remainingTime2);
|
||||
chess_.getElapsedTimeMS(remainingTime1, remainingTime2);
|
||||
|
||||
// 如果规则要求计时,则time1和time2表示倒计时
|
||||
if (timeLimit > 0) {
|
||||
// 玩家的剩余时间
|
||||
remainingTime1 = timeLimit * 60000 - remainingTime1;
|
||||
remainingTime2 = timeLimit * 60000 - remainingTime2;
|
||||
}
|
||||
|
||||
qt1 = QTime(0, 0, 0, 0).addMSecs(remainingTime1);
|
||||
qt2 = QTime(0, 0, 0, 0).addMSecs(remainingTime2);
|
||||
emit time1Changed(qt1.toString("mm:ss.zzz"));
|
||||
emit time2Changed(qt2.toString("mm:ss.zzz"));
|
||||
|
||||
// 如果胜负已分
|
||||
if (chess_.whoWin() != NineChess::NOBODY) {
|
||||
// 停止计时
|
||||
killTimer(timeID);
|
||||
|
||||
// 定时器ID为0
|
||||
timeID = 0;
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(chess_.getTip());
|
||||
message = QString::fromStdString(chess_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 播放音效
|
||||
playSound(":/sound/resources/sound/win.wav");
|
||||
}
|
||||
|
||||
// 测试用代码
|
||||
/*
|
||||
#if 0
|
||||
int ti = time.elapsed();
|
||||
static QTime t;
|
||||
if (ti < 0)
|
||||
|
@ -481,7 +517,7 @@ void GameController::timerEvent(QTimerEvent *event)
|
|||
//qDebug() << t;
|
||||
emit time2Changed(t.toString("hh:mm:ss"));
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
}
|
||||
|
||||
// 关键槽函数,根据QGraphicsScene的信号和状态来执行选子、落子或去子
|
||||
|
@ -496,6 +532,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
// 电脑走棋时,点击无效
|
||||
if (chess_.whosTurn() == NineChess::PLAYER1 && isEngine1)
|
||||
return false;
|
||||
|
||||
if (chess_.whosTurn() == NineChess::PLAYER2 && isEngine2)
|
||||
return false;
|
||||
|
||||
|
@ -515,12 +552,14 @@ bool GameController::actionPiece(QPointF pos)
|
|||
if (QMessageBox::Ok == msgBox.exec()) {
|
||||
chess_ = chessTemp;
|
||||
manualListModel.removeRows(currentRow + 1, manualListModel.rowCount() - currentRow - 1);
|
||||
|
||||
// 如果再决出胜负后悔棋,则重新启动计时
|
||||
if (chess_.whoWin() == NineChess::NOBODY) {
|
||||
// 重新启动计时
|
||||
timeID = startTimer(100);
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(chess_.getTip());
|
||||
message = QString::fromStdString(chess_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
}
|
||||
} else
|
||||
|
@ -528,7 +567,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
}
|
||||
|
||||
// 如果未开局则开局
|
||||
if (chess_.getPhase() == NineChess::GAME_NOTSTARTED)
|
||||
if (chess_.getStage() == NineChess::GAME_NOTSTARTED)
|
||||
gameStart();
|
||||
|
||||
// 判断执行选子、落子或去子
|
||||
|
@ -549,6 +588,7 @@ bool GameController::actionPiece(QPointF pos)
|
|||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果移子不成功,尝试重新选子,这里不break
|
||||
|
||||
case NineChess::ACTION_CHOOSE:
|
||||
|
@ -583,12 +623,13 @@ bool GameController::actionPiece(QPointF pos)
|
|||
|
||||
if (result) {
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(chess_.getTip());
|
||||
message = QString::fromStdString(chess_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 将新增的棋谱行插入到ListModel
|
||||
currentRow = manualListModel.rowCount() - 1;
|
||||
int k = 0;
|
||||
|
||||
// 输出命令行
|
||||
for (auto i = (chess_.getCmdList())->begin(); i != (chess_.getCmdList())->end(); ++i) {
|
||||
// 跳过已添加的,因标准list容器没有下标
|
||||
|
@ -684,7 +725,7 @@ bool GameController::command(const QString &cmd, bool update /*= true*/)
|
|||
}
|
||||
|
||||
// 如果未开局则开局
|
||||
if (chess_.getPhase() == NineChess::GAME_NOTSTARTED) {
|
||||
if (chess_.getStage() == NineChess::GAME_NOTSTARTED) {
|
||||
gameStart();
|
||||
}
|
||||
|
||||
|
@ -701,7 +742,7 @@ bool GameController::command(const QString &cmd, bool update /*= true*/)
|
|||
}
|
||||
|
||||
// 发信号更新状态栏
|
||||
message = QString::fromStdString(chess_.getTip());
|
||||
message = QString::fromStdString(chess_.getTips());
|
||||
emit statusBarChanged(message);
|
||||
|
||||
// 对于新开局
|
||||
|
@ -771,12 +812,15 @@ bool GameController::phaseChange(int row, bool forceUpdate)
|
|||
int rows = manualListModel.rowCount();
|
||||
QStringList mlist = manualListModel.stringList();
|
||||
qDebug() << "rows:" << rows << " current:" << row;
|
||||
|
||||
for (int i = 0; i <= row; i++) {
|
||||
qDebug() << mlist.at(i);
|
||||
chessTemp.command(mlist.at(i).toStdString().c_str());
|
||||
}
|
||||
|
||||
// 下面这步关键,会让悔棋者承担时间损失
|
||||
chessTemp.setStartTimeb(chess_.getStartTimeb());
|
||||
|
||||
// 刷新棋局场景
|
||||
updateScence(chessTemp);
|
||||
return true;
|
||||
|
@ -791,28 +835,36 @@ bool GameController::updateScence(NineChess &chess)
|
|||
{
|
||||
const int *board = chess.getBoard();
|
||||
QPointF pos;
|
||||
|
||||
// chess类中的棋子代码
|
||||
int key;
|
||||
|
||||
// 棋子总数
|
||||
int n = chess.getRule()->numOfChess * 2;
|
||||
int n = chess.getRule()->nTotalPiecesEachSide * 2;
|
||||
|
||||
// 动画组
|
||||
QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup;
|
||||
|
||||
// 棋子就位
|
||||
PieceItem *piece = nullptr;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
piece = pieceList.at(i);
|
||||
|
||||
// 将pieceList的下标转换为chess的棋子代号
|
||||
key = (i % 2) ? (i / 2 + 0x21) : (i / 2 + 0x11);
|
||||
|
||||
int j;
|
||||
|
||||
// 遍历棋盘,查找并放置棋盘上的棋子
|
||||
for (j = NineChess::SEAT; j < (NineChess::SEAT) * (NineChess::RING + 1); j++) {
|
||||
for (j = NineChess::N_SEATS; j < (NineChess::N_SEATS) * (NineChess::N_RINGS + 1); j++) {
|
||||
if (board[j] == key) {
|
||||
pos = scene.cp2pos(j / NineChess::SEAT, j % NineChess::SEAT + 1);
|
||||
pos = scene.cp2pos(j / NineChess::N_SEATS, j % NineChess::N_SEATS + 1);
|
||||
if (piece->pos() != pos) {
|
||||
|
||||
// 让移动的棋子位于顶层
|
||||
piece->setZValue(1);
|
||||
|
||||
// 棋子移动动画
|
||||
QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos");
|
||||
animation->setDuration(durationTime);
|
||||
|
@ -829,12 +881,12 @@ bool GameController::updateScence(NineChess &chess)
|
|||
}
|
||||
|
||||
// 如果没有找到,放置棋盘外的棋子
|
||||
if (j == (NineChess::SEAT) * (NineChess::RING + 1)) {
|
||||
if (j == (NineChess::N_SEATS) * (NineChess::N_RINGS + 1)) {
|
||||
// 判断是被吃掉的子,还是未安放的子
|
||||
if (key & 0x10) {
|
||||
pos = (key - 0x11 < n / 2 - chess.getPlayer1_InHand()) ? scene.pos_p2_g : scene.pos_p1;
|
||||
pos = (key - 0x11 < n / 2 - chess.getPiecesInHandCount_1()) ? scene.pos_p2_g : scene.pos_p1;
|
||||
} else
|
||||
pos = (key - 0x21 < n / 2 - chess.getPlayer2_InHand()) ? scene.pos_p1_g : scene.pos_p2;
|
||||
pos = (key - 0x21 < n / 2 - chess.getPiecesInHandCount_2()) ? scene.pos_p1_g : scene.pos_p2;
|
||||
|
||||
if (piece->pos() != pos) {
|
||||
QPropertyAnimation *animation = new QPropertyAnimation(piece, "pos");
|
||||
|
@ -849,10 +901,10 @@ bool GameController::updateScence(NineChess &chess)
|
|||
}
|
||||
|
||||
// 添加开局禁子点
|
||||
if (chess.getRule()->hasForbidden && chess.getPhase() == NineChess::GAME_OPENING) {
|
||||
for (int j = NineChess::SEAT; j < (NineChess::SEAT) * (NineChess::RING + 1); j++) {
|
||||
if (chess.getRule()->hasForbiddenPoint && chess.getStage() == NineChess::GAME_PLACING) {
|
||||
for (int j = NineChess::N_SEATS; j < (NineChess::N_SEATS) * (NineChess::N_RINGS + 1); j++) {
|
||||
if (board[j] == 0x0F) {
|
||||
pos = scene.cp2pos(j / NineChess::SEAT, j % NineChess::SEAT + 1);
|
||||
pos = scene.cp2pos(j / NineChess::N_SEATS, j % NineChess::N_SEATS + 1);
|
||||
if (n < pieceList.size()) {
|
||||
pieceList.at(n++)->setPos(pos);
|
||||
} else {
|
||||
|
@ -868,7 +920,7 @@ bool GameController::updateScence(NineChess &chess)
|
|||
}
|
||||
|
||||
// 中局清除禁子点
|
||||
if (chess.getRule()->hasForbidden && chess.getPhase() != NineChess::GAME_OPENING) {
|
||||
if (chess.getRule()->hasForbiddenPoint && chess.getStage() != NineChess::GAME_PLACING) {
|
||||
while (n < pieceList.size()) {
|
||||
delete pieceList.at(n);
|
||||
pieceList.removeAt(n);
|
||||
|
|
|
@ -27,85 +27,115 @@ class GameController : public QObject
|
|||
public:
|
||||
GameController(GameScene &scene, QObject *parent = nullptr);
|
||||
~GameController();
|
||||
|
||||
//主窗口菜单栏明细
|
||||
const QMap <int, QStringList> getActions();
|
||||
|
||||
int getRuleNo()
|
||||
{
|
||||
return ruleNo_;
|
||||
}
|
||||
|
||||
int getTimeLimit()
|
||||
{
|
||||
return timeLimit;
|
||||
}
|
||||
|
||||
int getStepsLimit()
|
||||
{
|
||||
return stepsLimit;
|
||||
}
|
||||
|
||||
bool isAnimation()
|
||||
{
|
||||
return hasAnimation;
|
||||
}
|
||||
|
||||
void setDurationTime(int i)
|
||||
{
|
||||
durationTime = i;
|
||||
}
|
||||
|
||||
int getDurationTime()
|
||||
{
|
||||
return durationTime;
|
||||
}
|
||||
|
||||
QStringListModel *getManualListModel()
|
||||
{
|
||||
return &manualListModel;
|
||||
}
|
||||
|
||||
void setAiDepthTime(int depth1, int time1, int depth2, int time2);
|
||||
void getAiDepthTime(int &depth1, int &time1, int &depth2, int &time2);
|
||||
|
||||
signals:
|
||||
|
||||
// 玩家1(先手)用时改变的信号
|
||||
void time1Changed(const QString &time);
|
||||
|
||||
// 玩家2(后手)用时改变的信号
|
||||
void time2Changed(const QString &time);
|
||||
|
||||
// 通知主窗口更新状态栏的信号
|
||||
void statusBarChanged(const QString &message);
|
||||
|
||||
public slots:
|
||||
|
||||
// 设置规则
|
||||
void setRule(int ruleNo, int stepLimited = -1, int timeLimited = -1);
|
||||
|
||||
// 游戏开始
|
||||
void gameStart();
|
||||
|
||||
// 游戏重置
|
||||
void gameReset();
|
||||
|
||||
// 设置编辑棋局状态
|
||||
void setEditing(bool arg = true);
|
||||
|
||||
// 设置黑白反转状态
|
||||
void setInvert(bool arg = true);
|
||||
|
||||
// 让电脑执先手
|
||||
void setEngine1(bool arg = true);
|
||||
|
||||
// 让电脑执后手
|
||||
void setEngine2(bool arg = true);
|
||||
|
||||
// 是否有落子动画
|
||||
void setAnimation(bool arg = true);
|
||||
|
||||
// 是否有落子音效
|
||||
void setSound(bool arg = true);
|
||||
|
||||
// 播放声音
|
||||
void playSound(const QString &soundPath);
|
||||
|
||||
// 上下翻转
|
||||
void flip();
|
||||
|
||||
// 左右镜像
|
||||
void mirror();
|
||||
|
||||
// 视图须时针旋转90°
|
||||
void turnRight();
|
||||
|
||||
// 视图逆时针旋转90°
|
||||
void turnLeft();
|
||||
|
||||
// 根据QGraphicsScene的信号和状态来执行选子、落子或去子
|
||||
bool actionPiece(QPointF p);
|
||||
|
||||
// 认输
|
||||
bool giveUp();
|
||||
|
||||
// 棋谱的命令行执行
|
||||
bool command(const QString &cmd, bool update = true);
|
||||
|
||||
// 历史局面及局面改变
|
||||
bool phaseChange(int row, bool forceUpdate = false);
|
||||
|
||||
// 更新棋局显示,每步后执行才能刷新局面
|
||||
bool updateScence();
|
||||
bool updateScence(NineChess &chess);
|
||||
|
@ -118,48 +148,70 @@ protected:
|
|||
private:
|
||||
// 棋对象的数据模型
|
||||
NineChess chess_;
|
||||
|
||||
// 棋对象的数据模型(临时)
|
||||
NineChess chessTemp;
|
||||
|
||||
// 2个AI的线程
|
||||
AiThread ai1, ai2;
|
||||
|
||||
// 棋局的场景类
|
||||
GameScene &scene;
|
||||
|
||||
// 所有棋子
|
||||
QList<PieceItem *> pieceList;
|
||||
|
||||
// 当前棋子
|
||||
PieceItem *currentPiece;
|
||||
|
||||
// 当前浏览的棋谱行
|
||||
int currentRow;
|
||||
|
||||
// 玩家1手棋数、玩家2手棋数、待去棋数,没有用到,注释掉
|
||||
//int player1_InHand, player2_InHand, num_NeedRemove;
|
||||
|
||||
// 是否处于“编辑棋局”状态
|
||||
bool isEditing;
|
||||
|
||||
// 是否黑白反转
|
||||
bool isInverted;
|
||||
|
||||
// 是否电脑执先手
|
||||
bool isEngine1;
|
||||
|
||||
// 是否电脑执后手
|
||||
bool isEngine2;
|
||||
|
||||
// 是否有落子动画
|
||||
bool hasAnimation;
|
||||
|
||||
// 动画持续时间
|
||||
int durationTime;
|
||||
|
||||
// 是否有落子音效
|
||||
bool hasSound;
|
||||
|
||||
// 定时器ID
|
||||
int timeID;
|
||||
|
||||
// 规则号
|
||||
int ruleNo_;
|
||||
|
||||
// 规则限时(分钟)
|
||||
int timeLimit;
|
||||
|
||||
// 规则限步数
|
||||
int stepsLimit;
|
||||
|
||||
// 玩家1剩余时间(毫秒)
|
||||
int remainingTime1;
|
||||
|
||||
// 玩家2剩余时间(毫秒)
|
||||
int remainingTime2;
|
||||
|
||||
// 用于主窗口状态栏显示的字符串
|
||||
QString message;
|
||||
|
||||
// 棋谱字符串列表模型
|
||||
QStringListModel manualListModel;
|
||||
};
|
||||
|
|
|
@ -81,7 +81,7 @@ void GameScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
|
|||
if (p != QPointF(0, 0))
|
||||
// 发送鼠标点最近的落子点
|
||||
emit mouseReleased(p);
|
||||
}// 如果是棋子
|
||||
} // 如果是棋子
|
||||
else if (item->type() == PieceItem::Type) {
|
||||
// 将当前棋子在场景中的位置发送出去
|
||||
emit mouseReleased(item->scenePos());
|
||||
|
|
|
@ -11,14 +11,19 @@ class GameScene : public QGraphicsScene
|
|||
public:
|
||||
explicit GameScene(QObject *parent = nullptr);
|
||||
~GameScene();
|
||||
|
||||
// 将模型的圈、位转化为落子点坐标
|
||||
QPointF cp2pos(int c, int p);
|
||||
|
||||
// 将落子点坐标转化为模型用的圈、位
|
||||
bool pos2cp(QPointF pos, int &c, int &p);
|
||||
|
||||
// 设置棋盘斜线
|
||||
void setDiagonal(bool arg = true);
|
||||
|
||||
// 玩家1的己方棋盒及对方棋盒位置
|
||||
const QPointF pos_p1, pos_p1_g;
|
||||
|
||||
// 玩家2的己方棋盒及对方棋盒位置
|
||||
const QPointF pos_p2, pos_p2_g;
|
||||
|
||||
|
|
|
@ -26,8 +26,10 @@ public:
|
|||
QSize sizeHint() const
|
||||
{
|
||||
QSize size = QListView::sizeHint();
|
||||
|
||||
// 缺省宽度设为128,这样就不太宽了
|
||||
size.setWidth(128);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -70,6 +72,7 @@ protected slots:
|
|||
{
|
||||
// 调用父类默认函数
|
||||
QListView::dataChanged(topLeft, bottomRight, roles);
|
||||
|
||||
// 如果包含model
|
||||
if (model()) {
|
||||
// 判断
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -22,108 +22,134 @@ class NineChess
|
|||
{
|
||||
// AI友元类
|
||||
friend class NineChessAi_ab;
|
||||
|
||||
public:
|
||||
// 5个静态成员常量
|
||||
// 静态成员常量
|
||||
// 3圈,禁止修改!
|
||||
static const int RING = 3;
|
||||
static const int N_RINGS = 3;
|
||||
|
||||
// 8位,禁止修改!
|
||||
static const int SEAT = 8;
|
||||
static const int N_SEATS = 8;
|
||||
|
||||
// 预定义的规则数目
|
||||
static const int RULENUM = 4;
|
||||
static const int N_RULES = 4;
|
||||
|
||||
// 嵌套的规则结构体
|
||||
struct Rule
|
||||
{
|
||||
// 规则名称
|
||||
const char *name;
|
||||
|
||||
// 规则介绍
|
||||
const char *info;
|
||||
const char *description;
|
||||
|
||||
// 任一方子数,各9子或各12子
|
||||
int numOfChess;
|
||||
int nTotalPiecesEachSide;
|
||||
|
||||
// 赛点子数,少于则判负
|
||||
int numAtLest;
|
||||
int nPiecesAtLeast;
|
||||
|
||||
// 是否有斜线
|
||||
bool hasObliqueLine;
|
||||
bool hasObliqueLines;
|
||||
|
||||
// 是否有禁点(摆棋阶段被提子的点不能再摆子)
|
||||
bool hasForbidden;
|
||||
bool hasForbiddenPoint;
|
||||
|
||||
// 是否后摆棋者先行棋
|
||||
bool isDefensiveMoveFirst;
|
||||
bool isDefenderMoveFirst;
|
||||
|
||||
// 相同顺序和位置的重复“三连”是否可反复提子
|
||||
bool canRepeated;
|
||||
bool allowRemovePiecesRepeatedly;
|
||||
|
||||
// 多个“三连”能否多提子
|
||||
bool removeMore;
|
||||
bool allowRemoveMultiPieces;
|
||||
|
||||
// 摆棋满子(闷棋,只有12子棋才出现),是否算先手负,false为和棋
|
||||
bool isFullLose;
|
||||
bool isStartingPlayerLoseWhenBoardFull;
|
||||
|
||||
// 走棋阶段不能行动(被“闷”)是否算负,false则轮空(由对手走棋)
|
||||
bool isNoWayLose;
|
||||
bool isLoseWhenNoWay;
|
||||
|
||||
// 剩三子时是否可以飞棋
|
||||
bool canFly;
|
||||
bool allowFlyWhenRemainThreePieces;
|
||||
|
||||
// 最大步数,超出判和
|
||||
int maxSteps;
|
||||
int maxStepsLedToDraw;
|
||||
|
||||
// 包干最长时间(秒),超出判负,为0则不计时
|
||||
int maxTime;
|
||||
int maxTimeLedToLose;
|
||||
};
|
||||
|
||||
// 预定义的规则
|
||||
static const struct Rule RULES[RULENUM];
|
||||
static const struct Rule RULES[N_RULES];
|
||||
|
||||
// 局面阶段标识
|
||||
enum Phases : uint16_t
|
||||
enum GameStage : uint16_t
|
||||
{
|
||||
GAME_NOTSTARTED = 0x0001, // 未开局
|
||||
GAME_OPENING = 0x0002, // 开局(摆棋)
|
||||
GAME_MID = 0x0004, // 中局(走棋)
|
||||
GAME_OVER = 0x0008 // 结局
|
||||
GAME_NOTSTARTED = 0x0001, // 未开局
|
||||
GAME_PLACING = 0x0002, // 开局(摆棋)
|
||||
GAME_MOVING = 0x0004, // 中局(走棋)
|
||||
GAME_OVER = 0x0008 // 结局
|
||||
};
|
||||
|
||||
// 玩家标识,轮流状态,胜负标识
|
||||
// 玩家标识, 轮流状态, 胜负标识
|
||||
enum Players : uint16_t
|
||||
{
|
||||
PLAYER1 = 0x0010, // 玩家1
|
||||
PLAYER2 = 0x0020, // 玩家2
|
||||
DRAW = 0x0040, // 双方和棋
|
||||
NOBODY = 0x0080 // 胜负未分
|
||||
DRAW = 0x0040, // 双方和棋
|
||||
NOBODY = 0x0080 // 胜负未分
|
||||
};
|
||||
|
||||
// 动作状态标识
|
||||
enum Actions : uint16_t
|
||||
enum Action : uint16_t
|
||||
{
|
||||
ACTION_CHOOSE = 0x0100, // 选子
|
||||
ACTION_PLACE = 0x0200, // 落子
|
||||
ACTION_CHOOSE = 0x0100, // 选子
|
||||
ACTION_PLACE = 0x0200, // 落子
|
||||
ACTION_CAPTURE = 0x0400 // 提子
|
||||
};
|
||||
|
||||
// 棋局结构体,算法相关,包含当前棋盘数据
|
||||
// 单独分离出来供AI判断局面用,生成置换表时使用
|
||||
struct ChessData
|
||||
struct ChessContext
|
||||
{
|
||||
// 棋局,抽象为一个(5×8)的char数组,上下两行留空
|
||||
/* 0x00代表无棋子
|
||||
0x0F代表禁点
|
||||
0x11~0x1C代表先手第1~12子
|
||||
0x21~0x2c代表后手第1~12子
|
||||
判断棋子是先手的用(board[i] & 0x10)
|
||||
判断棋子是后手的用(board[i] & 0x20) */
|
||||
int board[(NineChess::RING + 2) * NineChess::SEAT];
|
||||
// 棋局,抽象为一个(5×8)的数组,上下两行留空
|
||||
/*
|
||||
0x00 代表无棋子
|
||||
0x0F 代表禁点
|
||||
0x11~0x1C 代表先手第 1~12 子
|
||||
0x21~0x2C 代表后手第 1~12 子
|
||||
判断棋子是先手的用 (board[i] & 0x10)
|
||||
判断棋子是后手的用 (board[i] & 0x20)
|
||||
*/
|
||||
int board[(NineChess::N_RINGS + 2) * NineChess::N_SEATS];
|
||||
|
||||
// 局面阶段标识
|
||||
enum NineChess::Phases phase;
|
||||
enum NineChess::GameStage stage;
|
||||
|
||||
// 轮流状态标识
|
||||
enum NineChess::Players turn;
|
||||
|
||||
// 动作状态标识
|
||||
enum NineChess::Actions action;
|
||||
enum NineChess::Action action;
|
||||
|
||||
// 玩家1剩余未放置子数
|
||||
int player1_InHand;
|
||||
// 玩家2剩余未放置子数
|
||||
int player2_InHand;
|
||||
// 玩家1盘面剩余子数
|
||||
int player1_Remain;
|
||||
// 玩家1盘面剩余子数
|
||||
int player2_Remain;
|
||||
// 尚待去除的子数
|
||||
int num_NeedRemove;
|
||||
int nPiecesInHand_1;
|
||||
|
||||
/* 本打算用如下的结构体来表示“三连”
|
||||
// 玩家2剩余未放置子数
|
||||
int nPiecesInHand_2;
|
||||
|
||||
// 玩家1盘面剩余子数
|
||||
int nPiecesOnBoard_1;
|
||||
|
||||
// 玩家1盘面剩余子数
|
||||
int nPiecesOnBoard_2;
|
||||
|
||||
// 尚待去除的子数
|
||||
int nPiecesNeedRemove;
|
||||
|
||||
/*
|
||||
本打算用如下的结构体来表示“三连”
|
||||
struct Mill {
|
||||
char piece1; // “三连”中最小的棋子
|
||||
char pos1; // 最小棋子的位置
|
||||
|
@ -142,106 +168,124 @@ public:
|
|||
|
||||
private:
|
||||
// 空棋盘点位,用于判断一个棋子位置是否在棋盘上
|
||||
static const char inBoard[(RING + 2) * SEAT];
|
||||
static const char onBoard[(N_RINGS + 2) * N_SEATS];
|
||||
|
||||
// 招法表,每个位置有最多4种走法:顺时针、逆时针、向内、向外
|
||||
// 这个表跟规则有关,一旦规则改变需要重新修改
|
||||
static int moveTable[(RING + 2) * SEAT][4];
|
||||
static int moveTable[(N_RINGS + 2) * N_SEATS][4];
|
||||
|
||||
// 成三表,表示棋盘上各个位置有成三关系的对应位置表
|
||||
// 这个表跟规则有关,一旦规则改变需要重新修改
|
||||
static int millTable[(RING + 2) * SEAT][3][2];
|
||||
static int millTable[(N_RINGS + 2) * N_SEATS][3][2];
|
||||
|
||||
public:
|
||||
explicit NineChess();
|
||||
virtual ~NineChess();
|
||||
|
||||
// 拷贝构造函数
|
||||
explicit NineChess(const NineChess &);
|
||||
|
||||
// 运算符重载
|
||||
const NineChess &operator=(const NineChess &);
|
||||
|
||||
// 设置棋局状态和棋盘数据,用于初始化
|
||||
bool setData(const struct Rule *rule,
|
||||
int s = 0, // 限制步数
|
||||
int t = 0, // 限制时间
|
||||
int step = 0, // 默认起始步数为0
|
||||
// 设置棋局状态和棋盘上下文,用于初始化
|
||||
bool setContext(const struct Rule *rule,
|
||||
int maxStepsLedToDraw = 0, // 限制步数
|
||||
int maxTimeLedToLose = 0, // 限制时间
|
||||
int initialStep = 0, // 默认起始步数为0
|
||||
int flags = GAME_NOTSTARTED | PLAYER1 | ACTION_PLACE, // 默认状态
|
||||
const char *boardsource = nullptr, // 默认空棋盘
|
||||
int p1_InHand = 12, // 玩家1剩余未放置子数
|
||||
int p2_InHand = 12, // 玩家2剩余未放置子数
|
||||
int num_NeedRemove = 0 // 尚待去除的子数
|
||||
const char *board = nullptr, // 默认空棋盘
|
||||
int nPiecesInHand_1 = 12, // 玩家1剩余未放置子数
|
||||
int nPiecesInHand_2 = 12, // 玩家2剩余未放置子数
|
||||
int nPiecesNeedRemove = 0 // 尚待去除的子数
|
||||
);
|
||||
|
||||
// 获取棋局状态和棋盘数据
|
||||
void getData(struct Rule &rule, int &step, int &flags, int *&boardsource, int &p1_InHand, int &p2_InHand, int &num_NeedRemove);
|
||||
// 获取棋局状态和棋盘上下文
|
||||
void getContext(struct Rule &rule, int &step, int &flags, int *&board,
|
||||
int &nPiecesInHand_1, int &p2_nPiecesInHand_2InHand, int &nPiecesNeedRemove);
|
||||
|
||||
// 获取当前规则
|
||||
const struct Rule *getRule() const
|
||||
{
|
||||
return ¤tRule;
|
||||
}
|
||||
|
||||
// 获取棋盘数据
|
||||
const int *getBoard() const
|
||||
{
|
||||
return data.board;
|
||||
return context.board;
|
||||
}
|
||||
// 获取棋子位置(c, p)
|
||||
bool getPieceCP(const Players &player, const int &number, int &c, int &p);
|
||||
|
||||
// 获取当前棋子
|
||||
bool getCurrentPiece(Players &player, int &number);
|
||||
|
||||
// 获取当前棋子位置点
|
||||
int getCurrentPos() const
|
||||
{
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
// 获取当前步数
|
||||
int getStep() const
|
||||
{
|
||||
return currentStep;
|
||||
}
|
||||
|
||||
// 获取局面阶段标识
|
||||
enum Phases getPhase() const
|
||||
enum GameStage getStage() const
|
||||
{
|
||||
return data.phase;
|
||||
return context.stage;
|
||||
}
|
||||
|
||||
// 获取轮流状态标识
|
||||
enum Players whosTurn() const
|
||||
{
|
||||
return data.turn;
|
||||
return context.turn;
|
||||
}
|
||||
|
||||
// 获取动作状态标识
|
||||
enum Actions getAction() const
|
||||
enum Action getAction() const
|
||||
{
|
||||
return data.action;
|
||||
return context.action;
|
||||
}
|
||||
|
||||
// 判断胜负
|
||||
enum Players whoWin() const
|
||||
{
|
||||
return winner;
|
||||
}
|
||||
// 玩家1和玩家2的用时
|
||||
void getPlayer_TimeMS(int &p1_ms, int &p2_ms);
|
||||
void getElapsedTimeMS(int &p1_ms, int &p2_ms);
|
||||
|
||||
// 获取棋局的字符提示
|
||||
const string getTip() const
|
||||
const string getTips() const
|
||||
{
|
||||
return tip;
|
||||
return tips;
|
||||
}
|
||||
|
||||
// 获取位置点棋子的归属人
|
||||
enum Players getWhosPiece(int c, int p);
|
||||
|
||||
// 获取当前招法
|
||||
const char *getCmdLine() const
|
||||
{
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
// 获得棋谱
|
||||
const list<string> *getCmdList() const
|
||||
{
|
||||
return &cmdlist;
|
||||
}
|
||||
|
||||
// 获取开局时间
|
||||
timeb getStartTimeb() const
|
||||
{
|
||||
return startTimeb;
|
||||
}
|
||||
|
||||
// 重新设置开局时间
|
||||
void setStartTimeb(timeb stimeb)
|
||||
{
|
||||
|
@ -249,111 +293,144 @@ public:
|
|||
}
|
||||
|
||||
// 玩家1剩余未放置子数
|
||||
int getPlayer1_InHand() const
|
||||
int getPiecesInHandCount_1() const
|
||||
{
|
||||
return data.player1_InHand;
|
||||
return context.nPiecesInHand_1;
|
||||
}
|
||||
|
||||
// 玩家2剩余未放置子数
|
||||
int getPlayer2_InHand() const
|
||||
int getPiecesInHandCount_2() const
|
||||
{
|
||||
return data.player2_InHand;
|
||||
return context.nPiecesInHand_2;
|
||||
}
|
||||
|
||||
// 玩家1盘面剩余子数
|
||||
int getPlayer1_Remain() const
|
||||
int getPiecesOnBoardCount_1() const
|
||||
{
|
||||
return data.player1_Remain;
|
||||
return context.nPiecesOnBoard_1;
|
||||
}
|
||||
|
||||
// 玩家1盘面剩余子数
|
||||
int getPlayer2_Remain() const
|
||||
int getPiecesOnBoardCount_2() const
|
||||
{
|
||||
return data.player2_Remain;
|
||||
return context.nPiecesOnBoard_2;
|
||||
}
|
||||
|
||||
// 尚待去除的子数
|
||||
int getNum_NeedRemove() const
|
||||
{
|
||||
return data.num_NeedRemove;
|
||||
return context.nPiecesNeedRemove;
|
||||
}
|
||||
|
||||
// 游戏重置
|
||||
bool reset();
|
||||
|
||||
// 游戏开始
|
||||
bool start();
|
||||
|
||||
// 选子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始
|
||||
bool choose(int c, int p);
|
||||
|
||||
// 落子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始
|
||||
bool place(int c, int p, long time_p = -1);
|
||||
|
||||
// 去子,在第c圈第p个位置,为迎合日常,c和p下标都从1开始
|
||||
bool capture(int c, int p, long time_p = -1);
|
||||
|
||||
// 认输
|
||||
bool giveup(Players loser);
|
||||
|
||||
// 命令行解析函数
|
||||
bool command(const char *cmd);
|
||||
|
||||
// 局面左右镜像
|
||||
void mirror(bool cmdChange = true);
|
||||
|
||||
// 局面内外翻转
|
||||
void turn(bool cmdChange = true);
|
||||
|
||||
// 局面逆时针旋转
|
||||
void rotate(int degrees, bool cmdChange = true);
|
||||
|
||||
protected:
|
||||
// 判断棋盘pos处的棋子处于几个“三连”中
|
||||
int isInMills(int pos);
|
||||
|
||||
// 判断玩家的所有棋子是否都处于“三连”状态
|
||||
bool isAllInMills(char ch);
|
||||
bool isAllInMills(enum Players);
|
||||
|
||||
// 判断玩家的棋子是否被围
|
||||
bool isSurrounded(int pos);
|
||||
|
||||
// 判断玩家的棋子是否全部被围
|
||||
bool isAllSurrounded(char ch);
|
||||
|
||||
bool isAllSurrounded(enum Players);
|
||||
|
||||
// 三连加入列表
|
||||
int addMills(int pos);
|
||||
|
||||
// 将棋盘下标形式转化为第c圈,第p位,c和p下标都从1开始
|
||||
bool pos2cp(const int pos, int &c, int &p);
|
||||
|
||||
// 将第c圈,第p位转化为棋盘下标形式,c和p下标都从1开始
|
||||
int cp2pos(int c, int p);
|
||||
|
||||
// 更新时间和状态,用内联函数以提高效率
|
||||
inline long update(long time_p = -1);
|
||||
|
||||
// 是否分出胜负
|
||||
bool win();
|
||||
|
||||
// 清除所有禁点
|
||||
void cleanForbidden();
|
||||
void cleanForbiddenPoints();
|
||||
|
||||
// 改变轮流
|
||||
enum NineChess::Players changeTurn();
|
||||
|
||||
// 设置提示
|
||||
void setTip();
|
||||
void setTips();
|
||||
|
||||
// 下面几个函数没有算法无关判断和无关操作,节约算法时间
|
||||
bool command(int move);
|
||||
bool choose(int pos);
|
||||
bool place(int pos);
|
||||
bool capture(int pos);
|
||||
|
||||
// hash函数
|
||||
uint64_t chessHash();
|
||||
|
||||
private:
|
||||
// 当前使用的规则
|
||||
struct Rule currentRule;
|
||||
// 棋局数据
|
||||
struct ChessData data;
|
||||
|
||||
// 棋局上下文
|
||||
struct ChessContext context;
|
||||
|
||||
// 棋局数据中的棋盘数据,单独提出来
|
||||
int *board_;
|
||||
|
||||
// 选中的棋子在board中的位置
|
||||
int currentPos;
|
||||
|
||||
// 胜负标识
|
||||
enum Players winner;
|
||||
|
||||
// 当前步数
|
||||
int currentStep;
|
||||
|
||||
// 游戏起始时间
|
||||
timeb startTimeb;
|
||||
|
||||
// 当前游戏时间
|
||||
timeb currentTimeb;
|
||||
|
||||
// 玩家1用时(毫秒)
|
||||
long player1_MS;
|
||||
long elapsedMS_1;
|
||||
|
||||
// 玩家2用时(毫秒)
|
||||
long player2_MS;
|
||||
long elapsedMS_2;
|
||||
|
||||
/* 当前招法,AI会用到,如下表示
|
||||
0x 00 00
|
||||
|
@ -372,7 +449,7 @@ private:
|
|||
list <string> cmdlist;
|
||||
|
||||
// 当前棋局的字符提示
|
||||
string tip;
|
||||
string tips;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,14 +35,14 @@ void NineChessAi_ab::buildChildren(Node *node)
|
|||
return;
|
||||
|
||||
// 临时变量
|
||||
char opponent = chessTemp.data.turn == NineChess::PLAYER1 ? 0x20 : 0x10;
|
||||
char opponent = chessTemp.context.turn == NineChess::PLAYER1 ? 0x20 : 0x10;
|
||||
// 列出所有合法的下一招
|
||||
switch (chessTemp.data.action) {
|
||||
switch (chessTemp.context.action) {
|
||||
case NineChess::ACTION_CHOOSE:
|
||||
case NineChess::ACTION_PLACE:
|
||||
// 对于开局落子
|
||||
if ((chessTemp.data.phase) & (NineChess::GAME_OPENING | NineChess::GAME_NOTSTARTED)) {
|
||||
for (int i = NineChess::SEAT; i < (NineChess::RING + 1) * NineChess::SEAT; i++) {
|
||||
if ((chessTemp.context.stage) & (NineChess::GAME_PLACING | NineChess::GAME_NOTSTARTED)) {
|
||||
for (int i = NineChess::N_SEATS; i < (NineChess::N_RINGS + 1) * NineChess::N_SEATS; i++) {
|
||||
if (!chessTemp.board_[i]) {
|
||||
Node *newNode = new Node;
|
||||
newNode->parent = node;
|
||||
|
@ -55,11 +55,11 @@ void NineChessAi_ab::buildChildren(Node *node)
|
|||
// 对于中局移子
|
||||
else {
|
||||
int newPos;
|
||||
for (int i = NineChess::SEAT; i < (NineChess::RING + 1) * NineChess::SEAT; i++) {
|
||||
for (int i = NineChess::N_SEATS; i < (NineChess::N_RINGS + 1) * NineChess::N_SEATS; i++) {
|
||||
if (!chessTemp.choose(i))
|
||||
continue;
|
||||
if ((chessTemp.data.turn == NineChess::PLAYER1 && (chessTemp.data.player1_Remain > chessTemp.currentRule.numAtLest || !chessTemp.currentRule.canFly)) ||
|
||||
(chessTemp.data.turn == NineChess::PLAYER2 && (chessTemp.data.player2_Remain > chessTemp.currentRule.numAtLest || !chessTemp.currentRule.canFly))) {
|
||||
if ((chessTemp.context.turn == NineChess::PLAYER1 && (chessTemp.context.nPiecesOnBoard_1 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces)) ||
|
||||
(chessTemp.context.turn == NineChess::PLAYER2 && (chessTemp.context.nPiecesOnBoard_2 > chessTemp.currentRule.nPiecesAtLeast || !chessTemp.currentRule.allowFlyWhenRemainThreePieces))) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
newPos = chessTemp.moveTable[i][j];
|
||||
if (newPos && !chessTemp.board_[newPos]) {
|
||||
|
@ -71,7 +71,7 @@ void NineChessAi_ab::buildChildren(Node *node)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (int j = NineChess::SEAT; j < (NineChess::RING + 1) * NineChess::SEAT; j++) {
|
||||
for (int j = NineChess::N_SEATS; j < (NineChess::N_RINGS + 1) * NineChess::N_SEATS; j++) {
|
||||
if (!chessTemp.board_[j]) {
|
||||
Node *newNode = new Node;
|
||||
newNode->parent = node;
|
||||
|
@ -88,7 +88,7 @@ void NineChessAi_ab::buildChildren(Node *node)
|
|||
case NineChess::ACTION_CAPTURE:
|
||||
// 全成三的情况
|
||||
if (chessTemp.isAllInMills(opponent)) {
|
||||
for (int i = NineChess::SEAT; i < (NineChess::RING + 1) * NineChess::SEAT; i++) {
|
||||
for (int i = NineChess::N_SEATS; i < (NineChess::N_RINGS + 1) * NineChess::N_SEATS; i++) {
|
||||
if (chessTemp.board_[i] & opponent) {
|
||||
Node *newNode = new Node;
|
||||
newNode->parent = node;
|
||||
|
@ -98,7 +98,7 @@ void NineChessAi_ab::buildChildren(Node *node)
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = NineChess::SEAT; i < (NineChess::RING + 1) * NineChess::SEAT; i++) {
|
||||
for (int i = NineChess::N_SEATS; i < (NineChess::N_RINGS + 1) * NineChess::N_SEATS; i++) {
|
||||
if (chessTemp.board_[i] & opponent) {
|
||||
if (!chessTemp.isInMills(i)) {
|
||||
Node *newNode = new Node;
|
||||
|
@ -156,7 +156,7 @@ void NineChessAi_ab::setChess(const NineChess &chess)
|
|||
|
||||
this->chess_ = chess;
|
||||
chessTemp = chess;
|
||||
chessData = &(chessTemp.data);
|
||||
chessData = &(chessTemp.context);
|
||||
requiredQuit = false;
|
||||
deleteTree(rootNode);
|
||||
rootNode = new Node;
|
||||
|
@ -169,15 +169,15 @@ int NineChessAi_ab::evaluate(Node *node)
|
|||
{
|
||||
// 初始评估值为0,对先手有利则增大,对后手有利则减小
|
||||
int value = 0;
|
||||
switch (chessData->phase) {
|
||||
switch (chessData->stage) {
|
||||
case NineChess::GAME_NOTSTARTED:
|
||||
break;
|
||||
|
||||
case NineChess::GAME_OPENING:
|
||||
case NineChess::GAME_PLACING:
|
||||
// 按手中的棋子计分,不要break;
|
||||
value += chessData->player1_InHand * 50 - chessData->player2_InHand * 50;
|
||||
value += chessData->nPiecesInHand_1 * 50 - chessData->nPiecesInHand_2 * 50;
|
||||
// 按场上棋子计分
|
||||
value += chessData->player1_Remain * 100 - chessData->player2_Remain * 100;
|
||||
value += chessData->nPiecesOnBoard_1 * 100 - chessData->nPiecesOnBoard_2 * 100;
|
||||
switch (chessData->action) {
|
||||
// 选子和落子使用相同的评价方法
|
||||
case NineChess::ACTION_CHOOSE:
|
||||
|
@ -185,16 +185,16 @@ int NineChessAi_ab::evaluate(Node *node)
|
|||
break;
|
||||
// 如果形成去子状态,每有一个可去的子,算100分
|
||||
case NineChess::ACTION_CAPTURE:
|
||||
value += (chessData->turn == NineChess::PLAYER1) ? (chessData->num_NeedRemove) * 100 : -(chessData->num_NeedRemove) * 100;
|
||||
value += (chessData->turn == NineChess::PLAYER1) ? (chessData->nPiecesNeedRemove) * 100 : -(chessData->nPiecesNeedRemove) * 100;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case NineChess::GAME_MID:
|
||||
case NineChess::GAME_MOVING:
|
||||
// 按场上棋子计分
|
||||
value += chessData->player1_Remain * 100 - chessData->player2_Remain * 100;
|
||||
value += chessData->nPiecesOnBoard_1 * 100 - chessData->nPiecesOnBoard_2 * 100;
|
||||
switch (chessData->action) {
|
||||
// 选子和落子使用相同的评价方法
|
||||
case NineChess::ACTION_CHOOSE:
|
||||
|
@ -202,7 +202,7 @@ int NineChessAi_ab::evaluate(Node *node)
|
|||
break;
|
||||
// 如果形成去子状态,每有一个可去的子,算128分
|
||||
case NineChess::ACTION_CAPTURE:
|
||||
value += (chessData->turn == NineChess::PLAYER1) ? (chessData->num_NeedRemove) * 128 : -(chessData->num_NeedRemove) * 128;
|
||||
value += (chessData->turn == NineChess::PLAYER1) ? (chessData->nPiecesNeedRemove) * 128 : -(chessData->nPiecesNeedRemove) * 128;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -211,9 +211,9 @@ int NineChessAi_ab::evaluate(Node *node)
|
|||
|
||||
// 终局评价最简单
|
||||
case NineChess::GAME_OVER:
|
||||
if (chessData->player1_Remain < chessTemp.currentRule.numAtLest)
|
||||
if (chessData->nPiecesOnBoard_1 < chessTemp.currentRule.nPiecesAtLeast)
|
||||
value = -15000;
|
||||
else if (chessData->player2_Remain < chessTemp.currentRule.numAtLest)
|
||||
else if (chessData->nPiecesOnBoard_2 < chessTemp.currentRule.nPiecesAtLeast)
|
||||
value = 15000;
|
||||
break;
|
||||
|
||||
|
@ -239,7 +239,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
|
|||
int minMax;
|
||||
|
||||
// 搜索到叶子节点(决胜局面)
|
||||
if (chessData->phase == NineChess::GAME_OVER) {
|
||||
if (chessData->stage == NineChess::GAME_OVER) {
|
||||
node->value = evaluate(node);
|
||||
if (node->value > 0)
|
||||
node->value += depth;
|
||||
|
@ -286,10 +286,10 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
|
|||
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
|
||||
minMax = -infinity;
|
||||
for (auto child : node->children) {
|
||||
dataStack.push(chessTemp.data);
|
||||
dataStack.push(chessTemp.context);
|
||||
chessTemp.command(child->move);
|
||||
value = alphaBetaPruning(depth - 1, alpha, beta, child);
|
||||
chessTemp.data = dataStack.top();
|
||||
chessTemp.context = dataStack.top();
|
||||
dataStack.pop();
|
||||
// 取最大值
|
||||
if (value > minMax)
|
||||
|
@ -307,10 +307,10 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
|
|||
else {
|
||||
minMax = infinity;
|
||||
for (auto child : node->children) {
|
||||
dataStack.push(chessTemp.data);
|
||||
dataStack.push(chessTemp.context);
|
||||
chessTemp.command(child->move);
|
||||
value = alphaBetaPruning(depth - 1, alpha, beta, child);
|
||||
chessTemp.data = dataStack.top();
|
||||
chessTemp.context = dataStack.top();
|
||||
dataStack.pop();
|
||||
// 取最小值
|
||||
if (value < minMax)
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
{
|
||||
int value; // 节点的值
|
||||
int move; // 招法的命令行指令,图上标示为节点前的连线
|
||||
struct Node *parent; // 父节点
|
||||
struct Node *parent; // 父节点
|
||||
list<struct Node *> children; // 子节点列表
|
||||
};
|
||||
|
||||
|
@ -44,44 +44,55 @@ public:
|
|||
~NineChessAi_ab();
|
||||
|
||||
void setChess(const NineChess &chess);
|
||||
|
||||
void quit()
|
||||
{
|
||||
requiredQuit = true;
|
||||
}
|
||||
|
||||
// Alpha-Beta剪枝算法
|
||||
int alphaBetaPruning(int depth);
|
||||
|
||||
// 返回最佳走法的命令行
|
||||
const char *bestMove();
|
||||
|
||||
protected:
|
||||
// 建立子节点
|
||||
void buildChildren(Node *node);
|
||||
|
||||
// 子节点排序
|
||||
void sortChildren(Node *node);
|
||||
|
||||
// 清空节点树
|
||||
void deleteTree(Node *node);
|
||||
|
||||
// 评价函数
|
||||
int evaluate(Node *node);
|
||||
|
||||
// Alpha-Beta剪枝算法
|
||||
int alphaBetaPruning(int depth, int alpha, int beta, Node *node);
|
||||
|
||||
// 返回招法的命令行
|
||||
const char *move2string(int move);
|
||||
|
||||
// 判断是否在哈希表中
|
||||
unordered_map<uint64_t, NineChessAi_ab::HashValue>::iterator findHash(uint64_t hash);
|
||||
|
||||
private:
|
||||
// 原始模型
|
||||
NineChess chess_;
|
||||
|
||||
// 演算用的模型
|
||||
NineChess chessTemp;
|
||||
NineChess::ChessData *chessData;
|
||||
|
||||
NineChess::ChessContext *chessData;
|
||||
// hash计算时,各种转换用的模型
|
||||
NineChess chessTempShift;
|
||||
|
||||
// 根节点
|
||||
Node *rootNode;
|
||||
// 局面数据栈
|
||||
stack<NineChess::ChessData> dataStack;
|
||||
stack<NineChess::ChessContext> dataStack;
|
||||
|
||||
// 标识,用于跳出剪枝算法,立即返回
|
||||
bool requiredQuit;
|
||||
|
|
|
@ -53,8 +53,10 @@ NineChessWindow::NineChessWindow(QWidget * parent) :
|
|||
|
||||
// 关联视图和场景
|
||||
ui.gameView->setScene(scene);
|
||||
|
||||
// 视图反走样
|
||||
ui.gameView->setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// 视图反锯齿
|
||||
ui.gameView->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
|
@ -92,6 +94,7 @@ void NineChessWindow::closeEvent(QCloseEvent *event)
|
|||
{
|
||||
if (file.isOpen())
|
||||
file.close();
|
||||
|
||||
// 取消自动运行
|
||||
ui.actionAutoRun_A->setChecked(false);
|
||||
//qDebug() << "closed";
|
||||
|
@ -133,12 +136,16 @@ void NineChessWindow::initialize()
|
|||
QAction *ruleAction = new QAction(i.value().at(0), this);
|
||||
ruleAction->setToolTip(i.value().at(1));
|
||||
ruleAction->setCheckable(true);
|
||||
|
||||
// 索引值放在QAction的Data里
|
||||
ruleAction->setData(i.key());
|
||||
|
||||
// 添加到动作列表
|
||||
ruleActionList.append(ruleAction);
|
||||
|
||||
// 添加到“规则”菜单
|
||||
ui.menu_R->addAction(ruleAction);
|
||||
|
||||
connect(ruleAction, SIGNAL(triggered()),
|
||||
this, SLOT(actionRules_triggered()));
|
||||
}
|
||||
|
@ -172,6 +179,7 @@ void NineChessWindow::initialize()
|
|||
// 更新LCD1,显示玩家1用时
|
||||
connect(game, SIGNAL(time1Changed(QString)),
|
||||
ui.lcdNumber_1, SLOT(display(QString)));
|
||||
|
||||
// 更新LCD2,显示玩家2用时
|
||||
connect(game, SIGNAL(time2Changed(QString)),
|
||||
ui.lcdNumber_2, SLOT(display(QString)));
|
||||
|
@ -183,6 +191,7 @@ void NineChessWindow::initialize()
|
|||
// 为状态栏添加一个正常显示的标签
|
||||
QLabel *statusBarlabel = new QLabel(this);
|
||||
ui.statusBar->addWidget(statusBarlabel);
|
||||
|
||||
// 更新状态栏
|
||||
connect(game, SIGNAL(statusBarChanged(QString)),
|
||||
statusBarlabel, SLOT(setText(QString)));
|
||||
|
@ -190,8 +199,10 @@ void NineChessWindow::initialize()
|
|||
// 默认第2号规则
|
||||
ruleNo = 1;
|
||||
ruleActionList.at(ruleNo)->setChecked(true);
|
||||
|
||||
// 重置游戏规则
|
||||
game->setRule(ruleNo);
|
||||
|
||||
// 更新规则显示
|
||||
ruleInfo();
|
||||
|
||||
|
@ -232,7 +243,7 @@ void NineChessWindow::ruleInfo()
|
|||
ui.labelRule->setText(tl + sl);
|
||||
// 规则提示
|
||||
ui.labelInfo->setToolTip(QString(NineChess::RULES[ruleNo].name) + "\n" +
|
||||
NineChess::RULES[ruleNo].info);
|
||||
NineChess::RULES[ruleNo].description);
|
||||
ui.labelRule->setToolTip(ui.labelInfo->toolTip());
|
||||
|
||||
//QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name))
|
||||
|
@ -287,6 +298,7 @@ void NineChessWindow::on_actionLimited_T_triggered()
|
|||
buttonBox->setCenterButtons(true);
|
||||
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定"));
|
||||
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消"));
|
||||
|
||||
// 布局
|
||||
formLayout->setSpacing(6);
|
||||
formLayout->setContentsMargins(11, 11, 11, 11);
|
||||
|
@ -295,9 +307,11 @@ void NineChessWindow::on_actionLimited_T_triggered()
|
|||
formLayout->setWidget(1, QFormLayout::LabelRole, label_time);
|
||||
formLayout->setWidget(1, QFormLayout::FieldRole, comboBox_time);
|
||||
formLayout->setWidget(2, QFormLayout::SpanningRole, buttonBox);
|
||||
|
||||
// 关联信号和槽函数
|
||||
connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
|
||||
connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
|
||||
|
||||
// 收集数据
|
||||
if (dialog->exec() == QDialog::Accepted) {
|
||||
int dStep = comboBox_step->currentData().toInt();
|
||||
|
@ -424,9 +438,11 @@ void NineChessWindow::on_actionSaveAs_A_triggered()
|
|||
if (path.isEmpty() == false) {
|
||||
if (file.isOpen())
|
||||
file.close();
|
||||
//文件对象
|
||||
|
||||
// 文件对象
|
||||
file.setFileName(path);
|
||||
//打开文件,只写方式打开
|
||||
|
||||
// 打开文件,只写方式打开
|
||||
bool isok = file.open(QFileDevice::WriteOnly | QFileDevice::Text);
|
||||
if (isok) {
|
||||
//写文件
|
||||
|
@ -733,6 +749,7 @@ void NineChessWindow::on_actionAbout_A_triggered()
|
|||
dialog->setObjectName(QStringLiteral("aboutDialog"));
|
||||
dialog->setWindowTitle(tr("九连棋"));
|
||||
dialog->setModal(true);
|
||||
|
||||
// 生成各个控件
|
||||
QVBoxLayout *vLayout = new QVBoxLayout(dialog);
|
||||
QHBoxLayout *hLayout = new QHBoxLayout;
|
||||
|
@ -740,6 +757,7 @@ void NineChessWindow::on_actionAbout_A_triggered()
|
|||
QLabel *label_icon2 = new QLabel(dialog);
|
||||
QLabel *label_text = new QLabel(dialog);
|
||||
QLabel *label_image = new QLabel(dialog);
|
||||
|
||||
// 设置各个控件数据
|
||||
label_icon1->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/black_piece.png")));
|
||||
label_icon2->setPixmap(QPixmap(QString::fromUtf8(":/image/resources/image/white_piece.png")));
|
||||
|
@ -762,6 +780,7 @@ void NineChessWindow::on_actionAbout_A_triggered()
|
|||
hLayout->addWidget(label_icon2);
|
||||
hLayout->addWidget(label_text);
|
||||
vLayout->addWidget(label_image);
|
||||
|
||||
// 运行对话框
|
||||
dialog->exec();
|
||||
|
||||
|
|
|
@ -26,10 +26,13 @@ protected:
|
|||
private slots:
|
||||
// 初始化
|
||||
void initialize();
|
||||
|
||||
// 动态增加的菜单栏动作的槽函数
|
||||
void actionRules_triggered();
|
||||
|
||||
// 更新规则标签
|
||||
void ruleInfo();
|
||||
|
||||
// 自动运行定时处理函数
|
||||
void onAutoRunTimeOut(QPrivateSignal signal);
|
||||
|
||||
|
@ -68,16 +71,22 @@ private slots:
|
|||
private:
|
||||
// 界面文件
|
||||
Ui::NineChessWindowClass ui;
|
||||
|
||||
// 视图场景
|
||||
GameScene *scene;
|
||||
|
||||
// 控制器
|
||||
GameController *game;
|
||||
|
||||
// 动态增加的菜单栏动作列表
|
||||
QList <QAction *> ruleActionList;
|
||||
|
||||
// 游戏的规则号,涉及菜单项和对话框,所以要有
|
||||
int ruleNo;
|
||||
|
||||
// 文件
|
||||
QFile file;
|
||||
|
||||
// 定时器
|
||||
QTimer autoRunTimer;
|
||||
};
|
||||
|
|
|
@ -17,23 +17,32 @@ PieceItem::PieceItem(QGraphicsItem *parent) :
|
|||
);
|
||||
// 设置缓存模式
|
||||
setCacheMode(DeviceCoordinateCache);
|
||||
|
||||
// 鼠标放在棋子上时显示为伸开的手形
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
|
||||
// 只接受左键事件
|
||||
//setAcceptedMouseButtons(Qt::LeftButton);
|
||||
|
||||
// 不接受鼠标事件
|
||||
setAcceptedMouseButtons(0);
|
||||
//setAcceptHoverEvents(true);
|
||||
|
||||
// 默认模型为没有棋子
|
||||
model_ = noPiece;
|
||||
|
||||
// 棋子尺寸
|
||||
size = PIECE_SIZE;
|
||||
|
||||
// 选中子标识线宽度
|
||||
chooseLineWeight = LINE_WEIGHT;
|
||||
|
||||
// 删除线宽度
|
||||
removeLineWeight = LINE_WEIGHT * 5;
|
||||
|
||||
// 选中线为黄色
|
||||
chooseLineColor = Qt::darkYellow;
|
||||
|
||||
// 删除线为橘红色
|
||||
removeLineColor = QColor(0xff, 0x75, 0);
|
||||
}
|
||||
|
@ -66,7 +75,7 @@ void PieceItem::paint(QPainter *painter,
|
|||
if (model_ == blackPiece)
|
||||
painter->drawPixmap(-size / 2, -size / 2, size, size,
|
||||
QPixmap(":/image/resources/image/black_piece.png"));
|
||||
// 如果模型为白色,则画白色棋子
|
||||
// 如果模型为白色,则画白色棋子
|
||||
else if (model_ == whitePiece)
|
||||
painter->drawPixmap(-size / 2, -size / 2, size, size,
|
||||
QPixmap(":/image/resources/image/white_piece.png"));
|
||||
|
@ -75,14 +84,17 @@ void PieceItem::paint(QPainter *painter,
|
|||
if (showNum) {
|
||||
// 如果模型为黑色,用白色笔画序号
|
||||
painter->setPen(QColor(255, 255, 255));
|
||||
|
||||
// 如果模型为白色,用白色笔画序号
|
||||
if (model_ == whitePiece)
|
||||
painter->setPen(QColor(0, 0, 0));
|
||||
|
||||
// 字体
|
||||
QFont font;
|
||||
font.setFamily("Arial");
|
||||
font.setPointSize(size / 3);
|
||||
painter->setFont(font);
|
||||
|
||||
// 画序号,默认中间位置偏下,需微调
|
||||
painter->drawText(boundingRect().adjusted(0, 0, 0, -size / 12), Qt::AlignCenter, QString::number(num));
|
||||
|
||||
|
|
|
@ -77,20 +77,28 @@ protected:
|
|||
private:
|
||||
// 棋子本质
|
||||
enum Models model_;
|
||||
|
||||
// 棋子序号,黑白都从1开始
|
||||
int num;
|
||||
|
||||
// 棋子尺寸
|
||||
qreal size;
|
||||
|
||||
// 有无删除线
|
||||
bool deleted_;
|
||||
|
||||
// 显示序号
|
||||
bool showNum;
|
||||
|
||||
// 选中子标识线宽度
|
||||
qreal chooseLineWeight;
|
||||
|
||||
// 删除线宽度
|
||||
qreal removeLineWeight;
|
||||
|
||||
// 选中线颜色
|
||||
QColor chooseLineColor;
|
||||
|
||||
// 删除线颜色
|
||||
QColor removeLineColor;
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue