添加AI设置——深度和定时器功能

This commit is contained in:
liuweilhy 2018-12-16 23:06:02 +08:00
parent df2b4f8f7a
commit 435a82ac5e
10 changed files with 271 additions and 105 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.6.1, 2018-12-02T12:38:12. -->
<!-- Written by QtCreator 4.6.1, 2018-12-15T21:55:22. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>

View File

@ -2,9 +2,17 @@
#include "aithread.h"
AiThread::AiThread(int id, QObject *parent) : QThread(parent),
waiting_(false)
waiting_(false),
aiDepth(7),
aiTime(8)
{
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);
}
AiThread::~AiThread()
@ -18,14 +26,24 @@ void AiThread::setAi(const NineChess &chess)
{
mutex.lock();
this->chess = &chess;
ai_ab.setChess(*(this->chess));
mutex.unlock();
}
void AiThread::setAi(const NineChess &chess, int depth, int time)
{
mutex.lock();
this->chess = &chess;
ai_ab.setChess(chess);
aiDepth = depth;
aiTime = time;
mutex.unlock();
}
void AiThread::run()
{
// 测试用数据
int iTemp = 0;
// int iTemp = 0;
// 设一个标识1号线程只管玩家12号线程只管玩家2
int i = 0;
@ -45,28 +63,40 @@ void AiThread::run()
mutex.unlock();
continue;
}
else {
ai_ab.setChess(*chess);
mutex.unlock();
}
ai_ab.alphaBetaPruning(6);
ai_ab.setChess(*chess);
emit calcStarted();
mutex.unlock();
ai_ab.alphaBetaPruning(aiDepth);
const char * str = ai_ab.bestMove();
qDebug() << str;
if (strcmp(str, "error!"))
emit command(str);
qDebug() << "Thread" << id << "run" << ++iTemp << "times";
// qDebug() << "Thread" << id << "run" << ++iTemp << "times";
emit calcFinished();
// 执行完毕后继续判断
mutex.lock();
if (!isInterruptionRequested()) {
mutex.lock();
pauseCondition.wait(&mutex);
mutex.unlock();
}
mutex.unlock();
}
qDebug() << "Thread" << id << "quit";
}
void AiThread::act()
{
if (isFinished() || !isRunning())
return;
mutex.lock();
waiting_ = false;
ai_ab.quit();
mutex.unlock();
}
void AiThread::pause()
{
mutex.lock();
@ -87,11 +117,12 @@ void AiThread::stop()
if (isFinished() || !isRunning())
return;
if(!isInterruptionRequested())
if (!isInterruptionRequested()) {
requestInterruption();
mutex.lock();
waiting_ = false;
ai_ab.quit();
pauseCondition.wakeAll();
mutex.unlock();
mutex.lock();
waiting_ = false;
ai_ab.quit();
pauseCondition.wakeAll();
mutex.unlock();
}
}

View File

@ -4,6 +4,7 @@
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QTimer>
#include "ninechess.h"
#include "ninechessai_ab.h"
@ -18,14 +19,29 @@ public:
signals:
// 招法信号
void command(const QString &cmdline, bool update = true);
// 开始计算的信号
void calcStarted();
// 计算结束的信号
void calcFinished();
protected:
void run() override;
public:
// AI设置
void setAi(const NineChess &chess);
void setAi(const NineChess &chess, int depth, int time);
// 深度和限时
void getDepthTime(int &depth, int &time) { depth = aiDepth; time = aiTime; }
public slots:
void setAi(const NineChess &);
// 强制出招,不退出线程
void act();
// 线程暂停
void pause();
// 线程继续
void resume();
// 退出线程
void stop();
private:
@ -42,6 +58,12 @@ private:
const NineChess *chess;
// Alpha-Beta剪枝算法类
NineChessAi_ab ai_ab;
// AI的层数
int aiDepth;
// AI的限时
int aiTime;
// 定时器
QTimer timer;
};
#endif // AITHREAD_H

View File

