MFC实现红黑砖块

Posted titordong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MFC实现红黑砖块相关的知识,希望对你有一定的参考价值。

MFC实现红黑砖块

题目

老题目了,给定w,h长宽的图,上面有颜色不同的瓷砖,黑和红,问从给的起点出发,只能走黑色瓷砖,能走多少块,可视化输出过程

思路

咋一看搜索水题,但是要用可视化,要用模板类,,,崩溃掉了,又得拾起MFC了,在学会别的可视化之前,先凑活吧
每个点可以连4条边,超过边界的不连,从起点dfs,遍历每一个相邻的点,判断是不是#黑砖块,是就入栈,vis数组更新为true,每次出栈的时候涂色,难点其实不在图算法,而是MFC画图,,,,

1)图的模板类设计

只用一个class T就行了,没有边权值,基本上都是书上的代码

#pragma once
#include<iostream>
using namespace std;
const int DefaultSize = 1000;
template<class T>
struct Edge {
    int dest;
    Edge<T>*link;
    Edge(){}
    Edge(int to) :dest(to), link(NULL){}
};
template<class T>
struct Vertex {
    T data;
    Edge<T>*adj;
};
template<class T>
class Graph{
public:
    Graph(int sz=DefaultSize);
    ~Graph();
    int GetNodeNum() {
        return numVertices;
    }
    bool insertVertex(const T&vertex);
    bool insertEdge(int v1, int v2);
    int getFirstNeighbor(int v);
    int getNextNeighbor(int v, int w);
    T getValue(int v);
    void clear();
protected:
    Vertex<T>*NodeTable;
    int numVertices;
    int maxVertices;
    int numEdge;
};
template<class T>
Graph<T>::Graph(int sz) {
    maxVertices = sz;
    numVertices =numEdge=0;
    NodeTable = new Vertex<T>[maxVertices];
    if (NodeTable == NULL) {
        cerr << "错误" << endl;
        exit(1);
    }
    for (int i(0); i < maxVertices; i++) {
        NodeTable[i].adj = NULL;
    }
}
template<class T>
Graph<T>::~Graph() {
    for (int i(0); i < numVertices; i++) {
        Edge<T>*p = NodeTable[i].adj;
        while (p != NULL) {
            NodeTable[i].adj = p->link;
            delete p;
            p = NodeTable[i].adj;
        }
    }
    delete[]NodeTable;
}
template<class T>
bool Graph<T>::insertVertex(const T&vertex) {
    if (numVertices == maxVertices)return false;
    NodeTable[numVertices].data = vertex;
    numVertices++;
    return true;
}
template<class T>
bool Graph<T>::insertEdge(int v1, int v2) {
    if (v1 >= 0 && v1 < numVertices&&v2 >= 0 && v2 < numVertices) {
        Edge<T>*p = NodeTable[v1].adj;
        while (p != NULL && p->dest != v2) {
            p = p->link;
        }
        if (p != NULL)return false;
        p = new Edge<T>;
        p->dest = v2;
        p->link = NodeTable[v1].adj;
        NodeTable[v1].adj = p;
        numEdge++;
    }
    return 0;
}
template<class T>
int Graph<T>::getFirstNeighbor(int v) {
    if (v != -1) {
        Edge<T>*p = NodeTable[v].adj;
        if (p != NULL)return p->dest;
    }
    return -1;
}
template<class T>
int Graph<T>::getNextNeighbor(int v, int w) {
    if (v != -1) {
        Edge<T>*p = NodeTable[v].adj;
        while (p != NULL && p->dest != w) {
            p = p->link;
        }
        if (p != NULL && p->link != NULL)
            return p->link->dest;
    }
    return -1;
}
template<class T>
void Graph<T>::clear() {
    for (int i(0); i < numVertices; i++) {
        Edge<T>*p = NodeTable[i].adj;
        while (p != NULL) {
            NodeTable[i].adj = p->link;
            delete p;
            p = NULL;
            p = NodeTable[i].adj;
        }
    }
    numVertices = 0;
    numEdge = 0;
}
template<class T>
T Graph<T>::getValue(int v) {
    if (v == -1)return NULL;
    return NodeTable[v].data;
}

