好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

C#带你玩扫雷(附源码)

扫雷游戏,大家都应该玩过吧!其实规则也很简单,可是我们想自己实现一个扫雷,我们应该怎么做呢?

step1: 知晓游戏原理

扫雷就是要把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。游戏主区域由很多个方格组成。使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷;如果点开的格子为空白格,即其周围有0颗雷,则其周围格子自动打开;如果其周围还有空白格,则会引发连锁反应;在你认为有雷的格子上,点击右键即可标记雷;如果一个已打开格子周围所有的雷已经正确标出,则可以在此格上同时点击鼠标左右键以打开其周围剩余的无雷格。

1代表1的上下左右及斜角合计有一颗雷,依次轮推,2则有2颗,3则有3颗..

在确实是炸弹的方格上点了旗子,就安全了,不是炸弹的被点了旗子,后面会被炸死的..问号就先不确定这里有没有炸弹,不会存在点错了被炸死的状况..

step2: 由step1可知,游戏由格子组成,翻译成代码语言就叫做数组,也就是游戏地图就是一个二维数组。格子对象,格子的值即当前雷的数量,那么此时我们暂定雷的数字标识为-1。除此之外,格子对象还有是否被显示,显示当前雷数量等属性,那么我们大概可以定义这样一个类:

?

public class cellblockrole

  {

    /// <summary>

    /// 位于游戏地图中的坐标点x

    /// </summary>

    public int x { get ; set ; }

 

    /// <summary>

    /// 位于游戏地图中的坐标点y

    /// </summary>

    public int y { get ; set ; }

 

    /// <summary>

    /// 是否展示最后格子所代表的结果

    /// </summary>

    public bool isshowresult { get ; set ; } = false ;

 

    /// <summary>

    /// 是否计算数字结果

    /// </summary>

    public bool iscomputeresult { get ; set ; } = false ;

 

    /// <summary>

    /// 是否已经展示过计算结果了

    /// </summary>

    public bool ishasshowcomputed { get ; set ; } = false ;

 

    /// <summary>

    /// 当前的格子的角色数字, -1:地雷,其他当前雷的数量

    /// </summary>

    public int number { set ; get ; } = 0;

 

    /// <summary>

    /// 是否被flag标识

    /// </summary>

    public bool isflag { get ; set ; } = false ;

 

    /// <summary>

    /// 是否是雷

    /// </summary>

    public bool isboom => number == -1;

 

  }

绘制游戏ui画面,见代码:

?

using system;

using system.collections.generic;

using system测试数据ponentmodel;

using system.drawing;

using system.data;

using system.linq;

using system.text;

using system.threading;

using system.threading.tasks;

using system.windows.forms;

using sweeperlibrary.properties;

using timer = system.threading.timer;

 

namespace sweeperlibrary

{

   public delegate void ongameoverdelegate();

 

   public delegate void onshowanumberdelegate();

 

   public delegate void onpublishgametimedelegate( string timedescription);

 

   public partial class gameview : usercontrol

   {

 

     /// <summary>

     /// 游戏结束事件

     /// </summary>

     public event ongameoverdelegate ongameoverevent;

 

     /// <summary>

     /// 当一个格子被点击时,显示当前数字的事件

     /// </summary>

     public event onshowanumberdelegate onshowanumberevent;

 

     /// <summary>

     /// 发布当前游戏的时间

     /// </summary>

     public event onpublishgametimedelegate onpublishgametimeevent;

 

     /// <summary>

     /// 游戏绘制地图的每个格子的大小

     /// </summary>

     public static readonly int cellsize = 40;

 

     /// <summary>

     /// 游戏规模n*n

     /// </summary>

     public static readonly int gamecellcount = 10;

 

     /// <summary>

     /// 移动方向坐标点改变的数组

     /// </summary>

     public static readonly int [][] movedirectionpoints = {

       new []{-1, -1},

       new [] {0, -1},

       new [] {1, -1},

       new [] {1, 0},

       new [] {1, 1},

       new [] {0, 1},

       new [] {-1, 1},

       new [] {-1, 0}

     };

 

     /// <summary>

     /// 随机数雷生成对象

     /// </summary>

     private static readonly random random = new random(guid.newguid().gethashcode());

     /// <summary>

     /// 游戏地图标识数组

     /// </summary>

     private cellblockrole[][] gamemap = new cellblockrole[gamecellcount][];

 

     /// <summary>

     /// 雷的数量,默认为10

     /// </summary>

     public int boomcount { get ; set ; } = 10;

 

     /// <summary>

     /// 游戏开始时间

     /// </summary>

     private datetime gamestarttime;

 

     /// <summary>

     /// 计时定时器

     /// </summary>

     private system.windows.forms.timer gametimer = new system.windows.forms.timer();

 

     public gameview()

     {

       initializecomponent();

       setstyle(controlstyles.optimizeddoublebuffer, true );

       setstyle(controlstyles.allpaintinginwmpaint, true );

       initgame(); //默认游戏已经开始

       setgametimer(); //设置游戏定时器

     }

 