@ -6,6 +6,7 @@
#include <QGraphicsSceneMouseEvent>
#include <QKeyEvent>
#include <QApplication>
#include <QTimer>
#include <QSound>
#include <QDebug>
#include <QMessageBox>
@ -247,6 +248,34 @@ void GameController::setEngine2(bool arg)
}
}
void GameController::setAiDepthTime(int depth1, int time1, int depth2, int time2)
{
if (isEngine1) {
ai1.stop();
ai1.wait();
}
if (isEngine2) {
ai2.stop();
ai2.wait();
}
ai1.setAi(chess, depth1, time1);
ai2.setAi(chess, depth2, time2);
if (isEngine1) {
ai1.start();
}
if (isEngine2) {
ai2.start();
}
}
void GameController::getAiDepthTime(int &depth1, int &time1, int &depth2, int &time2)
{
ai1.getDepthTime(depth1, time1);
ai2.getDepthTime(depth2, time2);
}
void GameController::setAnimation(bool arg)
{
hasAnimation = arg;
@ -295,12 +324,12 @@ void GameController::flip()
else
phaseChange(currentRow, true);
ai1.setAi(chess);
ai2.setAi(chess);
if (isEngine1) {
ai1.setAi(chess);
ai1.start();
}
if (isEngine2) {
ai2.setAi(chess);
ai2.start();
}
}
@ -331,12 +360,12 @@ void GameController::mirror()
else
phaseChange(currentRow, true);
ai1.setAi(chess);
ai2.setAi(chess);
if (isEngine1) {
ai1.setAi(chess);
ai1.start();
}
if (isEngine2) {
ai2.setAi(chess);
ai2.start();
}
}
@ -366,12 +395,12 @@ void GameController::turnRight()
else
phaseChange(currentRow, true);
ai1.setAi(chess);
ai2.setAi(chess);
if (isEngine1) {
ai1.setAi(chess);
ai1.start();
}
if (isEngine2) {
ai2.setAi(chess);
ai2.start();
}
}
@ -398,12 +427,12 @@ void GameController::turnLeft()
// 刷新显示
updateScence();
ai1.setAi(chess);
ai2.setAi(chess);
if (isEngine1) {
ai1.setAi(chess);
ai1.start();
}
if (isEngine2) {
ai2.setAi(chess);
ai2.start();
}
}
@ -597,16 +626,18 @@ bool GameController::actionPiece(QPointF pos)
// 如果还未决出胜负
if (chess.whoWin() == NineChess::NOBODY) {
if (chess.whosTurn() == NineChess::PLAYER1) {
if (isEngine1)
if (isEngine1) {
ai1.resume();
}
if (isEngine2)
ai2.pause();
}
else {
if (isEngine1)
ai1.pause();
if (isEngine2)
if (isEngine2) {
ai2.resume();
}
}
}
// 如果已经决出胜负
@ -727,16 +758,18 @@ bool GameController::command(const QString &cmd, bool update /*= true*/)
// 如果还未决出胜负
if (chess.whoWin() == NineChess::NOBODY) {
if (chess.whosTurn() == NineChess::PLAYER1) {
if (isEngine1)
if (isEngine1) {
ai1.resume();
}
if (isEngine2)
ai2.pause();
}
else {
if (isEngine1)
ai1.pause();
if (isEngine2)
if (isEngine2) {
ai2.resume();
}
}
}
// 如果已经决出胜负

View File

@ -35,8 +35,9 @@ public:
bool isAnimation() { return hasAnimation; }
void setDurationTime(int i) { durationTime = i; }
int getDurationTime() { return durationTime; }
// 棋谱字符串列表模型
QStringListModel manualListModel;
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(先手)用时改变的信号
@ -138,7 +139,8 @@ private:
int time2;
// 用于主窗口状态栏显示的字符串
QString message;
// 棋谱字符串列表模型
QStringListModel manualListModel;
};
#endif // GAMECONTROLLER_H

View File

@ -41,6 +41,9 @@ protected slots:
}
void rowsInserted(const QModelIndex &parent, int start, int end) {
Q_UNUSED(parent)
Q_UNUSED(start)
Q_UNUSED(end)
newEmptyRow = true;
}

View File

