添加命令行解析和生成功能

This commit is contained in:
liuweilhy 2018-06-10 01:47:32 +08:00
parent 47aca2dfb7
commit b95e6b276b
8 changed files with 209 additions and 90 deletions

View File

@ -1,10 +1,10 @@
本软件代码遵循LGPL协议其中
本软件代码遵循LGPL v3协议,其中:
1. 对于Qt库遵循GPL协议。任何人都可以免费使用但必须保持其开源
2. 对于原作者自行开发的代码:
2.1 未经原作者书面授权,任何人不得修改、传播;
2.2 获得原作者书面授权免费使用者:
2.2.1 其修改版必须对原作者开源;
2.2.2 其修改版必须完整保留本Licence文件如需新增内容只能在原版Licence文件末尾追加;
2.2.2 其修改版必须完整保留本Licence文件如需新增内容只能在原版Licence文件追加;
2.2.3 原作者对其修改版在保留修改者署名情况下,有修改、传播及无偿商业化使用的权利;
2.3 获得原作者书面授权收费使用者,按照双方书面协议执行。

View File

@ -109,7 +109,7 @@
<QtMoc>
<QTDIR>$(QTDIR)</QTDIR>
<OutputFile>.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</OutputFile>
<Define>UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB</Define>
<Define>_WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_NO_DEBUG;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;NDEBUG;%(PreprocessorDefinitions)</Define>
<CompilerFlavor>msvc</CompilerFlavor>
<Include>$(Configuration)/moc_predefs.h</Include>
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>
@ -161,7 +161,7 @@
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<OutputFile>$(OutDir)\NineChess.exe</OutputFile>
<RandomizedBaseAddress>true</RandomizedBaseAddress>
<SubSystem>Windows</SubSystem>
<SubSystem>Console</SubSystem>
<SuppressStartupBanner>true</SuppressStartupBanner>
</Link>
<Midl>
@ -183,7 +183,7 @@
<QtMoc>
<QTDIR>$(QTDIR)</QTDIR>
<OutputFile>.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</OutputFile>
<Define>UNICODE;_UNICODE;WIN32;WIN64;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB</Define>
<Define>_WINDOWS;UNICODE;_UNICODE;WIN32;WIN64;QT_MULTIMEDIA_LIB;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_CORE_LIB;%(PreprocessorDefinitions)</Define>
<CompilerFlavor>msvc</CompilerFlavor>
<Include>$(Configuration)/moc_predefs.h</Include>
<ExecutionDescription>Moc'ing %(Identity)...</ExecutionDescription>

View File

@ -69,8 +69,11 @@ const QMap<int, QStringList> GameController::getActions()
void GameController::gameStart()
{
chess.start();
// 每隔100毫秒调用一次定时器处理函数
timeID = startTimer(100);
if (timeID == 0) {
timeID = startTimer(100);
}
}
void GameController::gameReset()
@ -147,14 +150,11 @@ void GameController::setRule(int ruleNo)
timeID = 0;
// 更新规则,原限时和限步不变
struct NineChess::Rule rule;
if (ruleNo >= 0 && ruleNo < NineChess::RULENUM)
rule = NineChess::RULES[ruleNo];
rule.maxSteps = stepsLimit;
rule.maxTime = timeLimit;
// 更新规则,原限时和限步不变
if (ruleNo < 0 || ruleNo >= NineChess::RULENUM)
return;
// 设置模型规则,重置游戏
chess.setRule(&rule);
chess.setData(&NineChess::RULES[ruleNo], stepsLimit, timeLimit);
// 清除棋子
qDeleteAll(pieceList);
@ -193,13 +193,12 @@ void GameController::setRule(int ruleNo, int stepLimited, int timeLimited)
timeID = 0;
// 更新规则,原限时和限步不变
struct NineChess::Rule rule;
if (ruleNo >= 0 && ruleNo < NineChess::RULENUM)
rule = NineChess::RULES[ruleNo];
stepsLimit = rule.maxSteps = stepLimited;
timeLimit = rule.maxTime = timeLimited;
if (ruleNo < 0 || ruleNo >= NineChess::RULENUM)
return;
stepsLimit = stepLimited;
timeLimit = timeLimited;
// 设置模型规则,重置游戏
chess.setRule(&rule);
chess.setData(&NineChess::RULES[ruleNo], stepsLimit, timeLimit);
// 清除棋子
qDeleteAll(pieceList);
@ -329,7 +328,10 @@ bool GameController::actionPiece(QPointF pos)
case NineChess::GAME_NOTSTARTED:
// 如果未开局则开局这里还要继续判断不可break
gameStart();
chess.start();
manualListModel.setStringList(QStringList());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), chess.getCmdLine());
case NineChess::GAME_OPENING:
// 如果是开局阶段(轮流落下新子),落子
if (chess.getAction() == NineChess::ACTION_PLACE) {
@ -342,6 +344,7 @@ bool GameController::actionPiece(QPointF pos)
if (chess.getPhase() == NineChess::GAME_MID && chess.getRule()->hasForbidden)
cleanForbidden();
break;
case NineChess::GAME_MID:
// 如果是中局阶段(轮流移子)
// 选子
@ -357,15 +360,28 @@ bool GameController::actionPiece(QPointF pos)
result = removePiece(pos);
}
break;
// 如果是结局状态,不做任何响应
default:
// 如果是结局状态,不做任何响应
break;
}
if (result)
{
if (chess.whoWin() != NineChess::NOBODY)
manualListModel.insertRow(manualListModel.rowCount());
int row = manualListModel.rowCount();
manualListModel.setData(manualListModel.index(row-1), chess.getCmdLine());
if (chess.whoWin() != NineChess::NOBODY) {
// 取胜时会连续出2个命令行需要将前一个插入
if (row < chess.getCmdList()->size()) {
manualListModel.insertRow(row-1);
auto i = (chess.getCmdList())->rbegin();
manualListModel.setData(manualListModel.index(row - 1), (*(++i)).c_str());
}
playSound(soundWin);
}
}
return result;
}