###2)界面设计
好了,最简单的模板类设计已经解决了(括弧笑),接下来我们开始做界面。首先建立一个基于单文档的MFC工程,打开资源视图,在工具条那里加3个按钮进去,然后分别在view类里面添加事件处理程序

技术分享图片

3)数据输入

先定义view下的自定义数据

    int W, H;
    int Spos;
    bool is_OK = false;
    char feld[30][30];
    Graph<char>G;
    afx_msg void OnReadGraph();
    afx_msg void Onbuild();
    void DrawRect(CRect crect, int border, CBrush&brush);
    void DrawCircle(CRect rect, int border, CBrush&brush);
    afx_msg void Onstart();

数据输入用了自带的资源管理器类,把txt的房间文件读取,需要注意的是,W和H后面都要加空格,这才能从strline中提取单个数字出来

void C走瓷砖View::OnReadGraph()
{
    Invalidate();
    CString fileName;
    CFileDialog dlg(TRUE);
    if (IDOK == dlg.DoModal())
        fileName = dlg.GetPathName();
    if (fileName.IsEmpty())
        return;
    CStdioFile file;
    if (file.Open(fileName, CFile::modeRead))
    {
        is_OK = true;
        CString strLine;
        CString str;
        file.ReadString(strLine);
        AfxExtractSubString(str, strLine, 0, ' ');
        W = _ttoi(str);
        AfxExtractSubString(str, strLine, 1, ' ');
        H = _ttoi(str);
        for (int i(0); i < H; i++) {
            file.ReadString(strLine);
            for (int j(0); j < W; j++) {
                feld[i][j] = strLine[j];
            }
        }
        file.Close();
    }
    // TODO: 在此添加命令处理程序代码
}

4)建图并画图

先定义两个函数,一个画矩形一个画圆,

void C走瓷砖View::DrawRect(CRect rect,int border,CBrush&brush)
{
    CClientDC dc(this);
    //dc.SetROP2(R2_XORPEN);
    
    CPen pen;
    pen.CreatePen(PS_SOLID, border, RGB(128,128, 128));
    CPen *ppen = dc.SelectObject(&pen);
    dc.Rectangle(rect);
    dc.SelectObject(ppen);
    CBrush *pbrush;
    pbrush = dc.SelectObject(&brush);
    dc.Rectangle(rect);
    dc.SelectObject(pbrush);
    // TODO: 在此处添加实现代码.
}
void C走瓷砖View::DrawCircle(CRect rect, int border, CBrush&brush)
{
    CClientDC dc(this);
    int width = 50 / max(W, H);
    CRect newrect(rect.left + width, rect.top +width, rect.right - width, rect.bottom - width);
    CBrush *pbrush;
    pbrush = dc.SelectObject(&brush);
    dc.Ellipse(newrect);
    dc.SelectObject(pbrush);
    // TODO: 在此处添加实现代码.
}

传3个参数进去,矩形,边界宽度,填充颜色,先填充颜色,再用pen画框子好看点
接下来是建图的函数