@ -154,6 +154,7 @@ const NineChess & NineChess::operator=(const NineChess &chess)
memcpy(cmdline, chess.cmdline, sizeof(cmdline));
cmdlist = chess.cmdlist;
tip = chess.tip;
return *this;
}
@ -1593,6 +1594,7 @@ void NineChess::mirror(bool cmdChange /*= true*/)
{
char ch;
int i, j;
for (i = 1; i <= RING; i++) {
for (j = 1; j < SEAT / 2; j++) {
ch = board[i*SEAT + j];
@ -1601,7 +1603,8 @@ void NineChess::mirror(bool cmdChange /*= true*/)
}
}
int16_t p1, p2, p3;
uint64_t p1, p2, p3;
if (move_ < 0) {
i = (-move_) / SEAT;
j = (-move_) % SEAT;
@ -1611,15 +1614,16 @@ void NineChess::mirror(bool cmdChange /*= true*/)
else {
p1 = move_ >> 8;
p2 = move_ & 0x00ff;
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
j = (SEAT - j) % SEAT;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
j = (SEAT - j) % SEAT;
p2 = i * SEAT + j;
move_ = (p1 << 8) | p2;
move_ = (int16_t)((p1 << 8) | p2);
}
if (currentPos != 0) {
@ -1633,20 +1637,20 @@ void NineChess::mirror(bool cmdChange /*= true*/)
for (auto mill = data.millList.begin(); mill != data.millList.end(); mill++) {
p1 = (*mill & 0x000000ff00000000) >> 32;
p2 = (*mill & 0x0000000000ff0000) >> 16;
p2 = (*mill & 0x00000000000000ff);
p3 = (*mill & 0x00000000000000ff);
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
j = (SEAT - j) % SEAT;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
j = (SEAT - j) % SEAT;
p2 = i * SEAT + j;
i = p3 / SEAT;
j = p3 % SEAT;
i = (int)p3 / SEAT;
j = (int)p3 % SEAT;
j = (SEAT - j) % SEAT;
p3 = i * SEAT + j;
@ -1660,7 +1664,6 @@ void NineChess::mirror(bool cmdChange /*= true*/)
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
long tm = -1;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
@ -1714,13 +1717,14 @@ void NineChess::turn(bool cmdChange /*= true*/)
{
char ch;
int i, j;
for (i = 0; i < SEAT; i++) {
ch = board[SEAT + i];
board[SEAT + i] = board[SEAT*RING + i];
board[SEAT*RING + i] = ch;
}
int16_t p1, p2, p3;
uint64_t p1, p2, p3;
if (move_ < 0) {
i = (-move_) / SEAT;
@ -1734,21 +1738,21 @@ void NineChess::turn(bool cmdChange /*= true*/)
else {
p1 = move_ >> 8;
p2 = move_ & 0x00ff;
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
if (i == 1)
i = RING;
else if (i == RING)
i = 1;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
if (i == 1)
i = RING;
else if (i == RING)
i = 1;
p2 = i * SEAT + j;
move_ = (p1 << 8) | p2;
move_ = (int16_t)((p1 << 8) | p2);
}
if (currentPos != 0) {
@ -1765,26 +1769,26 @@ void NineChess::turn(bool cmdChange /*= true*/)
for (auto mill = data.millList.begin(); mill != data.millList.end(); mill++) {
p1 = (*mill & 0x000000ff00000000) >> 32;
p2 = (*mill & 0x0000000000ff0000) >> 16;
p2 = (*mill & 0x00000000000000ff);
p3 = (*mill & 0x00000000000000ff);
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
if (i == 1)
i = RING;
else if (i == RING)
i = 1;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
if (i == 1)
i = RING;
else if (i == RING)
i = 1;
p2 = i * SEAT + j;
i = p3 / SEAT;
j = p3 % SEAT;
i = (int)p3 / SEAT;
j = (int)p3 % SEAT;
if (i == 1)
i = RING;
else if (i == RING)
@ -1801,7 +1805,6 @@ void NineChess::turn(bool cmdChange /*= true*/)
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
long tm = -1;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {
@ -1889,6 +1892,7 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
char ch1, ch2;
int i, j;
if (degrees == 2) {
for (i = 1; i <= RING; i++) {
ch1 = board[i*SEAT];
@ -1923,7 +1927,7 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
else
return;
int16_t p1, p2, p3;
uint64_t p1, p2, p3;
if (move_ < 0) {
i = (-move_) / SEAT;
@ -1934,15 +1938,15 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
else {
p1 = move_ >> 8;
p2 = move_ & 0x00ff;
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
j = (j + SEAT - degrees) % SEAT;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
j = (j + SEAT - degrees) % SEAT;
p2 = i * SEAT + j;
move_ = (p1 << 8) | p2;
move_ = (int16_t)((p1 << 8) | p2);
}
if (currentPos != 0) {
@ -1956,20 +1960,20 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
for (auto mill = data.millList.begin(); mill != data.millList.end(); mill++) {
p1 = (*mill & 0x000000ff00000000) >> 32;
p2 = (*mill & 0x0000000000ff0000) >> 16;
p2 = (*mill & 0x00000000000000ff);
p3 = (*mill & 0x00000000000000ff);
i = p1 / SEAT;
j = p1 % SEAT;
i = (int)p1 / SEAT;
j = (int)p1 % SEAT;
j = (j + SEAT - degrees) % SEAT;
p1 = i * SEAT + j;
i = p2 / SEAT;
j = p2 % SEAT;
i = (int)p2 / SEAT;
j = (int)p2 % SEAT;
j = (j + SEAT - degrees) % SEAT;
p2 = i * SEAT + j;
i = p3 / SEAT;
j = p3 % SEAT;
i = (int)p3 / SEAT;
j = (int)p3 % SEAT;
j = (j + SEAT - degrees) % SEAT;
p3 = i * SEAT + j;
@ -1983,7 +1987,6 @@ void NineChess::rotate(int degrees, bool cmdChange /*= true*/)
int c1, p1, c2, p2;
int args = 0;
int mm = 0, ss = 0, mss = 0;
long tm = -1;
args = sscanf(cmdline, "(%1u,%1u)->(%1u,%1u) %2u:%2u.%3u", &c1, &p1, &c2, &p2, &mm, &ss, &mss);
if (args >= 4) {

View File

@ -10,8 +10,7 @@
NineChessAi_ab::NineChessAi_ab():
rootNode(nullptr),
requiredQuit(false),
depth(3) // 默认3层深度
requiredQuit(false)
{
rootNode = new Node;
rootNode->value = 0;
@ -345,21 +344,6 @@ const char *NineChessAi_ab::move2string(int16_t move)
return cmdline;
}
void NineChessAi_ab::reverse(const NineChess *node1, NineChess *node2, int i)
{
}
void NineChessAi_ab::turn(const NineChess *node1, NineChess *node2, int i)
{
}
void NineChessAi_ab::rotate(const NineChess *node1, NineChess *node2, int i)
{
}
bool NineChessAi_ab::isInHash(const Node *node)
{
/*

View File

@ -42,7 +42,6 @@ public:
~NineChessAi_ab();
void setChess(const NineChess &chess);
void setDepth(int depth) { this->depth = depth; }
void quit() { requiredQuit = true; }
// Alpha-Beta剪枝算法
int alphaBetaPruning(int depth);
@ -63,13 +62,6 @@ protected:
// 返回招法的命令行
const char *move2string(int16_t move);
// 局面逆序
void reverse(const NineChess *node1, NineChess *node2, int i);
// 局面层次翻转
void turn(const NineChess *node1, NineChess *node2, int i);
// 局面旋转
void rotate(const NineChess *node1, NineChess *node2, int i);
// 判断是否在哈希表中
bool isInHash(const Node *node);
@ -92,7 +84,7 @@ private:
// 标识,用于跳出剪枝算法,立即返回
bool requiredQuit;
// 剪枝算法的层深
int depth;
// int depth;
// 定义极大值等于32位有符号整形数字的最大值
static const int infinity = INT32_MAX;

View File

@ -13,6 +13,10 @@
#include <QComboBox>
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QSpinBox>
#include <QLabel>
#include <QHelpEvent>
#include <QToolTip>
@ -53,7 +57,6 @@ NineChessWindow::NineChessWindow(QWidget *parent)
ui.gameView->setRenderHint(QPainter::Antialiasing);
// 因功能限制,使部分功能不可用,将来再添加
ui.actionEngine_E->setDisabled(true);
ui.actionInternet_I->setDisabled(true);
ui.actionSetting_O->setDisabled(true);
@ -194,7 +197,7 @@ void NineChessWindow::initialize()
ruleInfo();
// 关联列表视图和字符串列表模型
ui.listView->setModel(&(game->manualListModel));
ui.listView->setModel(game->getManualListModel());
// 因为QListView的rowsInserted在setModel之后才能启动
// 第一次需手动初始化选中listView第一项
//qDebug() << ui.listView->model();
@ -563,6 +566,7 @@ void NineChessWindow::on_actionRowChange()
void NineChessWindow::onAutoRunTimeOut(QPrivateSignal signal)
{
Q_UNUSED(signal)
int rows = ui.listView->model()->rowCount();
int currentRow = ui.listView->currentIndex().row();
@ -645,7 +649,99 @@ void NineChessWindow::on_actionInternet_I_triggered()
void NineChessWindow::on_actionEngine_E_triggered()
{
// 空着,有时间再做
// 定义新对话框
QDialog *dialog = new QDialog(this);
dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog->setObjectName(QStringLiteral("Dialog"));
dialog->setWindowTitle(tr("AI设置"));
dialog->resize(256, 188);
dialog->setModal(true);
// 生成各个控件
QVBoxLayout *vLayout = new QVBoxLayout(dialog);
QGroupBox *groupBox1 = new QGroupBox(dialog);
QGroupBox *groupBox2 = new QGroupBox(dialog);
QHBoxLayout *hLayout1 = new QHBoxLayout;
QLabel *label_depth1 = new QLabel(dialog);
QSpinBox *spinBox_depth1 = new QSpinBox(dialog);
QLabel *label_time1 = new QLabel(dialog);
QSpinBox *spinBox_time1 = new QSpinBox(dialog);
QHBoxLayout *hLayout2 = new QHBoxLayout;
QLabel *label_depth2 = new QLabel(dialog);
QSpinBox *spinBox_depth2 = new QSpinBox(dialog);
QLabel *label_time2 = new QLabel(dialog);
QSpinBox *spinBox_time2 = new QSpinBox(dialog);
QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
// 设置各个控件数据
groupBox1->setTitle(tr("玩家1 AI设置"));
label_depth1->setText(tr("深度"));
spinBox_depth1->setMinimum(1);
spinBox_depth1->setMaximum(10);
label_time1->setText(tr("限时"));
spinBox_time1->setMinimum(1);
spinBox_time1->setMaximum(30);
groupBox2->setTitle(tr("玩家2 AI设置"));
label_depth2->setText(tr("深度"));
spinBox_depth2->setMinimum(1);
spinBox_depth2->setMaximum(10);
label_time2->setText(tr("限时"));
spinBox_time2->setMinimum(1);
spinBox_time2->setMaximum(30);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->setCenterButtons(true);
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消"));
// 布局控件
vLayout->addWidget(groupBox1);
vLayout->addWidget(groupBox2);
vLayout->addWidget(buttonBox);
groupBox1->setLayout(hLayout1);
groupBox2->setLayout(hLayout2);
hLayout1->addWidget(label_depth1);
hLayout1->addWidget(spinBox_depth1);
hLayout1->addWidget(label_time1);
hLayout1->addWidget(spinBox_time1);
hLayout2->addWidget(label_depth2);
hLayout2->addWidget(spinBox_depth2);
hLayout2->addWidget(label_time2);
hLayout2->addWidget(spinBox_time2);
// 关联信号和槽函数
connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
// 目前数据
int depth1, depth2, time1, time2;
game->getAiDepthTime(depth1, time1, depth2, time2);
spinBox_depth1->setValue(depth1);
spinBox_depth2->setValue(depth2);
spinBox_time1->setValue(time1);
spinBox_time2->setValue(time2);
// 新设数据
if (dialog->exec() == QDialog::Accepted) {
int depth1_new, depth2_new, time1_new, time2_new;
depth1_new = spinBox_depth1->value();
depth2_new = spinBox_depth2->value();
time1_new = spinBox_time1->value();
time2_new = spinBox_time2->value();
if (depth1 != depth1_new || depth2 != depth2_new || time1 != time1_new || time2 != time2_new) {
// 重置AI
game->setAiDepthTime(depth1_new, time1_new, depth2_new, time2_new);
}
}
// 删除对话框,子控件会一并删除
dialog->disconnect();
delete dialog;
}
void NineChessWindow::on_actionViewHelp_V_triggered()