修改了一些bug,但AI仍然有些蠢

This commit is contained in:
liuweilhy 2018-12-06 22:09:40 +08:00
parent 24d897eb69
commit 76da5d3527
7 changed files with 229 additions and 181 deletions

View File

@ -1,9 +1,10 @@
#include <QDebug>
#include "aithread.h"
AiThread::AiThread(QObject *parent) : QThread(parent),
AiThread::AiThread(int id, QObject *parent) : QThread(parent),
waiting_(false)
{
this->id = id;
}
AiThread::~AiThread()
@ -22,27 +23,46 @@ void AiThread::setAi(const NineChess &chess)
void AiThread::run()
{
// 测试用数据
int iTemp = 0;
// 设一个标识1号线程只管玩家12号线程只管玩家2
int i = 0;
forever{
if (isInterruptionRequested())
return;
while (!isInterruptionRequested()) {
mutex.lock();
if (waiting_)
pauseCondition.wait(&mutex);
mutex.unlock();
if (chess->whosTurn() == NineChess::PLAYER1)
i = 1;
else if (chess->whosTurn() == NineChess::PLAYER2)
i = 2;
else
i = 0;
ai_ab.setChess(*chess);
ai_ab.alphaBetaPruning(5);
if (i != id || waiting_) {
pauseCondition.wait(&mutex);
mutex.unlock();
continue;
}
else {
ai_ab.setChess(*chess);
mutex.unlock();
}
ai_ab.alphaBetaPruning(3);
const char * str = ai_ab.bestMove();
qDebug() << str;
if (strcmp(str, "error!"))
emit command(str);
qDebug() << "Thread" << id << " run " << ++iTemp << "times";
// 测试用
qDebug() << "thread running " << iTemp++ << "times";
// 执行完毕后继续判断
if (!isInterruptionRequested()) {
mutex.lock();
pauseCondition.wait(&mutex);
mutex.unlock();
}
}
qDebug() << "Thread" << id << " quit.";
}
void AiThread::pause()
@ -62,14 +82,13 @@ void AiThread::resume()
void AiThread::stop()
{
if(isFinished())
if (isFinished() || !isRunning())
return;
if(!isInterruptionRequested())
requestInterruption();
mutex.lock();
requestInterruption();
if (waiting_) {
waiting_ = false;
pauseCondition.wakeAll();
}
waiting_ = false;
pauseCondition.wakeAll();
mutex.unlock();
}

View File

@ -12,7 +12,7 @@ class AiThread : public QThread
Q_OBJECT
public:
explicit AiThread(QObject *parent = nullptr);
explicit AiThread(int id, QObject *parent = nullptr);
~AiThread();
signals:
@ -29,6 +29,8 @@ public slots:
void stop();
private:
// 玩家ID
int id;
// 互斥锁
QMutex mutex;
// 线程等待标识,这里没用到,留着以后扩展用

View File

@ -30,7 +30,9 @@ GameController::GameController(GameScene &scene, QObject *parent) : QObject(pare
timeID(0),
ruleNo(-1),
timeLimit(0),
stepsLimit(0)
stepsLimit(0),
ai1(1),
ai2(2)
{
// 已在view的样式表中添加背景scene中不用添加背景
// 区别在于view中的背景不随视图变换而变换scene中的背景随视图变换而变换
@ -53,6 +55,11 @@ GameController::~GameController()
// 停止计时器
if (timeID != 0)
killTimer(timeID);
// 停掉线程
ai1.stop();
ai2.stop();
ai1.wait();
ai2.wait();
}
const QMap<int, QStringList> GameController::getActions()
@ -94,11 +101,9 @@ void GameController::gameReset()
// 停掉线程
ai1.stop();
ai1.quit();
ai1.wait();
ai2.stop();
ai2.quit();
ai2.wait();
isEngine1 = false;
isEngine2 = false;
// 清除棋子
qDeleteAll(pieceList);
@ -331,29 +336,55 @@ void GameController::timerEvent(QTimerEvent *event)
bool GameController::command(const QString &cmd, bool update /*= true*/)
{
if (chess.command(cmd.toStdString().c_str())) {
if (chess.getPhase() == NineChess::GAME_NOTSTARTED) {
gameReset();
gameStart();
}
if (update)
updateScence(chess);
// 将新增的棋谱行插入到ListModel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
for (auto i = (chess.getCmdList())->begin(); i != (chess.getCmdList())->end(); ++i) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), (*i).c_str());
}
return true;
}
else {
// 防止接收滞后结束的线程发送的指令
if (sender() == &ai1 && !isEngine1)
return false;
if (sender() == &ai2 && !isEngine2)
return false;
if (!chess.command(cmd.toStdString().c_str()))
return false;
if (chess.getPhase() == NineChess::GAME_NOTSTARTED) {
gameReset();
gameStart();
}
if (update)
updateScence(chess);
// 将新增的棋谱行插入到ListModel
currentRow = manualListModel.rowCount() - 1;
int k = 0;
// 输出命令行
for (auto i = (chess.getCmdList())->begin(); i != (chess.getCmdList())->end(); ++i) {
// 跳过已添加的因标准list容器没有下标
if (k++ <= currentRow)
continue;
manualListModel.insertRow(++currentRow);
manualListModel.setData(manualListModel.index(currentRow), (*i).c_str());
}
// AI设置
if (&chess == &(this->chess)) {
// 如果还未决出胜负
if (chess.whoWin() == NineChess::NOBODY) {
if (chess.whosTurn() == NineChess::PLAYER1) {
if (isEngine1)
ai1.resume();
}
else {
if (isEngine2)
ai2.resume();
}
}
// 如果已经决出胜负
else {
ai1.stop();
ai2.stop();
}
}
return true;
}
// 历史局面
@ -724,27 +755,5 @@ bool GameController::updateScence(NineChess &chess)
animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
// AI设置
if (&chess == &(this->chess)) {
// 如果还未决出胜负
if (chess.whoWin() == NineChess::NOBODY) {
if (chess.whosTurn() == NineChess::PLAYER1) {
if(isEngine1)
ai1.resume();
ai2.pause();
}
else {
if(isEngine2)
ai2.resume();
ai1.pause();
}
}
// 如果已经决出胜负
else {
ai1.stop();
ai2.stop();
}
}
return true;
}

