test: 新增测试模块用于本地对战

使用共享内存实现,效率比 socket 高。
打开 TEST_MODE 宏才能生效。
This commit is contained in:
Calcitem 2019-12-07 16:09:24 +08:00
parent be8b1d4baa
commit 3182eff1ad
9 changed files with 239 additions and 3 deletions

View File

@ -30,6 +30,8 @@
//#define TRAINING_MODE
//#define TEST_MODE
//#define DEBUG_MODE
//#define DEBUG_MODE_A

View File

@ -19,6 +19,7 @@ INCLUDEPATH += include
INCLUDEPATH += src/base
INCLUDEPATH += src/ai
INCLUDEPATH += src/game
INCLUDEPATH += src/test
INCLUDEPATH += src/ui/qt
SOURCES += \
@ -39,6 +40,7 @@ SOURCES += \
src/main.cpp \
src/base/aithread.cpp \
src/ai/search.cpp \
src/test/test.cpp \
src/ui/qt/gamewindow.cpp \
src/ui/qt/pieceitem.cpp \
src/ui/qt/server.cpp \
@ -75,6 +77,7 @@ HEADERS += \
src/game/position.h \
src/game/rule.h \
src/game/types.h \
src/test/test.h \
src/ui/qt/client.h \
src/ui/qt/gamecontroller.h \
src/ui/qt/gamescene.h \

View File

@ -106,10 +106,10 @@
<IgnoreImportLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</IgnoreImportLibrary>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(ProjectDir)\src\base;$(ProjectDir)\include;$(ProjectDir)\src\ai;$(ProjectDir)\src\game;$(ProjectDir)\src\ui\qt;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir)\src\base;$(ProjectDir)\include;$(ProjectDir)\src\ai;$(ProjectDir)\src\game;$(ProjectDir)\src\ui\qt;$(ProjectDir)\src\test;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(ProjectDir)\src\ui\qt;$(ProjectDir)\src\game;$(ProjectDir)\src\ai;$(ProjectDir)\src\base;$(ProjectDir)\include;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir)\src\ui\qt;$(ProjectDir)\src\game;$(ProjectDir)\src\ai;$(ProjectDir)\src\base;$(ProjectDir)\include;$(ProjectDir)\src\test;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
@ -466,6 +466,7 @@
<ClInclude Include="src\game\position.h" />
<ClInclude Include="src\game\rule.h" />
<ClInclude Include="src\game\types.h" />
<QtMoc Include="src\test\test.h" />
<ClInclude Include="src\ui\qt\boarditem.h" />
<QtMoc Include="src\ui\qt\client.h" />
<QtMoc Include="src\ui\qt\gamecontroller.h" />
@ -717,6 +718,7 @@
<ClCompile Include="src\game\position.cpp" />
<ClCompile Include="src\game\rule.cpp" />
<ClCompile Include="src\main.cpp" />
<ClCompile Include="src\test\test.cpp" />
<ClCompile Include="src\ui\qt\boarditem.cpp" />
<ClCompile Include="src\ui\qt\client.cpp" />
<ClCompile Include="src\ui\qt\gamecontroller.cpp" />

View File

@ -52,6 +52,9 @@
<Filter Include="base">
<UniqueIdentifier>{c5349658-6cbb-4076-8529-29de7092f9d8}</UniqueIdentifier>
</Filter>
<Filter Include="test">
<UniqueIdentifier>{944f132b-1dc6-4222-a345-79e3d2c821e7}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">
@ -294,6 +297,9 @@
<QtMoc Include="src\base\aithread.h">
<Filter>base</Filter>
</QtMoc>
<QtMoc Include="src\test\test.h">
<Filter>test</Filter>
</QtMoc>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\main.cpp">
@ -374,6 +380,9 @@
<ClCompile Include="src\base\thread.cpp">
<Filter>base</Filter>
</ClCompile>
<ClCompile Include="src\test\test.cpp">
<Filter>test</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="millgame.rc">

View File