View File

@ -12,6 +12,8 @@
#include <QPointF>
#include <QMap>
#include <QList>
#include <QTextStream>
#include <QStringListModel>
#include "gamescene.h"
#include "ninechess.h"
@ -28,6 +30,10 @@ public:
const QMap <int, QStringList> getActions();
int getTimeLimit() { return timeLimit; }
int getStepsLimit() { return stepsLimit; }
// 文本流
QTextStream textStream;
// 棋谱字符串列表模型
QStringListModel manualListModel;
signals:
// 玩家1(先手)用时改变的信号
@ -112,8 +118,7 @@ private:
int time2;
// 用于主窗口状态栏显示的字符串
QString message;
// 文字棋谱
QStringList manual;
// 各个音效文件路径
QString soundNewgame;
QString soundChoose;

View File

@ -117,11 +117,17 @@ NineChess::~NineChess()
{
}
bool NineChess::setData(const struct Rule *rule, int step, int flags, const char *boardsource,
bool NineChess::setData(const struct Rule *rule, int s, int t, int step, int flags, const char *boardsource,
int p1_InHand, int p2_InHand, int num_NeedRemove)
{
// 有效性判断
if (s < 0 || t < 0 || step < 0 || p1_InHand < 0 || p2_InHand < 0 || num_NeedRemove < 0)
return false;
// 根据规则
this->rule = *rule;
this->rule.maxSteps = s;
this->rule.maxTime = t;
// 设置步数
this->step = step;
// 设置状态
@ -286,13 +292,10 @@ bool NineChess::setData(const struct Rule *rule, int step, int flags, const char
// 用时置零
player1_MS = player2_MS = 0;
setTip();
return true;
}
bool NineChess::setRule(const struct Rule *rule)
{
setData(rule);
// 提示
setTip();
return true;
}
@ -315,15 +318,38 @@ bool NineChess::reset()
bool NineChess::start()
{
// 如果游戏已经开始则返回false
if (phase == GAME_OPENING || phase == GAME_MID)
return false;
// 如果游戏结束,则重置游戏,进入未开始状态
if (phase == GAME_OVER)
reset();
update();
if (phase == GAME_NOTSTARTED)
// 如果游戏处于未开始状态
if (phase == GAME_NOTSTARTED) {
phase = GAME_OPENING;
ftime(&startTimeb);
// 用时置零
player1_MS = player2_MS = 0;
return true;
// 启动计时器
ftime(&startTimeb);
// 计棋谱
cmdlist.clear();
int i;
for (i = 0; i < RULENUM; i++) {
if (strcmp(rule.name, RULES[i].name) == 0)
break;
}
if (sprintf(cmdline, "r%1d s%03d t%02d", i + 1, rule.maxSteps, rule.maxTime) > 0) {
cmdlist.push_back(string(cmdline));
return true;
}
else {
cmdline[0] = '\0';
return false;
}
}
// 其它情况
return false;
}
int NineChess::cp2pos(int c, int p)
@ -366,6 +392,8 @@ bool NineChess::place(int c, int p)
player2_InHand--;
player2_Remain++;
}
sprintf(cmdline, "(%1d,%1d)", c, p);
cmdlist.push_back(string(cmdline));
posOfSelected = pos;
step++;
// 如果决出胜负
@ -431,11 +459,14 @@ bool NineChess::place(int c, int p)
return false;
}
// 移子
sprintf(cmdline, "(%1d,%1d)->(%1d,%1d)", posOfSelected / RING, posOfSelected % RING + 1, c, p);
cmdlist.push_back(string(cmdline));
board[pos] = board[posOfSelected];
board[posOfSelected] = '\x00';
posOfSelected = pos;
step++;
n = addMills(posOfSelected);
// 中局阶段未成三
if (n == 0) {
// 进入选子状态
@ -497,6 +528,8 @@ bool NineChess::remove(int c, int p)
player2_Remain--;
else if (turn == PLAYER2)
player1_Remain--;
sprintf(cmdline, "-(%1d,%1d)", c, p);
cmdlist.push_back(string(cmdline));
posOfSelected = 0;
num_NeedRemove--;
step++;
@ -597,6 +630,40 @@ bool NineChess::choose(int c, int p)
return false;
}
// 打算用个C++的命令行解析库的,简单的没必要,但中文编码有极小
bool NineChess::command(const char *cmd)
{
int r, s, t;
int c1, p1, c2, p2;
// 设置规则
if (sscanf(cmd, "r%d s%d t%d", &r, &s, &t) == 3) {
if (r <= 0 || r > RULENUM)
return false;
return setData(&NineChess::RULES[r - 1], s, t);
}
// 选子移动
else if (sscanf(cmd, "(%d,%d)->(%d,%d)", &c1, &p1, &c2, &p2) == 4) {
if (choose(c1, p1))
return place(c2, p2);
else
return false;
}
// 去子
else if (sscanf(cmd, "-(%d,%d)", &c1, &p1) == 2) {
return remove(c1, p1);
}
// 落子
else if (sscanf(cmd, "(%d,%d)", &c1, &p1) == 2) {
return place(c1, p1);
}
return false;
}
inline bool NineChess::update()
{
// 根据局面调整计时器
@ -605,7 +672,7 @@ inline bool NineChess::update()
case NineChess::GAME_OPENING:
case NineChess::GAME_MID:
ftime(&currentTimeb);
// 在时间更新时增添一套胜负判断的代码以免频繁调用win()带来的效率问题
// 超时胜负判断
if (turn == PLAYER1) {
player1_MS = (long)(currentTimeb.time - startTimeb.time) * 1000
+ (currentTimeb.millitm - startTimeb.millitm) - player2_MS;
@ -650,8 +717,11 @@ bool NineChess::win()
if (rule.maxTime > 0) {
// 更新时间,判断胜负
update();
if (winner != NOBODY)
if (winner != NOBODY) {
sprintf(cmdline, "Time over. Player%1d win!", winner == PLAYER1 ? 1 : 2);
cmdlist.push_back(string(cmdline));
return true;
}
}
// 如果有步数限定
@ -659,6 +729,8 @@ bool NineChess::win()
if (step > rule.maxSteps) {
winner = DRAW;
phase = GAME_OVER;
sprintf(cmdline, "Steps over. In draw!");
cmdlist.push_back(string(cmdline));
return true;
}
}
@ -667,12 +739,16 @@ bool NineChess::win()
if (player1_Remain + player1_InHand < rule.numAtLest) {
winner = PLAYER2;
phase = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果玩家2子数小于赛点则玩家1获胜
else if (player2_Remain + player2_InHand < rule.numAtLest) {
winner = PLAYER1;
phase = GAME_OVER;
sprintf(cmdline, "Player1 win!");
cmdlist.push_back(string(cmdline));
return true;
}
// 如果摆满了,根据规则判断胜负
@ -680,12 +756,16 @@ bool NineChess::win()
if (rule.isFullLose) {
winner = PLAYER2;
phase = GAME_OVER;
sprintf(cmdline, "Player2 win!");
cmdlist.push_back(string(cmdline));
return true;
}
else
{
winner = DRAW;
phase = GAME_OVER;
sprintf(cmdline, "Full. In draw!");
cmdlist.push_back(string(cmdline));
return true;
}
}
@ -696,6 +776,8 @@ bool NineChess::win()
if (rule.isNoWayLose) {
winner = (turn == PLAYER1) ? PLAYER2 : PLAYER1;
phase = GAME_OVER;
sprintf(cmdline, "Surrounded. Player%1d win!", winner == PLAYER1 ? 1 : 2);
cmdlist.push_back(string(cmdline));
return true;
}
// 否则让棋,由对手走
@ -709,16 +791,6 @@ bool NineChess::win()
return false;
}
bool NineChess::command(const char *cmd)
{
return true;
}
void NineChess::setCmdline()
{
}
int NineChess::isInMills(int pos)
{
int n = 0;

View File

@ -59,6 +59,7 @@ public:
GAME_MID = 0x00000004, // 中局(走棋)
GAME_OVER = 0x00000008 // 结局
};
// 玩家标识,轮流状态,胜负标识
enum Player {
PLAYER1 = 0x00000010, // 玩家1
@ -66,6 +67,7 @@ public:
DRAW = 0x00000040, // 双方和棋
NOBODY = 0x00000080 // 胜负未分
};
// 动作状态标识
enum Actions {
ACTION_CHOOSE = 0x00000100, // 选子
@ -88,15 +90,16 @@ public:
~NineChess();
// 设置棋局状态和棋盘数据,用于初始化
bool setData(const struct Rule *rule,
int step = 0, // 默认起始步数为0
int flags = GAME_NOTSTARTED | PLAYER1 | ACTION_PLACE | NOBODY, // 默认状态
const char *boardsource = NULL, // 默认空棋盘
int p1_InHand = 12, // 玩家1剩余未放置子数
int p2_InHand = 12, // 玩家2剩余未放置子数
int num_NeedRemove = 0 // 尚待去除的子数
);
// 设置规则
bool setRule(const struct Rule *rule);
int s = 0, // 限制步数
int t = 0, // 限制时间
int step = 0, // 默认起始步数为0
int flags = GAME_NOTSTARTED | PLAYER1 | ACTION_PLACE | NOBODY, // 默认状态
const char *boardsource = NULL, // 默认空棋盘
int p1_InHand = 12, // 玩家1剩余未放置子数
int p2_InHand = 12, // 玩家2剩余未放置子数
int num_NeedRemove = 0 // 尚待去除的子数
);
// 获取棋局状态和棋盘数据
void getData(struct Rule &rule, int &step, int &flags, const char *&boardsource, int &p1_InHand, int &p2_InHand, int &num_NeedRemove);
// 获取当前规则
@ -111,14 +114,18 @@ public:
enum Actions getAction() { return action; }
// 判断胜负
enum Player whoWin() { return winner; }
// 获取当前招法
const char *getCmdline() { return cmdline; }
// 玩家1和玩家2的用时
void getPlayer_TimeMS(int &p1_ms, int &p2_ms);
// 获取棋局的字符提示
const string getTip() { return tip; }
// 获取位置点棋子的归属人
enum Player getWhosPiece(int c, int p);
// 获取位置点棋子的序号
int getPieceNum(int c, int p);
// 获取当前招法
const char *getCmdLine() { return cmdline; }
// 获得棋谱
const list<string> * getCmdList() { return &cmdlist; }
// 玩家1剩余未放置子数
int getPlayer1_InHand() { return player1_InHand; }
@ -130,10 +137,8 @@ public:
int getPlayer2_Remain() { return player2_Remain; }
// 尚待去除的子数
int getNum_NeedRemove() { return num_NeedRemove; }
// 玩家1和玩家2的用时
void getPlayer_TimeMS(int &p1_ms, int &p2_ms);
// 游戏清空
// 游戏重置
bool reset();
// 游戏开始
bool start();
@ -147,8 +152,6 @@ public:
bool command(const char *cmd);
protected:
// 设置当前招法的命令行指令,即一招棋谱
void setCmdline();
// 判断棋盘pos处的棋子处于几个“三连”中
int isInMills(int pos);
// 判断玩家的所有棋子是否都处于“三连”状态
@ -214,15 +217,15 @@ private:
* (board[i] & 0x10)
* (board[i] & 0x20)
*/
char board[(RING+2)*SEAT];
char board[(RING + 2)*SEAT];
// 选中的棋子在board中的位置
int posOfSelected;
// 空棋盘点位,用于判断一个棋子位置是否在棋盘上
static const char inBoard[(RING+2)*SEAT];
static const char inBoard[(RING + 2)*SEAT];
// 招法表每个位置有最多4种走法顺时针、逆时针、向内、向外
static char moveTable[(RING+2)*SEAT][4];
static char moveTable[(RING + 2)*SEAT][4];
// 成三表,表示棋盘上各个位置有成三关系的对应位置表
static char millTable[(RING+2)*SEAT][3][2];
static char millTable[(RING + 2)*SEAT][3][2];
/* 本打算用如下的结构体来表示“三连”
struct Mill {
@ -242,10 +245,10 @@ private:
// 当前招法的命令行指令,即一招棋谱
char cmdline[32];
// 棋谱
list <string> cmdlist;
// 当前棋局的字符提示
string tip;
// 招法不用结构体表示了用int表示=原位置*256+新位置落子没有原位置的计为0xff
short move;
};
#endif

View File

@ -9,6 +9,7 @@
#include <QMessageBox>
#include <QTimer>
#include <QDialog>
#include <QFileDialog>
#include <QButtonGroup>
#include <QPushButton>
#include <QComboBox>
@ -35,15 +36,28 @@ NineChessWindow::NineChessWindow(QWidget *parent)
// 设置场景
scene = new GameScene(this);
// 初始化各个控件
// 关联视图和场景
ui.gameView->setScene(scene);
// 视图反走样
ui.gameView->setRenderHint(QPainter::Antialiasing, true);
// 初始化各个控件
// 视图反锯齿
ui.gameView->setRenderHint(QPainter::Antialiasing);
// 因功能限制,使部分功能不可用
ui.actionViewText_V->setDisabled(true);
ui.actionPrevious_B->setDisabled(true);
ui.actionNext_F->setDisabled(true);
ui.actionEnd_E->setDisabled(true);
ui.actionAutoRun_A->setDisabled(true);
ui.actionEngine_E->setDisabled(true);
ui.actionInternet_I->setDisabled(true);
ui.actionEngine1_T->setDisabled(true);
ui.actionEngine2_R->setDisabled(true);
ui.actionSetting_O->setDisabled(true);
ui.actionAnimation_A->setDisabled(true);
// 关联既有动作信号和主窗口槽
// 视图上下翻转
connect(ui.actionFlip_F, &QAction::triggered,
@ -58,23 +72,6 @@ NineChessWindow::NineChessWindow(QWidget *parent)
connect(ui.actionTurnLeftt_L, &QAction::triggered,
ui.gameView, &GameView::turnLeft);
// 因功能限制,使部分功能不可用
ui.actionNew_N->setDisabled(true);
ui.actionOpen_O->setDisabled(true);
ui.actionSave_S->setDisabled(true);
ui.actionSaveAs_A->setDisabled(true);
ui.actionViewText_V->setDisabled(true);
ui.actionPrevious_B->setDisabled(true);
ui.actionNext_F->setDisabled(true);
ui.actionEnd_E->setDisabled(true);
ui.actionAutoRun_A->setDisabled(true);
ui.actionEngine_E->setDisabled(true);
ui.actionInternet_I->setDisabled(true);
ui.actionEngine1_T->setDisabled(true);
ui.actionEngine2_R->setDisabled(true);
ui.actionSetting_O->setDisabled(true);
ui.actionAnimation_A->setDisabled(true);
// 初始化游戏规则菜单
ui.menu_R->installEventFilter(this);
// 安装一次性定时器,执行初始化
@ -86,6 +83,7 @@ NineChessWindow::~NineChessWindow()
if (game != NULL)
delete game;
qDeleteAll(ruleActionList);
;
}
bool NineChessWindow::eventFilter(QObject *watched, QEvent *event)
@ -180,6 +178,9 @@ void NineChessWindow::initialize()
QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name))
.arg(tr(NineChess::RULES[ruleNo].info));
ui.tab_Rule->setPlainText(tip_Rule);
// 关联列表视图和字符串列表模型
ui.tab_Manual->setModel(& game->manualListModel);
}
void NineChessWindow::actionRules_triggered()
@ -208,7 +209,24 @@ void NineChessWindow::on_actionNew_N_triggered()
void NineChessWindow::on_actionOpen_O_triggered()
{
QString path = QFileDialog::getOpenFileName(this, "open", "../", "TXT(*.txt)");
if (path.isEmpty() == false)
{
//文件对象
QFile file(path);
//打开文件,只读方式打开
bool isok = file.open(QIODevice::ReadOnly);
if (isok = true)
{
//读文件
QByteArray array = file.readAll();
qDebug() << array;
}
//文件关闭
file.close();
}
}
void NineChessWindow::on_actionSave_S_triggered()

View File

@ -2,6 +2,9 @@
#define NINECHESSWINDOW_H
#include <QtWidgets/QMainWindow>
#include <QTextStream>
#include <QStringListModel>
#include <QFile>
#include "ui_ninechesswindow.h"
class GameScene;
@ -29,6 +32,8 @@ private:
QList <QAction *> ruleActionList;
// 游戏的规则号,涉及菜单项和对话框,所以要有
int ruleNo;
// 文件
QFile file;
private slots:
// 初始化