View File

@ -1165,13 +1165,13 @@ bool NineChess::command(int16_t move)
if (move < 0) {
return capture(-move);
}
else if (move & 0x00ff) {
return place(move & 0x00ff);
}
else {
else if (move & 0x1f00) {
if (choose(move >> 8))
return place(move & 0x00ff);
}
else {
return place(move & 0x00ff);
}
return false;
}

View File

@ -173,50 +173,50 @@ public:
// 获取棋局状态和棋盘数据
void getData(struct Rule &rule, int &step, int &flags, const char *&boardsource, int &p1_InHand, int &p2_InHand, int &num_NeedRemove);
// 获取当前规则
const struct Rule *getRule() { return &rule; }
const struct Rule *getRule() const { return &rule; }
// 获取棋盘数据
const char *getBoard() { return data.board; }
const char *getBoard() const { return data.board; }
// 获取棋子位置(c, p)
bool getPieceCP(const Players &player, const int &number, int &c, int &p);
// 获取当前棋子
bool getCurrentPiece(Players &player, int &number);
// 获取当前棋子位置点
int getCurrentPos() { return currentPos; }
int getCurrentPos() const { return currentPos; }
// 获取当前步数
int getStep() { return data.step; }
int getStep() const { return data.step; }
// 获取局面阶段标识
enum Phases getPhase() { return data.phase; }
enum Phases getPhase() const { return data.phase; }
// 获取轮流状态标识
enum Players whosTurn() { return data.turn; }
enum Players whosTurn() const { return data.turn; }
// 获取动作状态标识
enum Actions getAction() { return data.action; }
enum Actions getAction() const { return data.action; }
// 判断胜负
enum Players whoWin() { return winner; }
enum Players whoWin() const { return winner; }
// 玩家1和玩家2的用时
void getPlayer_TimeMS(int &p1_ms, int &p2_ms);
// 获取棋局的字符提示
const string getTip() { return tip; }
const string getTip() const { return tip; }
// 获取位置点棋子的归属人
enum Players getWhosPiece(int c, int p);
// 获取当前招法
const char *getCmdLine() { return cmdline; }
const char *getCmdLine() const { return cmdline; }
// 获得棋谱
const list<string> * getCmdList() { return &cmdlist; }
const list<string> * getCmdList() const { return &cmdlist; }
// 获取开局时间
timeb getStartTimeb() { return startTimeb; }
timeb getStartTimeb() const { return startTimeb; }
// 重新设置开局时间
void setStartTimeb(timeb stimeb) { startTimeb = stimeb; }
// 玩家1剩余未放置子数
int getPlayer1_InHand() { return data.player1_InHand; }
int getPlayer1_InHand() const { return data.player1_InHand; }
// 玩家2剩余未放置子数
int getPlayer2_InHand() { return data.player2_InHand; }
int getPlayer2_InHand() const { return data.player2_InHand; }
// 玩家1盘面剩余子数
int getPlayer1_Remain() { return data.player1_Remain; }
int getPlayer1_Remain() const { return data.player1_Remain; }
// 玩家1盘面剩余子数
int getPlayer2_Remain() { return data.player2_Remain; }
int getPlayer2_Remain() const { return data.player2_Remain; }
// 尚待去除的子数
int getNum_NeedRemove() { return data.num_NeedRemove; }
int getNum_NeedRemove() const { return data.num_NeedRemove; }
// 游戏重置
bool reset();

