C#语言完成扫雷游戏的代码编写

C#语言完成扫雷游戏的代码编写,第1张

//尝试在控制台下使用面向对象知识完成扫雷游戏,使用字符打印相关信息即可。

using System

using System.Threading

namespace 扫雷

{

    //地图类,用来生成地图

    class Cmap

    {

        public Cmap(int x,int y) { CreatMap(x, y, 20)CreatGameMap(x, y)}

 含拍       int x//地图长

        int y//地图宽

        public int p//雷的数量---数量要小于x*y待实现

   消清     public int[] mapLine//地图的一维数组

        public int[,] map//地图的二维数组

        public string[,] gameMap//游戏地图

        public int[] mineSet//记录雷的位置

        public int gameState=0//记录游戏情况:0游戏中,1游戏失败,2游戏结束

        public int n = 0

        //创建地图

        public void CreatMap(int x, int y, int p)

        {

          Random random = new Random()

          this.x = x

          this.y = y

          this.p = p

          map = new int[x, y]

          mapLine = new int[x*y]

            //生成雷的位置

            mineSet = new int[p]

            for (int i = 0i <pi++)

            {

                mineSet[i] = random.Next(0, mapLine.Length)

                for (int j = 0j <ij++)

                {

                    if (mineSet[j] == mineSet[i])

                    {

                        i--

                    }

                }

            }

            //一维数组初始化 

            for (int i = 0i <mapLine.Lengthi++)

            {

                mapLine[i] = 0

            }

            //一维数组装雷

            for (int i = 0i <mineSet.Lengthi++)

            {

                mapLine[mineSet[i]] = 9

            }

            //将有雷的信息的一维数组撞填到二维数组里面

            int conter = 0

            for (int i = 0i <map.GetLength(0)i++)

            {

                for (int j = 0j <map.GetLength(1)j++)

                {

                    map[i, j] = mapLine[conter++]

                }

           拿老前 } 

            //数字块算法

            int SetNumber(int x,int y)

            {  int conter=0

                if (map[x,y]==9)

                {

                    return 9

                }

                else

                {

                    if (x - 1 >= 0 &&y - 1 >= 0) { if (map[x - 1, y - 1] == 9) { conter++} }

                    if (y - 1 >= 0) { if (map[x, y - 1] == 9) { conter++} }

                    if (x - 1 >= 0) { if (map[x - 1, y] == 9) { conter++} }

                    if (x - 1 >= 0 &&y + 1 <= this.y-1) { if (map[x - 1, y + 1] == 9) { conter++} }

                    if (x + 1 <= this.x - 1 &&y - 1 >= 0) { if (map[x + 1, y - 1] == 9) { conter++} }

                    if (y + 1 <= this.y-1) { if (map[x , y + 1] == 9) { conter++} }

                    if (x + 1 <= this.x-1 ) { if (map[x + 1, y] == 9) { conter++} }

                    if (x + 1 <= this.x-1 &&y + 1 <= this.y - 1) { if (map[x + 1, y + 1] == 9) { conter++} }

                }

                return conter

            }

            //遍历地图,生成数字块

            for (int i = 0i <map.GetLength(0)i++)

            {

                for (int j = 0j <map.GetLength(1)j++)

                {

                    map[i, j] = SetNumber(i, j)

                }

            }

        }

        //展示地图

        public void ShowMap()

        {

            for (int i = 0i <map.GetLength(0)i++)

            {

                Console.Write(" ")

                for (int j = 0j <map.GetLength(1)j++)

                {

                    Console.Write($"{map[i, j]}  ")

                }

                Console.WriteLine()

                //Console.WriteLine()

            }

        }

        //创建游戏列表

        public void CreatGameMap(int x, int y)

        {

            gameMap = new string[x, y]

            //填充游戏界面:

            for (int i = 0i <gameMap.GetLength(0)i++)

            {

                for (int j = 0j <gameMap.GetLength(1)j++)

                {

                    gameMap[i, j] = "■"

                }

            }

        }

        //打印游戏界面

        public void ShowGameMap()