@ -28,6 +28,7 @@
#include "search.h"
#include "server.h"
#include "client.h"
#include "test.h"
class AiThread : public QThread
{

121
src/test/test.cpp Normal file
View File

@ -0,0 +1,121 @@
/*
Sanmill, a mill game playing engine derived from NineChess 1.5
Copyright (C) 2015-2018 liuweilhy (NineChess author)
Copyright (C) 2019 Calcitem <calcitem@outlook.com>
Sanmill is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Sanmill is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#ifdef TEST_MODE
#include <QBuffer>
#include <QUuid>
#include <QDataStream>
#include <QString>
#include <QThread>
#include <random>
#include "test.h"
Test::Test()
{
sharedMemory.setKey("MillGameSharedMemory");
if (sharedMemory.attach()) {
loggerDebug("Attached shared memory segment.\n");
} else {
if (sharedMemory.create(SHARED_MEMORY_SIZE)) {
loggerDebug("Created shared memory segment.\n");
} else {
loggerDebug("Unable to create shared memory segment.\n");
}
}
to = (char *)sharedMemory.data();
uuid = createUuidString();
uuidSize = uuid.size();
assert(uuidSize == 38);
}
Test::~Test()
{
detach();
}
void Test::writeToMemory(const QString &cmdline)
{
if (cmdline == readStr) {
return;
}
char from[128] = { 0 };
strcpy(from, cmdline.toStdString().c_str());
while (true) {
sharedMemory.lock();
if (to[0] != 0) {
sharedMemory.unlock();
QThread::msleep(100);
continue;
}
memset(to, 0, SHARED_MEMORY_SIZE);
memcpy(to, uuid.toStdString().c_str(), uuidSize);
memcpy(to + uuidSize, from, strlen(from));
sharedMemory.unlock();
break;
}
}
void Test::readFromMemory()
{
sharedMemory.lock();
QString str = to;
sharedMemory.unlock();
if (str.size() == 0) {
return;
}
if (!(str.mid(0, uuidSize) == uuid)) {
str = str.mid(uuidSize);
if (str.size()) {
sharedMemory.lock();
memset(to, 0, SHARED_MEMORY_SIZE);
sharedMemory.unlock();
readStr = str;
emit command(str);
}
}
}
void Test::detach()
{
if (sharedMemory.isAttached()) {
sharedMemory.detach();
}
}
QString Test::createUuidString()
{
return QUuid::createUuid().toString();
}
#endif // TEST_MODE

61
src/test/test.h Normal file
View File

@ -0,0 +1,61 @@
/*
Sanmill, a mill game playing engine derived from NineChess 1.5
Copyright (C) 2015-2018 liuweilhy (NineChess author)
Copyright (C) 2019 Calcitem <calcitem@outlook.com>
Sanmill is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Sanmill is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TEST_H
#define TEST_H
#include "config.h"
#ifdef TEST_MODE
#include <QObject>
#include <QSharedMemory>
#include <QString>
#include <QBuffer>
class Test : public QObject
{
Q_OBJECT
public:
Test();
~Test();
signals:
void command(const QString &cmd, bool update = true);
public slots:
void writeToMemory(const QString &str);
void readFromMemory();
private:
void detach();
QString createUuidString();
private:
const int SHARED_MEMORY_SIZE = 4096;
QSharedMemory sharedMemory;
QString uuid;
int uuidSize;
char *to { nullptr };
QString readStr;
};
#endif // TEST_MODE
#endif // TEST_H

View File

@ -76,12 +76,25 @@ GameController::GameController(
gameReset();
#ifdef TEST_MODE
gameTest = new Test();
readMemoryTimer = new QTimer(this);
connect(readMemoryTimer, SIGNAL(timeout()), this, SLOT(onTimeOut()));
readMemoryTimer->start(100);
#endif // TEST_MODE
// 关联AI和控制器的着法命令行
connect(aiThread[BLACK], SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
connect(aiThread[WHITE], SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
#ifdef TEST_MODE
connect(this->gameTest, SIGNAL(command(const QString &, bool)),
this, SLOT(command(const QString &, bool)));
#endif
#ifndef TRAINING_MODE
// 关联AI和网络类的着法命令行
connect(aiThread[BLACK]->getClient(), SIGNAL(command(const QString &, bool)),
@ -110,6 +123,13 @@ GameController::~GameController()
#endif /* ENDGAME_LEARNING */
}
#ifdef TEST_MODE
void GameController::onTimeOut()
{
gameTest->readFromMemory();
}
#endif // TEST_MODE
const map<int, QStringList> GameController::getActions()
{
// 主窗口更新菜单栏
@ -926,12 +946,16 @@ bool GameController::command(const QString &cmd, bool update /* = true */)
#endif
}
#ifdef TEST_MODE
gameTest->writeToMemory(cmd);
#endif // TEST_MODE
#ifndef TRAINING_MODE
// 网络: 将着法放到服务器的发送列表中
if (isAiPlayer[BLACK]) {
aiThread[BLACK]->getServer()->setAction(cmd);
} else if (isAiPlayer[WHITE]) {
aiThread[BLACK]->getServer()->setAction(cmd); // 注意: 同样是AI1
aiThread[BLACK]->getServer()->setAction(cmd); // 注意: 同样是 aiThread[BLACK]
}
#endif // TRAINING_MODE

View File

@ -274,6 +274,11 @@ public slots:
// 显示网络配置窗口
void showNetworkWindow();
#ifdef TEST_MODE
// 定时器超时
void onTimeOut();
#endif // TEST_MODE
protected:
//bool eventFilter(QObject * watched, QEvent * event);
// 定时器
@ -286,6 +291,11 @@ private:
// 棋对象的数据模型(临时)
Game tempGame;
#ifdef TEST_MODE
// 测试
Test *gameTest;
#endif // TEST_MODE
private:
// 2个AI的线程
AiThread *aiThread[COLOR_COUNT];
@ -365,6 +375,9 @@ private:
// 棋谱字符串列表模型
QStringListModel manualListModel;
// 读取共享内存的定时器
QTimer *readMemoryTimer;
};
#endif // GAMECONTROLLER_H