void C走瓷砖View::Onbuild()
{
    if (is_OK == false) {
        AfxMessageBox(_T("请先读取房间!"));
        return;
    }
    G.clear();
    int cut=0;
    for (int i(0); i < H; i++) {
        for (int j(0); j < W; j++) {
            if (feld[i][j] == '@') {
                Spos = i * W + j;
            }
            G.insertVertex(feld[i][j]);
            cut++;
        }
    }
    int s1, s2, s3, s4;
    for (int i(0); i <H; i++) {
        for (int j(0); j < W; j++) {
            if (j) {
                s1 = i * W + j - 1;
                G.insertEdge(i*W + j, s1);
            }
            if (j < W - 1) {
                s2 = i * W + j + 1;
                G.insertEdge(i*W + j, s2);
            }
            s3 = (i -1)* W + j;
            G.insertEdge(i*W + j, s3);
            s4 = (i +1)* W + j;
            G.insertEdge(i*W + j, s4);
        }
    }
    int width = 500 / max(W, H);
    CRect rect(200, 200, 200 + width * W, 200 + width * H);
    int border = 5;
    CBrush brush;
    brush.CreateSolidBrush(RGB(255, 255,0));
    DrawRect(rect, border, brush);
    border = 2;
    CBrush redbrush, blackbrush;
    redbrush.CreateSolidBrush(RGB(255, 0, 0));
    blackbrush.CreateSolidBrush(RGB(0, 0, 0));
    for (int i(0); i < H; i++) {
        for (int j(0); j < W; j++) {
            CRect rect(200+j*width, 200+i*width, 200 + (j+1) * width, 200 + (i+1) * width);
            if (feld[i][j] == '*') {
                DrawRect(rect, border, redbrush);
            }
            else DrawRect(rect, border, blackbrush);
            if (feld[i][j] == '@') {
                CBrush greenbrush;
                greenbrush.CreateSolidBrush(RGB(0,255,0));
                DrawCircle(rect, border, greenbrush);
            }
        }
    }
    // TODO: 在此添加命令处理程序代码
}

用了画图的函数以后改代码也方便了很多

5)DFS走格子动画显示

其实这里也和上面的差不多,用到了栈

void C走瓷砖View::Onstart()
{
    if (!is_OK) {
        AfxMessageBox(_T("请先读取并绘制图"));
        return;
    }
    int width = 500 / max(W, H);
    int cur = Spos;
    stack<int>P;
    P.push(cur);
    bool *vis= new bool[G.GetNodeNum()];
    for (int i(0); i < G.GetNodeNum(); i++) {
        vis[i] = false;
    }
    CBrush pinkbrush;
    pinkbrush.CreateSolidBrush(RGB(255, 192,203));
    int border = 2;
    vis[cur] = true;
    while (!P.empty()) {
        cur = P.top();
        P.pop();
        CRect rect(200 + cur%W * width, 200 + (cur/W)* width, 200 + ((cur%W) + 1) * width, 200 + (cur / W + 1) * width);
        DrawRect(rect, border, pinkbrush);
        Sleep(50);
        int w = G.getFirstNeighbor(cur);
        if (w != -1) {
            if (!vis[w] && G.getValue(w) == '#') {
                P.push(w);
                vis[w] = true;
            }
            int s = G.getNextNeighbor(cur, w);
            while (s != -1) {
                if (!vis[s] && G.getValue(s) == '#') {
                    P.push(s);
                    vis[s] = true;
                }
                s = G.getNextNeighbor(cur, s);
            }
        }
    }
    // TODO: 在此添加命令处理程序代码
}

6)房间数据哪里来?

当然不能自己手写了,手写大了太累,再写个程序,生成房间瓷砖数据

#include<iostream>
#include<time.h>
using namespace std;
int main() {
    int x;
    srand(time(0));
    while (cin >> x) {
        int W = rand() % 20 + 5;
        int H = W - rand() % 5;
        cout << W << ' ' << H << ' ' << endl;
        for (int i(0); i < H; i++) {
            for (int j(0); j < W; j++) {
                int s = rand() % 2;
                if (s == 1)
                    cout << "*";
                else cout << "#";
            }
            cout << endl;
        }
        cout << endl;
    }
    
}

只要随便输个数,就能生成一个图,然后选个你喜欢的地方,改成@起点,存在txt里就ok

后记

MFC其实还挺好玩的,(真香)
就是脖子好疼
2018/12/26 23:32:09

以上是关于MFC实现红黑砖块的主要内容,如果未能解决你的问题,请参考以下文章

原生JS实现简单打砖块弹球小游戏

2019春第二次课程设计实验报告

H5 简单实现打砖块游戏

C++ 代码片段(积累)

原生javascript面向对象开发儿时经典打砖块小游戏

打砖块c语言源代码,打砖块游戏的源代码(请多指教)