        {

            for (int i = 0i <gameMap.GetLength(0)i++)

            {

                Console.Write($"{i}  ")

                for (int j = 0j <gameMap.GetLength(1)j++)

                {

                    Console.Write($"{gameMap[i, j]} ")

                }

                Console.WriteLine()

            }

        }

        //界面转换

        public string MapChange(int x,int y)

        {

            if (map[x,y]==9)

            {

                return "☆"

            }

            else if (map[x,y]==0)

            {

                return "□"

            }

            else

            {

                return map[x, y].ToString()+" "

            }

        }

        //交互事件

        public void Click(int x, int y)

        {

            if (map[x,y]==9)

            {

                gameState = 2

                Console.WriteLine( $"游戏结束!")

            }

            else if (map[x,y]==0)

            {

                //递归

                AutoClick(x, y )

            }

            else

            {

                //继续游戏

            }

            gameMap[x, y] = MapChange(x, y)

        }

        public void AutoClick(int x, int y)

        {

            if (gameMap[x,y]== "■")

            {

                if (map[x, y] == 9)

                {

                    //Console.WriteLine($"{x}  {y}检测到地雷")

                    return

                }

                else if (map[x, y] == 0)

                {

                    //Console.WriteLine($"{x}  {y}开始递归")

                    gameMap[x, y] = MapChange(x, y)

                    //递归

                    if (x - 1 >= 0 &&y - 1 >= 0) { AutoClick(x - 1, y - 1)}//左上

                    if (y - 1 >= 0 ) { AutoClick(x, y - 1)}//左

                    if (x + 1 <= this.x - 1 &&y - 1 >= 0) { AutoClick(x + 1, y - 1)}//左下

                    if (x - 1 >= 0 ) {  AutoClick(x - 1, y)}//左下

                  if (x - 1 >= 0 &&y + 1 <= this.y - 1) {  AutoClick(x - 1, y + 1)}//

                    if (y + 1 <= this.y - 1 ) { AutoClick(x, y + 1)}

                    if (x + 1 <= this.x - 1 ) {  AutoClick(x + 1, y)}

                    if (x + 1 <= this.x - 1 &&y + 1 <= this.y - 1) {  AutoClick(x + 1, y + 1)}

                }

                else

                {

                    //Console.WriteLine($"{x}  {y}检测到数字")

                    //继续游戏

                    gameMap[x, y] = MapChange(x, y)

                    return

                }

            }

        }

        //白块递归

        //判断游戏输赢

        public void iswin()

        {

            n = 0

            for (int i = 0i <gameMap.GetLength(0)i++)

            {

                for (int j = 0j <gameMap.GetLength(1)j++)

                {

                    if (gameMap[i, j]== "■")

                    {

                        n++

                    }

                }

                //Console.WriteLine()

            }

            if (n<=p)

            {

                gameState = 1

            }

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            Cmap map = new Cmap(16, 30)

            //map.ShowMap()

            int x = 0

            int y = 0

            while (map.gameState == 0)

            {

                map.ShowGameMap()

                Console.WriteLine($"请输入您要扫描的坐标:")

                x = int.Parse(Console.ReadLine())

                y = int.Parse(Console.ReadLine())

                map.Click(x, y)

                Console.ReadLine()

                map.iswin()

                if (map.gameState == 1)

                {

                    Console.WriteLine("恭喜你游戏胜利")

                }

                if (map.gameState == 2)

                {

                    Console.WriteLine("很抱歉游戏失败")

                }

                if (map.gameState == 0)

                {

                    Console.WriteLine("游戏继续")

                }

                Console.ReadLine()

                Console.Clear()

            }

        }

    }

}

用VC++编写扫雷游戏

用VC++编写扫雷游戏

王洪亚

本文从分析windows扫雷游戏的功能特点开始,应用面向对象的可视化编程语言visual c++给出了个功能模块的具体实现方法,并提供了编写小游戏程序的一般方法和visual c++的一些使用技巧。

首先分析扫雷的最基本功能。

点击鼠标左键于未知区域,如果未知区域有雷,游戏停止,显示所有的地雷。如果没雷,则显示周围雷数,如果周围没雷,则再查看周围八个区域是否有雷直到有雷为止并显示,这其实是一个递归过程。

