逆向软件工程
1.扫雷开源项目
来源:https://github.com/xiaoxi666/mines_sweeper
2.运行环境:Visual Studio 2022
下面是几个主要的代码
每个格子元素:
点击查看代码
#include "cellitem.h"
//#include <qDebug>
#include <fielddata.h>
int cellItem::_refCount = 0;
QPixmap * cellItem::_initial = nullptr;
QPixmap * cellItem::_flag = nullptr;
QPixmap * cellItem::_question = nullptr;
QPixmap * cellItem::_blank = nullptr;
QPixmap * cellItem::_explode = nullptr;
QPixmap * cellItem::_digits[9] = {nullptr};
cellItem::cellItem(int x, int y,
enum cellStatus status, QGraphicsItem *parent)
:
QGraphicsPixmapItem(parent) {
cx = x;
cy = y;
_status = status;
++_refCount;//创建第一个cellItem时加载所有图片
if(_refCount == 1) {//引用计数,保证只new一次
_initial = new QPixmap("://images/initial.png");
_flag = new QPixmap("://images/flag.png");
_question = new QPixmap("://images/question.png");
_blank = new QPixmap("://images/blank.png");
_explode = new QPixmap("://images/explode.png");
QString prefix = "://images/";
QString num;
QString suffix = ".png";
for(int i=1;i<9;++i){
QString fileName;
fileName+=prefix;
fileName+=num.setNum(i);
fileName+=suffix;
_digits[i] = new QPixmap(fileName);
}
}
setPixmap(*_initial);
//qDebug()<<this<<endl;
}
cellItem::~cellItem(){
//释放所有cellItem后,释放图片
if(--_refCount == 0){
delete _initial ;
delete _flag ;
delete _question ;
delete _blank ;
delete _explode ;
for(int i=0;i<9;++i){
delete _digits[i];
}
}
}
enum cellStatus cellItem::getStatus(){
return _status;
}
void cellItem::setStatus(enum cellStatus status,int digit){
_status=status;
switch(_status){
case INITIAL:
setPixmap(*_initial);
break;
case FLAG:
setPixmap(*_flag);
break;
case QUESTION:
setPixmap(*_question);
break;
case EXPLODE:
setPixmap(*_explode);
break;
case DIGIT:
setPixmap(*_digits[digit]);
break;
case BLANK:
setPixmap(*_blank);
break;
default:
break;
}
}
点击查看代码
#include "configdialog.h"
#include "ui_configdialog.h"
#include <QIntValidator>
//#include <qDebug>
configDialog::configDialog(int cw, int ch, int cm, QWidget *parent) :
QDialog(parent),
_width(cw),
_height(ch),
_mines(cm),
ui(new Ui::configDialog)
{
ui->setupUi(this);
QIntValidator * vaildW = new QIntValidator(1, 30, this);//宽度限制
QIntValidator * vaildH = new QIntValidator(1, 19, this);//高度限制
QIntValidator * vaildM = new QIntValidator(0, 570, this);//雷数初步限制(后面还有个数检查)
ui->lineEditWidth->setValidator(vaildW);
ui->lineEditHeight->setValidator(vaildH);
ui->lineEditMines->setValidator(vaildM);
ui->lineEditWidth->setText(QString::number(_width));
ui->lineEditHeight->setText(QString::number(_height));
ui->lineEditMines->setText(QString::number(_mines));
connect(ui->buttonBox,SIGNAL(accepted()),this,SLOT(accept()));
}
//自定义accept槽(本来要调用父类的槽,现在调用自己的,因此稍后需要显示调用父类槽)
void configDialog::accept(){
_width=ui->lineEditWidth->text().toInt();
_height=ui->lineEditHeight->text().toInt();
_mines=ui->lineEditMines->text().toInt();
QDialog::accept();//自己的实现以后,再显示调用父类的accept槽
}
configDialog::~configDialog() {
delete ui;
}
点击查看代码
#include "fielddata.h"
#include <stdlib.h>
#include <ctime>
//#include <iostream>
//#include <qDebug>
//using namespace std;
FieldData *FieldData::_ins=nullptr;
FieldData::FieldData()
: _width(20), _height(15), _mines(50)//初始扫雷矩阵大小
{
reset();
srand(time(nullptr));
}
FieldData::~FieldData() {
}
void FieldData::reset(){
_cells.clear();
initCells();
deployMines();
_visited.clear();
initVisited();
}
//初始化,申请空间
void FieldData::initCells() {
for(int x = 0; x < _width; ++x) {
_cells.push_back(CellColumn(_height));
}
}
//初始化visited
void FieldData::initVisited() {
for(int x = 0; x < _width; ++x) {
_visited.push_back(CellColumn(_height));
}
for(int x = 0; x < _width; ++x) {
for(int y = 0; y < _height; ++y) {
_visited[x][y] = 0;
}
}
}
//布雷
void FieldData::deployMines() {
//初始化
for(int x = 0; x < _width; ++x) {
for(int y = 0; y < _height; ++y) {
_cells[x][y] = 0;
}
}
int cpmines = _mines;
while(cpmines) {
int x = rand() % _width;
int y = rand() % _height;
//只有之前没有布过雷的位置才可以布雷
if(_cells[x][y] != -1) {
--cpmines;
_cells[x][y] = -1;
//更新周围的雷数
updateSurrounding(x - 1, y - 1); //左上
updateSurrounding(x, y - 1); //上
updateSurrounding(x + 1, y - 1); //右上
updateSurrounding(x - 1, y); //左
updateSurrounding(x + 1, y); //右
updateSurrounding(x - 1, y + 1); //左下
updateSurrounding(x, y + 1); //下
updateSurrounding(x + 1, y + 1); //右下
}
}
//dump();
}
//布雷之后,更新周围的雷数
void FieldData::updateSurrounding(int x, int y) {
//越界判断
if(x >= 0 && x < _width && y >= 0 && y < _height) {
if(_cells[x][y] != -1) {
++_cells[x][y];
}
}
}
//void FieldData::dump(){
// for(int y = 0; y < _height; ++y) {
// for(int x = 0; x < _width; ++x) {
// cout<<_cells[x][y]<<'\t';
// }
// cout<<endl;
// }
// //qDebug()<<"---------------------------------"<<endl;
//}
//每次点击后更新(参数:宽,高,雷)
void FieldData::customizeWHM(int cw, int ch, int cm) {
_width=cw;
_height=ch;
_mines=cm;
}
FieldData * FieldData::getInstance(){
if(_ins==nullptr){
_ins=new FieldData;
}
return _ins;
}