     private void gameview_paint( object sender, painteventargs e)

     {

       width = gamecellcount + 1 + gamecellcount * cellsize;

       height = gamecellcount + 1 + gamecellcount * cellsize;

       //绘制游戏界面

       graphics graphics = e.graphics;

       graphics.clear(color.whitesmoke);

       if (gamemap != null && gamemap.length > 0 && gamemap[0] != null && gamemap[0].length > 0)

       {

         for ( int y = 0; y < gamecellcount; y++)

         {

           for ( int x = 0; x < gamecellcount; x++)

           {

             int dx = x + 1 + x * cellsize,

               dy = y + 1 + y * cellsize;

             cellblockrole cellblockrole = gamemap[y][x];

             graphics.fillrectangle( new solidbrush(cellblockrole.isshowresult ? color.lightslategray : color.whitesmoke),

               dx, dy, cellsize, cellsize);

             graphics.drawrectangle( new pen(color.lightgray), dx, dy, cellsize, cellsize);

             if (cellblockrole.isshowresult && cellblockrole.number != 0)

             {

               switch (cellblockrole.number)

               {

                 case -1: //雷

                   graphics.drawimage(image.fromhbitmap(resources.boom.gethbitmap()), new rectanglef(dx, dy, cellsize, cellsize));

                   break ;

                 default : //数字

                   string drawtext = cellblockrole.number.tostring();

                   font textfont = new font(fontfamily.genericsansserif, 12, fontstyle.bold);

                   sizef textsize = graphics.measurestring(drawtext, textfont);

                   graphics.drawstring(drawtext, textfont, new solidbrush(color.white),

                     dx + (cellsize - textsize.width) / 2, dy + (cellsize - textsize.height) / 2);

                   break ;

               }

             }

           }

         }

       }

     }

 

     private void gameview_mousedown( object sender, mouseeventargs e)

     {

       int px = (e.x - 1) / (cellsize + 1),

         py = (e.y - 1) / (cellsize + 1);

       switch (e.button)

       {

         case mousebuttons.left: //鼠标左键

           if (!gamemap[py][px].isshowresult)

           {

             if (gamemap[py][px].isboom)

             {

               new thread(() =>

               {

                 showallcellblockrolenumber();

                 if ( this .invokerequired)

                 {

                   methodinvoker del = invalidate;

                   this .invoke(del);

                 } else

                 {

                   invalidate();

                 }

               }).start();

               gametimer.stop();

               ongameoverevent?.invoke();

             } else

             {

               new thread(() =>

               {

                 showneiborhoodcellrolesbyposi(px, py);

                 if ( this .invokerequired)

                 {

                   methodinvoker del = invalidate;

                   this .invoke(del);

                 } else

                 {

                   invalidate();

                 }

               }).start();

               onshowanumberevent?.invoke();

             }

           }

           break ;

         case mousebuttons.right: //鼠标右键

           break ;

       }

     }

 

     /// <summary>

     /// 初始化游戏

     /// </summary>

     private void initgame()

     {

       new thread(() =>

       {

         initgamemap();

         generatebooms();

         if ( this .invokerequired)

         {

           methodinvoker del = invalidate;

           this .invoke(del);

         } else

         {

           invalidate();

         }

       }).start();

     }

 

     /// <summary>

     /// 设置游戏定时器

     /// </summary>

     private void setgametimer()

     {

       gametimer.interval = 1000;

       gametimer.enabled = true ;

       gametimer.tick += (sender, args) =>

       {

         long dmillisecond = datetime.now.millisecond - gamestarttime.millisecond;

         long hour = dmillisecond / 60 / 60 / 1000;

         long minute = (dmillisecond - hour * (60 * 60 * 1000)) / (60 * 1000);

         long second = ((dmillisecond - hour * (60 * 60 * 1000)) % (60 * 1000)) / 1000;

         onpublishgametimeevent?.invoke((hour > 0 ? (hour > 9 ? hour.tostring() : "0" + hour) + ":" : "" )

                         + (minute > 9 ? minute.tostring() : "0" + minute) + ":" + (second > 9 ? second.tostring() : "0" + second));

       };

     }

 

     /// <summary>

     /// 初始化游戏地图

     /// </summary>

     private void initgamemap()

     {

       for ( int i = 0; i < gamecellcount; i++)

       {

         gamemap[i] = new cellblockrole[gamecellcount];

         for ( int j = 0; j < gamecellcount; j++)

         {

           gamemap[i][j] = new cellblockrole

           {

             x = j,

             y = i

           };

         }

       }

       gamestarttime = datetime.now;

       gametimer.start();

     }

 

     /// <summary>

     /// 重置游戏地图

     /// </summary>

     public void resetgamemap()

     {

       new thread(() =>

       {

         for ( int i = 0; i < gamecellcount; i++)

         {

           for ( int j = 0; j < gamecellcount; j++)

           {

             gamemap[i][j].x = j;

             gamemap[i][j].y = i;

             gamemap[i][j].number = 0;

             gamemap[i][j].isshowresult = false ;

             gamemap[i][j].iscomputeresult = false ;

             gamemap[i][j].ishasshowcomputed = false ;

 

           }

         }

         generatebooms(); //生成一些雷

         if ( this .invokerequired)

         {

           methodinvoker del = invalidate;

           this .invoke(del);

         } else

         {

           invalidate();

         }

       }).start();

       gamestarttime = datetime.now;

       gametimer.start();

     }

 