点击鼠标右键于未知区域,则将其置为有雷而不管是否真的有雷。可选择初、中、高三级并可自定义雷数和区域大小。

雷区上部左侧显示总雷数减被标明有雷区域的数目。

雷区上部中间位置显示一按钮用于开局和显示鼠标动作的结果。

雷区上部右侧显示扫雷的时间。

将雷全部扫清后,则显咐友示一对话框将你的姓名记入排行榜。以时间排序。

为完成上述功能,应用visual c++的具体技术细节如下:

1. 应用appwizard创建基于sdi的应用程序cbombapp,去除打印和状态条支持,在资源编辑器中修改菜单和相应的加速键,使其与windows扫雷游戏一致。具体为开局(id-game-begin)、初级(id-game-junior)、中级(id-game-middle)、高级(id-game-senior)、自定义(id-game-custom)、颜色(id-game-color)、英雄榜(id-game-sort)、退出(id-game-exit)、誉镇帮助(id-help)。

2. 在资源编辑器中对应于雷区的每个小区域的13个属性。用画笔或其他绘图工具绘制出相对应的13个10乘10的16色小位图,三个对应于小人表情的20乘20的16色小位图,供更换颜色时使用的一套与前16个对应的单色位图,显示时间和雷数的0~9十个数字位图(底色为黑色)。

定制customer对话框,内含三个静态文本控件和三个编辑控件,三个编辑控件分别对应成员m_irownum,m_icolumnnum,m_ibombnum。该对话框用于定制雷数,行列数,其相应的mfc类庆简粗为ccustomer。定制sort对话框,内含九个静态文本控件,其中六个显示排行榜的姓名和时间,其对应的mfc类为csort。定制input对话框,内含一个静态文本控件和一个编辑控件,编辑控件用于在游戏成功结束时输入姓名,其对应的mfc类为cinput。

3. 定义类bomb,封装每颗雷的相关属性。

class bomb

{

public:

int isbomb//决定初始时是否是雷

bool issel//判断区域是否被处理过且周围有雷

bool isdone//判断递归时是否被处理过

int num//周围雷数

bool findbomb//排雷者认为是雷时置一(但是不一定真是雷)

}

4. 重载cmainframe中precreatewindow,并设置相应属性,使其窗体大小固定,这样就固定了显示区域的大小为初始10乘10个雷和外加雷区上部的控制区域,部分代码如下。

cs.style=ws_overlapped|ws_sysmenu| ws_border|ws_minimizebox

cs.cy = 10*15+6

cs.cx = 10*15+60//6和60分别是横纵的附加值,用于边框、菜单、标题条、控制区域。

5. 游戏的主要工作就是呈现不断变换的图形或动画,并按用户的输入交互进行显示,而windows文档-----视窗构架中的视窗的功能正是接受用户输入并负责显示,因此由cview类来完成扫雷的大部分工作。在cbombview中定义下列成员变量记录相关 *** 作的结果或对象的状态。

bomb m_bomb[30][30]//最大的地雷区域

cstring m_currenttime//用于记录并显示扫雷时间

ctime m_begintime//记录游戏开始时的时间

bool m_timerbegin//定时器是否开启

int m_ibomnum//雷的数目

int m_irow//雷的行数

int m_icolumn//雷的列数

int m_ibombfound//指示被认为是雷的数目

cbitmapbutton m_bitbutton//控制区的位图按钮

int m_currentlevel//指示当前游戏的级别

bool m_biscolor//指示当前是彩色还是单色

cbitmap m_bmbomb[12]//用于存放12个小位图

int m_igameover//游戏未结束置0,已清除所有的雷置1,被炸死置2。

重载cbombview中oncreate函数创建位图按钮,该位图按钮的两幅位图对应了正常、排雷正确两种状态,当要显示被炸死的状态时应动态销毁该按钮,并重新创建一位图对应正常和被炸死两种状态,将该位图按钮的id号定为id_game_begin,这样一来当点击按钮时便可重新开始游戏,部分代码如下。

crect rcclient

getclientrect(&rcclient)