3.主要问题:
(1)踩到雷即结束游戏,没有加入地雷连锁爆炸动画
(2)左右键同时按下功能未实现
4.第一个问题,要想实现地雷连锁爆炸动画,可以使用递归或 BFS 算法 模拟爆炸传播,逐步显示动画效果。
点击查看代码
// 显示当前战场
void printField() {
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
printf("%c ", field[i][j]);
}
printf("\n");
}
Sleep(300); // 让动画慢一点
}
// 递归爆炸扩散
void explode(int x, int y) {
if (x < 0 || x >= SIZE || y < 0 || y >= SIZE || field[x][y] != '*') return;
field[x][y] = 'X'; // 爆炸标记
printField();
// 递归向四周扩散爆炸
explode(x - 1, y); // 上
explode(x + 1, y); // 下
explode(x, y - 1); // 左
explode(x, y + 1); // 右
}
第二个问题,具体实现思路很简单:用两个标记位分别标记左右键是否处于按下状态,两者同时为真的时候,进一步检查标记旗,符合要求就打开
点击查看代码
// 处理鼠标点击
void handleMouseEvent(SDL_Event *event) {
int x = event->button.x / 30; // 计算鼠标点到的格子
int y = event->button.y / 30;
if (event->type == SDL_MOUSEBUTTONDOWN) {
if (event->button.button == SDL_BUTTON_LEFT) leftPressed = true;
if (event->button.button == SDL_BUTTON_RIGHT) rightPressed = true;
// **左右键同时按下时触发功能**
if (leftPressed && rightPressed) {
if (opened[x][y] && countFlags(x, y) == board[x][y]) {
openSurrounding(x, y);
}
}
}
if (event->type == SDL_MOUSEBUTTONUP) {
if (event->button.button == SDL_BUTTON_LEFT) leftPressed = false;
if (event->button.button == SDL_BUTTON_RIGHT) rightPressed = false;
}
}

而地雷连锁爆炸由于我没有爆炸动画的文件,所以暂时还不能完全实现。

6.总结:
第一个难点在于去哪里找到合适的项目。
刚开始我在博客园上浏览,但那些软件不是太大太复杂,就是最初级的XX管理系统。
所以后来我就去github上找了,终于找到了相对合适可以入手的项目。

拿到代码首先要理清它总体的一个结构,然后再去仔细观察它的各个代码。代码里的许多东西都还看不懂,所以得去网上查或者问AI

逆向软件工程就是在改进代码中学习。必须要搞明白代码是怎么运行的、代码还有哪些问题,才能对代码进行改进。而以后的软件开发则是自己对自己的逆向软件工程,改进自己的代码,让软件能实现更好的效果。软件开发就是不断的改进的过程
浙公网安备 33010602011771号