View File

@ -7,6 +7,7 @@
#include "ninechessai_ab.h"
#include <cmath>
#include <time.h>
#include <qdebug.h>
NineChessAi_ab::NineChessAi_ab():
rootNode(nullptr),
@ -125,7 +126,10 @@ void NineChessAi_ab::sortChildren(Node *node)
i->value = evaluate(node);
}
// 排序
node->children.sort([](Node *n1, Node *n2) { return n1->value > n2->value; });
if(chessTemp.whosTurn() == NineChess::PLAYER1)
node->children.sort([](Node *n1, Node *n2) { return n1->value > n2->value; });
else
node->children.sort([](Node *n1, Node *n2) { return n1->value < n2->value; });
}
void NineChessAi_ab::deleteTree(Node *node)
@ -157,21 +161,21 @@ void NineChessAi_ab::setChess(const NineChess &chess)
{
// 对于0、2、4、6位偶数位
if (!(j & 1)) {
boardScore[1 * NineChess::SEAT + j] = 80;
boardScore[1 * NineChess::SEAT + j] = 90;
boardScore[2 * NineChess::SEAT + j] = 100;
boardScore[3 * NineChess::SEAT + j] = 80;
boardScore[3 * NineChess::SEAT + j] = 90;
}
// 对于有斜线情况下的1、3、5、7位奇数位
else if(chessTemp.rule.hasObliqueLine) {
boardScore[1 * NineChess::SEAT + j] = 70;
boardScore[2 * NineChess::SEAT + j] = 90;
boardScore[3 * NineChess::SEAT + j] = 70;
boardScore[1 * NineChess::SEAT + j] = 85;
boardScore[2 * NineChess::SEAT + j] = 95;
boardScore[3 * NineChess::SEAT + j] = 85;
}
// 对于无斜线情况下的1、3、5、7位奇数位
else {
boardScore[1 * NineChess::SEAT + j] = 60;
boardScore[2 * NineChess::SEAT + j] = 80;
boardScore[3 * NineChess::SEAT + j] = 60;
boardScore[1 * NineChess::SEAT + j] = 80;
boardScore[2 * NineChess::SEAT + j] = 85;
boardScore[3 * NineChess::SEAT + j] = 80;
}
}
}
@ -203,9 +207,9 @@ int NineChessAi_ab::evaluate(Node *node)
case NineChess::ACTION_CHOOSE:
case NineChess::ACTION_PLACE:
break;
// 如果形成去子状态,每有一个可去的子,算500分
// 如果形成去子状态,每有一个可去的子,算1000分
case NineChess::ACTION_CAPTURE:
value += (chessData->turn == NineChess::PLAYER1) ? chessData->num_NeedRemove * 500 : -chessData->num_NeedRemove * 500;
value += (chessData->turn == NineChess::PLAYER1) ? chessData->num_NeedRemove * 1000 : -chessData->num_NeedRemove * 1000;
break;
default:
break;
@ -253,7 +257,8 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
if (chessTemp.whosTurn() == NineChess::PLAYER1) {
for (auto child : node->children) {
dataStack.push(chessTemp.data);
chessTemp.command(child->move);
if(!chessTemp.command(child->move))
qDebug() << child->move;
value = alphaBetaPruning(depth - 1, alpha, beta, child);
chessTemp.data = dataStack.top();
dataStack.pop();
@ -262,6 +267,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
alpha = value;
// 剪枝返回
if (alpha >= beta) {
node->value = alpha;
return value;
}
}
@ -272,7 +278,8 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
else {
for (auto child : node->children) {
dataStack.push(chessTemp.data);
chessTemp.command(child->move);
if(!chessTemp.command(child->move))
qDebug() << child->move;
value = alphaBetaPruning(depth - 1, alpha, beta, child);
chessTemp.data = dataStack.top();
dataStack.pop();
@ -281,6 +288,7 @@ int NineChessAi_ab::alphaBetaPruning(int depth, int alpha, int beta, Node *node)
beta = value;
// 剪枝返回
if (alpha >= beta) {
node->value = beta;
return value;
}
}

View File

@ -27,7 +27,7 @@ NineChessWindow::NineChessWindow(QWidget *parent)
: QMainWindow(parent),
scene(nullptr),
game(nullptr),
ruleNo(0)
ruleNo(-1)
{
ui.setupUi(this);
//去掉标题栏
@ -181,7 +181,6 @@ void NineChessWindow::initialize()
// 默认第2号规则
ruleNo = 2;
// 设置选中当前规则的菜单项
ruleActionList.at(ruleNo)->setChecked(true);
// 重置游戏规则
game->setRule(ruleNo);
@ -232,6 +231,83 @@ void NineChessWindow::ruleInfo()
// .arg(tr(NineChess::RULES[ruleNo].info));
}
void NineChessWindow::on_actionLimited_T_triggered()
{
/* 其实本来可以用设计器做个ui然后从QDialog派生个自己的对话框
*
*
* QDialog界面
*/
int gStep = game->getStepsLimit();
int gTime = game->getTimeLimit();
// 定义新对话框
QDialog *dialog = new QDialog(this);
dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog->setObjectName(QStringLiteral("Dialog"));
dialog->setWindowTitle(tr("步数和时间限制"));
dialog->resize(256, 108);
dialog->setModal(true);
// 生成各个控件
QFormLayout *formLayout = new QFormLayout(dialog);
QLabel *label_step = new QLabel(dialog);
QLabel *label_time = new QLabel(dialog);
QComboBox *comboBox_step = new QComboBox(dialog);
QComboBox *comboBox_time = new QComboBox(dialog);
QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
// 设置各个控件ObjectName不设也没关系
/*formLayout->setObjectName(QStringLiteral("formLayout"));
label_step->setObjectName(QStringLiteral("label_step"));
label_time->setObjectName(QStringLiteral("label_time"));
comboBox_step->setObjectName(QStringLiteral("comboBox_step"));
comboBox_time->setObjectName(QStringLiteral("comboBox_time"));
buttonBox->setObjectName(QStringLiteral("buttonBox"));*/
// 设置各个控件数据
label_step->setText(tr("超出限制步数判和:"));
label_time->setText(tr("任意一方超时判负:"));
comboBox_step->addItem(tr("无限制"), 0);
comboBox_step->addItem(tr("50步"), 50);
comboBox_step->addItem(tr("100步"), 100);
comboBox_step->addItem(tr("200步"), 200);
comboBox_time->addItem(tr("无限制"), 0);
comboBox_time->addItem(tr("5分钟"), 5);
comboBox_time->addItem(tr("10分钟"), 10);
comboBox_time->addItem(tr("20分钟"), 20);
comboBox_step->setCurrentIndex(comboBox_step->findData(gStep));
comboBox_time->setCurrentIndex(comboBox_time->findData(gTime));
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->setCenterButtons(true);
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消"));
// 布局
formLayout->setSpacing(6);
formLayout->setContentsMargins(11, 11, 11, 11);
formLayout->setWidget(0, QFormLayout::LabelRole, label_step);
formLayout->setWidget(0, QFormLayout::FieldRole, comboBox_step);
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();
int dTime = comboBox_time->currentData().toInt();
if (gStep != dStep || gTime != dTime) {
// 重置游戏规则
game->setRule(ruleNo, dStep, dTime);
}
}
// 删除对话框,子控件会一并删除
dialog->disconnect();
delete dialog;
// 更新规则显示
ruleInfo();
}
void NineChessWindow::actionRules_triggered()
{
// 取消其它规则的选择
@ -242,9 +318,14 @@ void NineChessWindow::actionRules_triggered()
action->setChecked(true);
ruleNo = action->data().toInt();
// 如果游戏规则没变化,则返回
if (ruleNo == game->getRuleNo())
return;
// 取消AI设定
ui.actionEngine1_T->setChecked(false);
ui.actionEngine2_R->setChecked(false);
// 重置游戏规则
game->setRule(ruleNo);
// 更新规则显示
@ -255,6 +336,10 @@ void NineChessWindow::on_actionNew_N_triggered()
{
if (file.isOpen())
file.close();
// 取消AI设定
ui.actionEngine1_T->setChecked(false);
ui.actionEngine2_R->setChecked(false);
// 重置游戏规则
game->gameReset();
}
@ -280,7 +365,10 @@ void NineChessWindow::on_actionOpen_O_triggered()
bool isok = file.open(QFileDevice::ReadOnly | QFileDevice::Text);
if (isok)
{
//读文件
// 取消AI设定
ui.actionEngine1_T->setChecked(false);
ui.actionEngine2_R->setChecked(false);
// 读文件
QTextStream textStream(&file);
QString cmd;
cmd = textStream.readLine();
@ -520,84 +608,6 @@ void NineChessWindow::on_actionAutoRun_A_toggled(bool arg1)
ui.actionAutoRun_A->setChecked(false);
}
void NineChessWindow::on_actionLimited_T_triggered()
{
/* 其实本来可以用设计器做个ui然后从QDialog派生个自己的对话框
*
*
* QDialog界面
*/
int stepLimited = game->getStepsLimit();
int timeLimited = game->getTimeLimit();
// 定义新对话框
QDialog *dialog = new QDialog(this);
dialog->setWindowFlags(Qt::Dialog | Qt::WindowCloseButtonHint);
dialog->setObjectName(QStringLiteral("Dialog"));
dialog->setWindowTitle(tr("步数和时间限制"));
dialog->resize(256, 108);
dialog->setModal(true);
// 生成各个控件
QFormLayout *formLayout = new QFormLayout(dialog);
QLabel *label_step = new QLabel(dialog);
QLabel *label_time = new QLabel(dialog);
QComboBox *comboBox_step = new QComboBox(dialog);
QComboBox *comboBox_time = new QComboBox(dialog);
QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
// 设置各个控件ObjectName不设也没关系
/*formLayout->setObjectName(QStringLiteral("formLayout"));
label_step->setObjectName(QStringLiteral("label_step"));
label_time->setObjectName(QStringLiteral("label_time"));
comboBox_step->setObjectName(QStringLiteral("comboBox_step"));
comboBox_time->setObjectName(QStringLiteral("comboBox_time"));
buttonBox->setObjectName(QStringLiteral("buttonBox"));*/
// 设置各个控件数据
label_step->setText(tr("超出限制步数判和:"));
label_time->setText(tr("任意一方超时判负:"));
comboBox_step->addItem(tr("无限制"), 0);
comboBox_step->addItem(tr("50步"), 50);
comboBox_step->addItem(tr("100步"), 100);
comboBox_step->addItem(tr("200步"), 200);
comboBox_time->addItem(tr("无限制"), 0);
comboBox_time->addItem(tr("5分钟"), 5);
comboBox_time->addItem(tr("10分钟"), 10);
comboBox_time->addItem(tr("20分钟"), 20);
comboBox_step->setCurrentIndex(comboBox_step->findData(stepLimited));
comboBox_time->setCurrentIndex(comboBox_time->findData(timeLimited));
buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
buttonBox->setCenterButtons(true);
buttonBox->button(QDialogButtonBox::Ok)->setText(tr("确定"));
buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("取消"));
// 布局
formLayout->setSpacing(6);
formLayout->setContentsMargins(11, 11, 11, 11);
formLayout->setWidget(0, QFormLayout::LabelRole, label_step);
formLayout->setWidget(0, QFormLayout::FieldRole, comboBox_step);
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) {
stepLimited = comboBox_step->currentData().toInt();
timeLimited = comboBox_time->currentData().toInt();
// 选择当前规则
QAction *action = dynamic_cast<QAction *>(sender());
action->setChecked(true);
int ruleNo = action->data().toInt();
// 重置游戏规则
game->setRule(ruleNo, stepLimited, timeLimited);
}
// 删除对话框,子控件会一并删除
delete dialog;
// 更新规则显示
ruleInfo();
}
void NineChessWindow::on_actionLocal_L_triggered()
{
ui.actionLocal_L->setChecked(true);