完善“历史局面”功能

This commit is contained in:
liuweilhy 2018-08-20 00:52:58 +08:00
parent b3654dd794
commit 2d7a795f97
12 changed files with 327 additions and 68 deletions

View File

@ -243,6 +243,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
@ -258,14 +263,14 @@
<widget class="QLabel" name="label1">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>微软雅黑</family>
<pointsize>10</pointsize>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
@ -319,6 +324,11 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
<string/>
</property>
@ -334,14 +344,14 @@
<widget class="QLabel" name="label2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<horstretch>3</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>微软雅黑</family>
<pointsize>10</pointsize>
<pointsize>9</pointsize>
</font>
</property>
<property name="text">
@ -379,6 +389,45 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="labelInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="ninechesswindow.qrc">:/icon/Resources/icon/Help.png</pixmap>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelRule">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>8</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<family>AmdtSymbols</family>
<pointsize>7</pointsize>
</font>
</property>
<property name="text">
<string>rule</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="SizeHintListView" name="listView">
<property name="font">

View File

@ -109,6 +109,11 @@ QPointF BoardItem::nearestPosition(QPointF const pos)
return nearestPos;
}
QPointF BoardItem::cp2pos(int c, int p)
{
return position[(c-1)*SEAT+p-1];
}
bool BoardItem::pos2cp(QPointF pos, int &c, int &p)
{
// 寻找最近的落子点

View File

@ -20,6 +20,8 @@ public:
void setDiagonal(bool arg = true);
// 返回最近的落子点
QPointF nearestPosition(QPointF const pos);
// 将模型的圈、位转化为落子点坐标
QPointF cp2pos(int c, int p);
// 将落子点坐标转化为模型用的圈、位
bool pos2cp(QPointF pos, int &c, int &p);

View File

@ -12,7 +12,8 @@
#include "boarditem.h"
#include "pieceitem.h"
GameController::GameController(GameScene & scene, QObject *parent) : QObject(parent),
GameController::GameController(GameScene &scene, QObject *parent) : QObject(parent),
// 是否浏览过历史纪录
scene(scene),
piece(NULL),
isEditing(false),
@ -22,11 +23,10 @@ isEngine2(false),
hasAnimation(true),
hasSound(true),
timeID(0),
ruleNo(-1),
timeLimit(0),
stepsLimit(0)
{
// 设置场景尺寸大小为棋盘大小的1.08倍
scene.setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54, BOARD_SIZE*1.08, BOARD_SIZE*1.08);
// 已在view的样式表中添加背景scene中不用添加背景
// 区别在于view中的背景不随视图变换而变换scene中的背景随视图变换而变换
//scene.setBackgroundBrush(QPixmap(":/image/Resources/image/background.png"));
@ -85,6 +85,7 @@ void GameController::gameReset()
timeID = 0;
// 重置游戏
chess.reset();
chessTemp.reset();
// 清除棋子
qDeleteAll(pieceList);
@ -109,6 +110,7 @@ void GameController::gameReset()
manualListModel.removeRows(0, manualListModel.rowCount());
manualListModel.insertRow(0);
manualListModel.setData(manualListModel.index(0), chess.getCmdLine());
// 发出信号通知主窗口更新LCD显示
QTime qtime = QTime(0, 0, 0, 0).addMSecs(time1);
emit time1Changed(qtime.toString("mm:ss.zzz"));
@ -129,7 +131,7 @@ void GameController::setInvert(bool arg)
{
isInverted = arg;
// 遍历所有棋子
foreach(PieceItem * p, pieceList)
for (PieceItem * p : pieceList)
{
if (p)
{
@ -150,12 +152,15 @@ void GameController::setRule(int ruleNo, int stepLimited /*= -1*/, int timeLimit
// 更新规则,原限时和限步不变
if (ruleNo < 0 || ruleNo >= NineChess::RULENUM)
return;
this->ruleNo = ruleNo;
if (stepLimited != -1 && timeLimited != -1) {
stepsLimit = stepLimited;
timeLimit = timeLimited;
}
// 设置模型规则,重置游戏
chess.setData(&NineChess::RULES[ruleNo], stepsLimit, timeLimit);
chessTemp = chess;
// 重置游戏
gameReset();
@ -253,6 +258,21 @@ void GameController::timerEvent(QTimerEvent *event)
*/
}
// 历史局面
void GameController::phaseChange(int row, bool change /*= false*/)
{
int rows = manualListModel.rowCount();
QStringList mlist = manualListModel.stringList();
qDebug() << "rows: " << rows;
for (int i = 0; i <= row; i++)
{
qDebug() << mlist.at(i);
chessTemp.command(mlist.at(i).toStdString().c_str());
}
updateScence(chessTemp);
}
// 槽函数根据QGraphicsScene的信号和状态来执行选子、落子或去子
bool GameController::actionPiece(QPointF pos)
{
@ -300,12 +320,13 @@ bool GameController::actionPiece(QPointF pos)
if (result)
{
int row = manualListModel.rowCount();
int size = chess.getCmdList()->size();
auto i = (chess.getCmdList())->rbegin();
int c = 0;
// 输出命令行
for (int n = size - row; n > 0; n--) {
manualListModel.insertRow(row);
manualListModel.setData(manualListModel.index(row), (*(i++)).c_str());
for (auto i = (chess.getCmdList())->begin(); i != (chess.getCmdList())->end(); ++i) {
if (++c <= row)
continue;
manualListModel.insertRow(c - 1);
manualListModel.setData(manualListModel.index(c - 1), (*i).c_str());
}
if (chess.whoWin() != NineChess::NOBODY)
playSound(soundWin);
@ -314,11 +335,6 @@ bool GameController::actionPiece(QPointF pos)
return result;
}
// 历史局面
void GameController::phaseChange(const QModelIndex &index)
{
}
// 选子
PieceItem *GameController::choosePiece(QPointF pos)
{
@ -470,7 +486,7 @@ bool GameController::removePiece(QPointF pos)
bool GameController::cleanForbidden()
{
foreach(PieceItem *p, pieceList)
for (PieceItem *p : pieceList)
{
if (p->isDeleted()) {
pieceList.removeOne(p);
@ -479,3 +495,46 @@ bool GameController::cleanForbidden()
}
return true;
}
bool GameController::updateScence(NineChess &chess)
{
// 清除棋子
qDeleteAll(pieceList);
pieceList.clear();
piece = NULL;
const char *board = chess.getBoard();
for (int i = NineChess::SEAT; i < (NineChess::SEAT)*(NineChess::RING + 1); i++) {
QPointF pos = scene.cp2pos(i / NineChess::SEAT, i % NineChess::SEAT + 1);
if (board[i] & 0x30) {
PieceItem *newP = NULL;
PieceItem::Models md;
if (isInverted)
md = (board[i] & 0x10) ? PieceItem::whitePiece : PieceItem::blackPiece;
else
md = (board[i] & 0x10) ? PieceItem::blackPiece : PieceItem::whitePiece;
newP = new PieceItem;
newP->setModel(md);
newP->setPos(pos);
newP->setNum(chess.getPieceNum(i / NineChess::SEAT, i % NineChess::SEAT + 1));
// 如果重复三连不可用,则显示棋子序号
if (!(chess.getRule()->canRepeated))
newP->setShowNum(true);
pieceList.append(newP);
scene.addItem(newP);
}
else if (board[i] & 0x0F) {
PieceItem *newP = NULL;
newP = new PieceItem;
newP->setDeleted();
newP->setPos(pos);
pieceList.append(newP);
scene.addItem(newP);
}
}
scene.clearSelection();
return true;
}

View File

@ -15,8 +15,9 @@
#include <QTextStream>
#include <QStringListModel>
#include <QModelIndex>
#include "gamescene.h"
#include "ninechess.h"
#include "gamescene.h"
//#include "sizehintlistview.h"
class PieceItem;
@ -29,6 +30,7 @@ public:
~GameController();
//主窗口菜单栏明细
const QMap <int, QStringList> getActions();
int getRuleNo() { return ruleNo; }
int getTimeLimit() { return timeLimit; }
int getStepsLimit() { return stepsLimit; }
// 文本流
@ -45,6 +47,8 @@ signals:
void statusBarChanged(const QString & message);
public slots:
// 设置规则
void setRule(int ruleNo, int stepLimited = -1, int timeLimited = -1);
// 游戏开始
void gameStart();
// 游戏重置
@ -53,8 +57,6 @@ public slots:
void setEditing(bool arg = true);
// 设置黑白反转状态
void setInvert(bool arg = true);
// 设置规则
void setRule(int ruleNo, int stepLimited = -1, int timeLimited = -1);
// 让电脑执先手
void setEngine1(bool arg = true);
// 让电脑执后手
@ -67,11 +69,12 @@ public slots:
void playSound(QString &soundPath);
// 根据QGraphicsScene的信号和状态来执行选子、落子或去子
bool actionPiece(QPointF p);
// 历史局面
void phaseChange(const QModelIndex &index);
// 历史局面及局面改变
void phaseChange(int row, bool change = false);
protected:
bool eventFilter(QObject * watched, QEvent * event);
// 定时器
void timerEvent(QTimerEvent * event);
// 选子
PieceItem *choosePiece(QPointF pos);
@ -83,12 +86,18 @@ protected:
bool removePiece(QPointF pos);
// 删除禁止点子
bool cleanForbidden();
// 更新棋局显示
bool updateScence(NineChess &chess);
private:
// 棋对象的数据模型
NineChess chess;
// 棋对象的数据模型(临时)
NineChess chessTemp;
// 棋局的场景类
GameScene &scene;
// 棋谱列表
//SizeHintListView &listView;
// 所有棋子
QList<PieceItem *> pieceList;
// 当前棋子
@ -109,6 +118,8 @@ private:
bool hasSound;
// 定时器ID
int timeID;
// 规则变化
int ruleNo;
// 规则限时(分钟)
int timeLimit;
// 规则限步数

View File

@ -1,4 +1,4 @@
#include "gamescene.h"
#include "gamescene.h"
#include "pieceitem.h"
#include "boarditem.h"
#include <QGraphicsItem>
@ -50,7 +50,7 @@ void GameScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
}
// 取消其它棋子的选中状态
//foreach (QGraphicsItem * item, selectedItems())
//for (QGraphicsItem * item: selectedItems())
//{
// item->setSelected(false);
//}
@ -81,12 +81,17 @@ void GameScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
}
}
QPointF GameScene::cp2pos(int c, int p)
{
return board->cp2pos(c, p);
}
bool GameScene::pos2cp(QPointF pos, int &c, int &p)
{
return board->pos2cp(pos, c, p);
}
void GameScene::setDiagonal(bool arg)
void GameScene::setDiagonal(bool arg /*= true*/)
{
if (board)
board->setDiagonal(arg);

View File

@ -1,4 +1,4 @@
#ifndef GAMESCENE_H
#ifndef GAMESCENE_H
#define GAMESCENE_H
#include <QGraphicsScene>
@ -11,6 +11,8 @@ class GameScene : public QGraphicsScene
public:
explicit GameScene(QObject *parent = nullptr);
~GameScene();
// 将模型的圈、位转化为落子点坐标
QPointF cp2pos(int c, int p);
// 将落子点坐标转化为模型用的圈、位
bool pos2cp(QPointF pos, int &c, int &p);
// 设置棋盘斜线

View File

@ -130,7 +130,9 @@ bool NineChess::setData(const struct Rule *rule, int s, int t, int step, int fla
this->rule.maxTime = t;
// 设置步数
this->step = step;
// 设置状态
// 局面阶段标识
if (flags & GAME_NOTSTARTED)
phase = GAME_NOTSTARTED;
@ -327,9 +329,72 @@ void NineChess::getData(struct Rule &rule, int &step, int &chess, const char *&b
num_NeedRemove = this->num_NeedRemove;
}
const char * NineChess::getBoard()
{
return board;
}
bool NineChess::reset()
{
return setData(&rule);
if (phase == GAME_NOTSTARTED && player1_MS == player2_MS == 0)
return true;
// 步数归零
step = 0;
// 局面阶段标识
phase = GAME_NOTSTARTED;
// 轮流状态标识
turn = PLAYER1;
// 动作状态标识
action = ACTION_PLACE;
// 胜负标识
winner = NOBODY;
// 当前棋局3×8
memset(board, 0, sizeof(board));
// 盘面子数归零
player1_Remain = player2_Remain = 0;
// 设置玩家盘面剩余子数和未放置子数
player1_InHand = player2_InHand = rule.numOfChess;
// 设置去子状态时的剩余尚待去除子数
num_NeedRemove = 0;
// 清空成三记录
millList.clear();
// 不选中棋子
posOfSelected = 0;
// 用时置零
player1_MS = player2_MS = 0;
// 提示
setTip();
// 计棋谱
cmdlist.clear();
int i;
for (i = 0; i < RULENUM; i++) {
if (strcmp(this->rule.name, RULES[i].name) == 0)
break;
}
if (sprintf(cmdline, "r%1u s%03u t%02u", i + 1, rule.maxSteps, rule.maxTime) > 0) {
cmdlist.push_back(string(cmdline));
return true;
}
else {
cmdline[0] = '\0';
return false;
}
return true;
}
bool NineChess::start()
@ -362,9 +427,13 @@ int NineChess::cp2pos(int c, int p)
bool NineChess::place(int c, int p, long time_p /* = -1*/)
{
// 如果局面为"未开局"或“结局”返回false
if (phase == GAME_NOTSTARTED || phase == GAME_OVER)
// 如果局面为“结局”返回false
if (phase == GAME_OVER)
return false;
// 如果局面为“未开局”,则开具
if (phase == GAME_NOTSTARTED)
start();
// 如非“落子”状态返回false
if (action != ACTION_PLACE)
return false;

View File

@ -102,6 +102,8 @@ public:
// 获取棋局状态和棋盘数据
void getData(struct Rule &rule, int &step, int &flags, const char *&boardsource, int &p1_InHand, int &p2_InHand, int &num_NeedRemove);
// 获取棋盘数据
const char *getBoard();
// 获取当前规则
const struct Rule *getRule() { return &rule; }
// 获取当前步数

View File

@ -1,9 +1,6 @@
#if _MSC_VER >= 1600
#pragma execution_character_set("utf-8")
#endif
#include "ninechesswindow.h"
#include "gamecontroller.h"
#include "gamescene.h"
#include <QDesktopServices>
#include <QMap>
#include <QMessageBox>
@ -19,6 +16,10 @@
#include <QHelpEvent>
#include <QToolTip>
#include <QDebug>
#include "ninechesswindow.h"
#include "gamecontroller.h"
#include "gamescene.h"
#include "graphicsconst.h"
NineChessWindow::NineChessWindow(QWidget *parent)
: QMainWindow(parent),
@ -36,6 +37,9 @@ NineChessWindow::NineChessWindow(QWidget *parent)
// 设置场景
scene = new GameScene(this);
// 设置场景尺寸大小为棋盘大小的1.08倍
scene->setSceneRect(-BOARD_SIZE * 0.54, -BOARD_SIZE * 0.54, BOARD_SIZE*1.08, BOARD_SIZE*1.08);
// 初始化各个控件
// 关联视图和场景
@ -81,7 +85,6 @@ NineChessWindow::~NineChessWindow()
if (game != NULL)
delete game;
qDeleteAll(ruleActionList);
;
}
bool NineChessWindow::eventFilter(QObject *watched, QEvent *event)
@ -142,8 +145,6 @@ void NineChessWindow::initialize()
game, SLOT(setSound(bool)));
connect(ui.actionAnimation_A, SIGNAL(toggled(bool)),
game, SLOT(setAnimation(bool)));
connect(ui.listView, SIGNAL(activated(const QModelIndex &index)),
game, SLOT(phaseChange(const QModelIndex &index)));
/* 关联控制器的信号和主窗口控件的槽
GameController中直接控制NineChessWindow
@ -169,15 +170,12 @@ void NineChessWindow::initialize()
// 默认第2号规则
ruleNo = 2;
// 设置选中当前规则的菜单项
ruleActionList.at(ruleNo)->setChecked(true);
// 重置游戏规则
game->setRule(ruleNo);
// 规则提示
//QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name))
// .arg(tr(NineChess::RULES[ruleNo].info));
// 更新规则显示
ruleInfo();
// 关联列表视图和字符串列表模型
ui.listView->setModel(& game->manualListModel);
@ -201,11 +199,33 @@ void NineChessWindow::initialize()
on_actionRowChange();
}
void NineChessWindow::ruleInfo()
{
int s = game->getStepsLimit();
int t = game->getTimeLimit();
QString tl(" 不限时");
QString sl(" 不限步");
if (s > 0)
sl = "" + QString::number(s) + "";
if (t > 0)
tl = " 限时" + QString::number(s) + "";
// 规则显示
ui.labelRule->setText(tl + sl);
// 规则提示
ui.labelInfo->setToolTip(QString(NineChess::RULES[ruleNo].name) + "\n" +
NineChess::RULES[ruleNo].info);
ui.labelRule->setToolTip(ui.labelInfo->toolTip());
//QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name))
// .arg(tr(NineChess::RULES[ruleNo].info));
}
void NineChessWindow::actionRules_triggered()
{
// setChecked函数会发出toggled信号在这里响应toggled信号会陷入死循环
// 取消其它规则的选择
foreach(QAction *action, ruleActionList)
for(QAction *action: ruleActionList)
action->setChecked(false);
// 选择当前规则
QAction *action = dynamic_cast<QAction *>(sender());
@ -213,9 +233,8 @@ void NineChessWindow::actionRules_triggered()
ruleNo = action->data().toInt();
// 重置游戏规则
game->setRule(ruleNo);
// 规则提示
//QString tip_Rule = QString("%1\n%2").arg(tr(NineChess::RULES[ruleNo].name))
// .arg(tr(NineChess::RULES[ruleNo].info));
// 更新规则显示
ruleInfo();
}
@ -292,45 +311,56 @@ void NineChessWindow::on_actionInvert_I_toggled(bool arg1)
// 前后招的公共槽
void NineChessWindow::on_actionRowChange()
{
QModelIndex index = ui.listView->currentIndex();
int row = index.row();
QAbstractItemModel * model = ui.listView->model();
int rows = model->rowCount();
int currentRow = ui.listView->currentIndex().row();
QObject * const obsender = this->sender();
QObject * const obsender = sender();
if (obsender != NULL) {
if (obsender == (QObject *)ui.actionBegin_S) {
ui.listView->setCurrentIndex(ui.listView->model()->index(0, 0));
ui.listView->setCurrentIndex(model->index(0, 0));
}
else if (obsender == (QObject *)ui.actionPrevious_B) {
if (row > 0) {
ui.listView->setCurrentIndex(ui.listView->model()->index(row - 1, 0));
if (currentRow > 0) {
ui.listView->setCurrentIndex(model->index(currentRow - 1, 0));
}
}
else if (obsender == (QObject *)ui.actionNext_F) {
if (row < ui.listView->model()->rowCount() - 1) {
ui.listView->setCurrentIndex(ui.listView->model()->index(row + 1, 0));
if (currentRow < rows - 1) {
ui.listView->setCurrentIndex(model->index(currentRow + 1, 0));
}
}
else if (obsender == (QObject *)ui.actionEnd_E) {
ui.listView->setCurrentIndex(ui.listView->model()->index(ui.listView->model()->rowCount() - 1, 0));
ui.listView->setCurrentIndex(model->index(rows - 1, 0));
}
currentRow = ui.listView->currentIndex().row();
}
index = ui.listView->currentIndex();
row = index.row();
ui.actionBegin_S->setEnabled(true);
ui.actionPrevious_B->setEnabled(true);
ui.actionNext_F->setEnabled(true);
ui.actionEnd_E->setEnabled(true);
if (row <= 0) {
// 更新动作状态
if (rows <= 1) {
ui.actionBegin_S->setEnabled(false);
ui.actionPrevious_B->setEnabled(false);
}
if (row >= ui.listView->model()->rowCount()-1)
{
ui.actionNext_F->setEnabled(false);
ui.actionEnd_E->setEnabled(false);
}
else {
if (currentRow <= 0) {
ui.actionBegin_S->setEnabled(false);
ui.actionPrevious_B->setEnabled(false);
ui.actionNext_F->setEnabled(true);
ui.actionEnd_E->setEnabled(true);
}
if (currentRow >= rows - 1)
{
ui.actionBegin_S->setEnabled(true);
ui.actionPrevious_B->setEnabled(true);
ui.actionNext_F->setEnabled(false);
ui.actionEnd_E->setEnabled(false);
}
}
// 更新局面
game->phaseChange(currentRow);
}
void NineChessWindow::on_actionAutoRun_A_toggled(bool arg1)
@ -413,6 +443,10 @@ void NineChessWindow::on_actionLimited_T_triggered()
// 重置游戏规则
game->setRule(ruleNo, stepLimited, timeLimited);
}
// 更新规则显示
ruleInfo();
// 删除对话框,子控件会一并删除
delete dialog;
}

View File

@ -34,6 +34,8 @@ private:
int ruleNo;
// 文件
QFile file;
// 更新规则标签
void ruleInfo();
private slots:
// 初始化

View File

@ -30,10 +30,14 @@ signals:
// 需要一个currentChanged信号但默认没有需要把这个槽改造成信号
void currentChangedSignal(const QModelIndex &current, const QModelIndex &previous);
protected:
protected slots:
// 屏蔽掉双击编辑功能
void mouseDoubleClickEvent(QMouseEvent *event) {}
// 插入新行后自动选中最后一个
/* 本来重载rowsInserted函数用于在插入新行后自动选中最后一行
Model的insertRow执行后rowsInserted会被立即执行
Model的setData还未被执行
void rowsInserted(const QModelIndex &parent, int start, int end) {
// 调用父类函数为使滚动条更新否则scrollToBottom不能正确执行。
QListView::rowsInserted(parent, start, end);
@ -41,6 +45,21 @@ protected:
setCurrentIndex(id);
scrollToBottom();
}
*/
// 采用判断最后一个元素是否改变来选中之
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight,
const QVector<int> &roles = QVector<int>()) {
QAbstractItemView::dataChanged(topLeft, bottomRight, roles);
QModelIndex index = model()->index(model()->rowCount() - 1, 0);
if (topLeft == bottomRight) {
if (index == bottomRight) {
setCurrentIndex(index);
scrollToBottom();
}
}
}
// 需要一个currentChanged信号但默认没有需要把这个槽改造成信号
// activated信号需要按下回车才发出selectedChanged和clicked信号也不合适
void currentChanged(const QModelIndex &current, const QModelIndex &previous) {