-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver_test.cpp
More file actions
96 lines (79 loc) · 3.43 KB
/
Copy pathserver_test.cpp
File metadata and controls
96 lines (79 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
// 服务器端到端自测:内存服务器 + 两个 TCP 客户端,用镜像 AI 决策驱动一整局到 game_over。
#include "gameserver.h"
#include <QCoreApplication>
#include <QEventLoop>
#include <QHostAddress>
#include <QJsonDocument>
#include <QJsonObject>
#include <QTimer>
#include <QTcpSocket>
#include <cstdio>
static void wait(int ms)
{
QEventLoop loop;
QTimer::singleShot(ms, &loop, &QEventLoop::quit);
loop.exec();
}
static QList<QJsonObject> takeLines(QByteArray& buf)
{
QList<QJsonObject> out;
int idx;
while ((idx = buf.indexOf('\n')) >= 0) {
const QByteArray line = buf.left(idx).trimmed();
buf.remove(0, idx + 1);
if (!line.isEmpty())
out.append(QJsonDocument::fromJson(line).object());
}
return out;
}
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
GameServer server;
if (!server.listen(0)) { printf("FAIL: listen\n"); return 1; }
QTcpSocket a, b;
a.connectToHost(QHostAddress::LocalHost, server.port());
b.connectToHost(QHostAddress::LocalHost, server.port());
wait(200);
QByteArray bufA, bufB;
QObject::connect(&a, &QTcpSocket::readyRead, [&]() { bufA += a.readAll(); });
QObject::connect(&b, &QTcpSocket::readyRead, [&]() { bufB += b.readAll(); });
a.write("{\"type\":\"queue\"}\n");
b.write("{\"type\":\"queue\"}\n");
wait(300);
QJsonObject matchedA, matchedB;
for (const QJsonObject& m : takeLines(bufA)) if (m[QStringLiteral("type")] == QLatin1String("matched")) matchedA = m;
for (const QJsonObject& m : takeLines(bufB)) if (m[QStringLiteral("type")] == QLatin1String("matched")) matchedB = m;
if (matchedA.isEmpty() || matchedB.isEmpty()) { printf("FAIL: no matched\n"); return 1; }
const int youA = matchedA[QStringLiteral("you")].toInt();
const int youB = matchedB[QStringLiteral("you")].toInt();
const quint32 seed = quint32(matchedA[QStringLiteral("seed")].toDouble());
if (quint32(matchedB[QStringLiteral("seed")].toDouble()) != seed) { printf("FAIL: seed mismatch\n"); return 1; }
// 本地镜像(同种子),用确定性 AI 决策驱动,动作与服务端保持同步
GameModel mirror;
mirror.setSeed(seed);
mirror.newGame();
int steps = 0;
while (mirror.winner() == -1 && steps++ < 500) {
const int cur = mirror.currentPlayer();
mirror.aiPlay();
const QString action = mirror.log().last();
QTcpSocket* s = (youA == cur) ? &a : &b;
QJsonObject payload;
payload[QStringLiteral("action")] = action;
QJsonObject msg;
msg[QStringLiteral("type")] = QStringLiteral("action");
msg[QStringLiteral("payload")] = payload;
s->write(QJsonDocument(msg).toJson(QJsonDocument::Compact) + '\n');
wait(60);
}
if (mirror.winner() == -1) { printf("FAIL: mirror no winner\n"); return 1; }
bool overA = false, overB = false;
for (const QJsonObject& m : takeLines(bufA)) if (m[QStringLiteral("type")] == QLatin1String("game_over")) overA = true;
for (const QJsonObject& m : takeLines(bufB)) if (m[QStringLiteral("type")] == QLatin1String("game_over")) overB = true;
if (!overA || !overB) { printf("FAIL: missing game_over (A=%d B=%d)\n", overA, overB); return 1; }
wait(100);
if (server.activeRooms() != 0) { printf("FAIL: rooms not cleaned\n"); return 1; }
printf("SERVER TEST OK (winner=%d, steps=%d)\n", mirror.winner(), steps);
return 0;
}