crect rect(rcclient.cx/2-8,10,rcclient.cx/2+8,20)

m_button.create("new",bs_defpushbutton|ws_visible|

bs_ownerdraw,rect,this,id_game_begin)

m_button.loadbitmaps(idb_face1, idb_face2)

显示时间的功能相对比较简单,在响应第一个wm_lbuttomdown消息时开启定时器,并记录游戏开始的时间,在wm_time消息响应函数ontimer中获得当前时间,减去游戏开始时的时间,在显示时间的客户区域显示得到的时间差(用数字位图),当游戏结束时(排完全部雷或被炸死)关闭定时器,停止显示。

wm_lbuttomdown消息响应函数onlbuttomdown是处理用户输入的主要执行者,函数首先判断点中位置是否是雷,是则关闭定时器,销毁原位图按钮,创建一对应正常和被炸死两种状态的新位图按钮,并调用setstate将其设置为pushdown(小人哭的状态),将m_bgameover,置为true标志游戏结束,否则先调用setstate 设置位图按钮为pushdown (小人笑的状态),并在onlbuttomup中设置位图按钮为正常状态,然后调用caculate函数记下周围雷的数目,最后调用invalidate使客户区无效,迫使ondraw函数重绘客户区域,在调用invalidate时不应重画背景,避免闪烁,这样就完成了在雷区按下左键的响应动作。

wm_rbuttomdown消息响应函数onlbuttomdown将被认为有雷位置的m_ibombnum.findbomb置一,减少左上角的雷记数,然后判断是否真正全部排完了雷,是则结束游戏d出input对话框,让扫雷的人输入姓名,在响应idok通知码时将其写入注册表,没有全部排完则使客户区无效,迫使ondraw函数重绘客户区域完成在雷区按下右键的动作。

ondraw函数在每次点击左键或右键时都会被调用重雷区和控制区域,因为点击情况的复杂性和雷属性的多元化导致ondraw函数需要精心设计。

函数caculate计算某个雷周围的雷数,根据前面的分析知道,计算某个雷周围的雷数本身就是一个递归过程,在编制时应注意递归的边界条件,稍不注意会陷入无穷递归而耗尽了系统的资源。

6. 菜单命令的响应是游戏交互的另一个重要方式,下面的九个命令响应函数分别与九个菜单项相对应,用以完成用户的更新和设置命令。

ongamebegin完成初始时间清零,随机布雷,依据颜色指示装载12幅小位图,使雷区无效调用ondraw重绘等工作。其中随机布雷就是多次调用rand(),根据其返回值决定m_bomb[i][j].isbomb的值。

ongamecustom首先d出ccustomer对话框,在用户输入设置后响应idok通知码时将用户输入的雷数、行列数分别赋给cview的数据成员m_ibombnum、m_irow、m_icolumn,得到框架窗口的指针,用其调用movewindow将窗口调至所需大小,销毁原位置的位图按钮,并在x轴坐标为新窗口宽1/2减8处,y轴坐标为新窗口顶部加30的位置创建一新按钮。最后调用ongamebegin重新开始游戏。

ongamejunior、ongamemiddle、ongamesenior三个函数与ongamecustom类似,只不过将分别赋给cview的数据成员m_ibombnum、m_irow、m_icolumn以固定的值,其大小可由编程者自定,笔者定为junior(20,8,8,)、middle(40,13,13)、senior(99,20,25)。

ongamecolor函数销毁原位图按钮,根据重新装载位图的标志m_iscolor来创建新的位图按钮,将装载12幅单色位图的标志取反,调用ongamebegin重新开始游戏。

ongamesort函数根据当前游戏级别从注册表中读出排名并d出sort对话框显示结果。到现在为止,一个自己编制的扫雷游戏就基本完成了,将数百行代码编译一下,找出小错误,最后build一遍,run一下,好了,可爱的扫雷游戏就出现在你的面前了。怎么样,自己的劳动成果并不比microsoft的差吧,而且你还可以把小位图画成各种样子,当然你自己要认得出才行了。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/8209067.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-14
下一篇 2023-04-14

发表评论

登录后才能评论

评论列表(0条)

保存