     /// <summary>

     /// 随机生成一些地雷

     /// </summary>

     public void generatebooms()

     {

       for ( int i = 0; i < boomcount; i++)

       {

         int boomnumberindex = random.next(0, gamecellcount * gamecellcount - 1); //生成随机数的范围

         int boomx = boomnumberindex % gamecellcount,

           boomy = boomnumberindex / gamecellcount;

         if (gamemap[boomy][boomx].number == 0)

           gamemap[boomy][boomx].number = -1; //-1表示雷

         else // 已经存在雷了,所以要重新处理

           i--;

       }

       makeallnumbercomputeincellrole(0, 0); //默认从坐标(0,0)开始

     }

 

     /// <summary>

     /// 显示所有的格子的信息

     /// </summary>

     private void showallcellblockrolenumber()

     {

       for ( int i = 0; i < gamecellcount; i++)

       {

         for ( int j = 0; j < gamecellcount; j++)

         {

           gamemap[i][j].isshowresult = true ;

         }

       }

     }

 

     /// <summary>

     /// 显示某点周边所有格子的数字

     /// </summary>

     /// <param name="posix">x轴坐标</param>

     /// <param name="posiy">y轴坐标</param>

     private void showneiborhoodcellrolesbyposi( int posix, int posiy)

     {

       gamemap[posiy][posix].isshowresult = true ;

       gamemap[posiy][posix].ishasshowcomputed = true ;

       int boomcount = getboomcountinneiborhood(posix, posiy);

       if (boomcount == 0) //如果周围没有雷,则翻开所有8个方向的相关数字

       {

         for ( int i = 0; i < movedirectionpoints.length; i++)

         {

           int [] itemposi = movedirectionpoints[i];

           int rx = posix + itemposi[0],

             ry = posiy + itemposi[1];

           bool isnotoutindexrange = rx >= 0 && rx < gamecellcount && ry >= 0 && ry < gamecellcount;

           if (isnotoutindexrange) //防止坐标溢出

           {

             gamemap[ry][rx].isshowresult = true ;

             if (!gamemap[ry][rx].ishasshowcomputed && gamemap[ry][rx].number == 0)

               showneiborhoodcellrolesbyposi(rx, ry);

           }

         }

       }

     }

 

     /// <summary>

     /// 获取某点附近的雷数量

     /// </summary>

     /// <param name="posix">x轴坐标点</param>

     /// <param name="posiy">y轴坐标点</param>

     /// <returns></returns>

     private int getboomcountinneiborhood( int posix, int posiy)

     {

       int boomcount = 0;

       for ( int i = 0; i < movedirectionpoints.length; i++)

       {

         int [] itemposi = movedirectionpoints[i];

         int rx = posix + itemposi[0],

           ry = posiy + itemposi[1];

         bool isnotoutindexrange = rx >= 0 && rx < gamecellcount && ry >= 0 && ry < gamecellcount;

         if (isnotoutindexrange && gamemap[ry][rx].isboom) //防止坐标溢出

         {

           boomcount++;

         }

       }

       return boomcount;

     }

 

     /// <summary>

     /// 计算每个格子的数字标识

     /// </summary>

     /// <param name="posix">x轴坐标</param>

     /// <param name="posiy">y轴坐标</param>

     private void makeallnumbercomputeincellrole( int posix, int posiy)

     {

       int boomcount = getboomcountinneiborhood(posix, posiy);

       if (boomcount != 0) //如果周围没有雷,则计算周围的8个方向的格子

       {

         gamemap[posiy][posix].number = boomcount;

       } else

       {

         if (!gamemap[posiy][posix].isboom)

           gamemap[posiy][posix].number = 0;

       }

       gamemap[posiy][posix].iscomputeresult = true ;

       for ( int i = 0; i < movedirectionpoints.length; i++)

       {

         int [] itemposi = movedirectionpoints[i];

         int rx = posix + itemposi[0],

           ry = posiy + itemposi[1];

         bool isnotoutindexrange = rx >= 0 && rx < gamecellcount && ry >= 0 && ry < gamecellcount;

         if (isnotoutindexrange && !gamemap[ry][rx].iscomputeresult && !gamemap[ry][rx].isboom) //防止坐标溢出

         {

           makeallnumbercomputeincellrole(rx, ry);

         }

       }

     }

 

   }

 

}

主要代码已经实现,现已知现有代码的定时器由问题,暂时不支持flag(旗子标识)。当然代码中还有其他不足的地方,游戏持续优化中。。。

源代码地址: minesweeper-cshape.rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://HdhCmsTestjianshu测试数据/p/1c6be950992a

dy("nrwz");

查看更多关于C#带你玩扫雷(附源码)的详细内容...

  阅读:48次