C#窗体连连看小游戏(超详细)

成品链接

https://download.csdn.net/download/weixin_45525272/13713452

项目展示

在这里插入图片描述
在这里插入图片描述

各部分功能解释

一、项目分析

系统设计思路

(1)设计连连看功能类:LinkClass,Line
(2)设计两个事件:成功事件和失败事件
(3)成功事件:1.垂直事件2.水平事件3.一个拐点事件4.两个拐点事件
(4)失败事件:

  1. 一个按钮点击两次
  2. 点击的两个按钮所带图片不一样
  3. 点击两个具有相同图案的按钮但没有通路

(5)可以选择三种行和列:88,1010,12*12
(6)可以选择两种模式:数字,图片
(7)设计随机数并且随机数要配对
(8)赋予按钮应有的相关属性
(9)计时器设计,暂停按钮时计时器停止计时,开始按钮显示继续游戏
(10)每消除一对图片,计分加100
(11)重新排列功能,每重新排列一次总得分减400

二、项目设计

一、难度和模式
用两个comboBox分别设计行和列,图片或者数字模式


if (comboBox1.Text == ""|| comboBox2.Text=="")
{
   MessageBox.Show("请选择难度或者模式", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
   // 选择的关卡难度文本
   string Diffcult = comboBox1.Text;
   //根据对应难度初始化数组并给n赋值
   switch (Diffcult)
   {
       case "8*8":
           hang = 8;
           break;
       case "10*10":
           hang = 10;
           break;
       case "12*12":
           hang = 12;
           break; 
   }
//判断用户选的是图片还是数字
         string mul = comboBox2.Text;
         if (mul == "数字")
         {
             Button btnLinker = new Button() // 设置每个小按钮的相关属性
             {
                 Text = rnd.ToString(),//按钮上显示的是随机数
                 Tag = img,//标签
                 Name = string.Format("{0},{1}", row, col),//每个按钮命名
                 Dock = DockStyle.Fill,//调整控件层次,填充
                 Margin = new Padding(1),//调整外边距
             };
             btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
             this.TableLp.Controls.Add(btnLinker, col, row);//将按钮加进去并赋给位置
         }
         else//即用户选择了图片
         {
             Button btnLinker = new Button() // 设置每个小按钮的相关属性
             {
                 BackgroundImage = GetImageByName(img),//给按钮填充背景图片
                 BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉申
                 Tag = img,
                 Name = string.Format("{0},{1}", row, col),
                 Dock = DockStyle.Fill,
                 Margin = new Padding(1),
             };
             btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
             this.TableLp.Controls.Add(btnLinker, col, row);
         }
     }
   }
   this.LinkObj.LinkBoard = linkBoard;//初始化棋盘
   this.TableLp.Enabled = false;

2、进度条分数模块
用计时器线程来计时,进行进度条与时间标签的实时更新


        // 计时器线程
        Thread timeDelay;       // 线程变量
        int timetemp = 0;       // 计时器中间变量
 // 定时器事件:时间剩余
        void timeDelay_Tick()
        {
            //ma.WaitOne(); // 根据是否收到信号判断是否阻塞当前线程
            while (this.curDelay > 0 )   // 当前时间大于零
            {
                if (!isClosing)
                {
                    this.curDelay--;
                    this.pbTimeDelay.Value = this.curDelay;
                    this.lblTimeDelay.Text = this.curDelay.ToString();
                    
                }
                Thread.Sleep(1000);
            }
            if (!isClosing)
            {
              
                this.btnStart.Enabled = true;
                this.TableLp.Enabled = false;
              
            }
        }

3、开始游戏(游戏初始化)
清除残余,并且设置新的表格

this.TableLp.ColumnStyles.Clear();
                this.TableLp.RowStyles.Clear();
                this.TableLp.Controls.Clear();
this.TableLp.ColumnCount = hang + 2;
                this.TableLp.RowCount = hang + 2;

设置按钮及边框所占的比列
for (int i = 0; i < hang + 2; i++)
                {
                    //设置等宽等高  边缘2,里面内容10
                    if (i == 0 || i == hang + 1)
                    {
                        this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 2));//占总画面的2%
                        this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 2));
                    }
                    else
                    {
                        this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10));//占总画面的10%
                        this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 10));
                    }
                }

为每个按钮赋给图片或者相应的数字
 string img = string.Format("_1_{0}", rnd.ToString("00"));//给图片命名
                        linkBoard[row, col] = rnd;//赋给每个位置随机数字
                        //判断用户选的是图片还是数字
                        string mul = comboBox2.Text;
                        if (mul == "数字")
                        {
                            Button btnLinker = new Button() // 设置每个小按钮的相关属性
                            {
                                Text = rnd.ToString(),//按钮上显示的是随机数
                                Tag = img,//标签
                                Name = string.Format("{0},{1}", row, col),//每个按钮命名
                                Dock = DockStyle.Fill,//调整控件层次,填充
                                Margin = new Padding(1),//调整外边距
                            };
                            btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
                            this.TableLp.Controls.Add(btnLinker, col, row);//将按钮加进去并赋给位置
                        }
                        else//即用户选择了图片
                        {
                            Button btnLinker = new Button() // 设置每个小按钮的相关属性
                            {
                                BackgroundImage = GetImageByName(img),//给按钮填充背景图片
                                BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉申
                                Tag = img,
                                Name = string.Format("{0},{1}", row, col),
                                Dock = DockStyle.Fill,
                                Margin = new Padding(1),
                            };
                            btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
                            this.TableLp.Controls.Add(btnLinker, col, row);
                        }
                    }
  }

4、暂停游戏
通过将当前时间的变量赋值为0来结束当前计时器线程,并且当点击继续游戏的时候,通过开启新线程来重新运行计数功能

暂停

        private void btnPause_Click(object sender, EventArgs e)
        {
            this.TableLp.Enabled = false;
  
            btnStart.Enabled = true;
            btnStart.Text = "继续游戏";
            //ma.Reset();//信号关闭阻塞当前线程

            timetemp = this.curDelay;
            this.curDelay = 0;
            lblTimeDelay.Text = timetemp.ToString();
       }

继续

 else if (btnStart.Text == "继续游戏")
            {
                //ma.Set();//信号打开,不阻塞当前线程
                btnStart.Text = "开始游戏";
                btnStart.Enabled = false;
                this.TableLp.Enabled = true;
                this.curDelay= timetemp;// 将中间变量赋值给新的当前时间
                lblTimeDelay.Text = this.curDelay.ToString();
                timeDelay = new Thread(new ThreadStart(this.timeDelay_Tick));//新的计时器
                timeDelay.Start();
            }

5、重新排列
设置随机二维数组,将原来的数字所在的行和列用随机的二维数组替代

int rnd, rnd2;
                // 打乱二维数组顺序
                for (int row = 1; row < hang + 1; row++)
                {
                    for (int col = 1; col < hang + 1; col++)
                    {
                        // 产生随机数
                        rnd = r.Next(1, 1000) % hang + 1;
                        rnd2 = r.Next(1, 1000) % hang + 1;

                        int temp = linkBoard[rnd, rnd2];
                        linkBoard[rnd, rnd2] = linkBoard[row, col];
                        linkBoard[row, col] = temp;

                    }
                }

6、判断连接的核心算法
(1)垂直事件
获取按钮1和按钮2的y值,并利用循环来判断按钮1和按钮2之间是否为通路

 private bool vertical(Point a, Point b)
        {
            int col_start = a.Y < b.Y ? a.Y : b.Y;    //获取a,b中较小的y值
            int col_end = a.Y < b.Y ? b.Y : a.Y;      //获取a,b中较大的y值

            //遍历a,b之间是否通路,如果一个不是就返回false;
            for (int i = col_start + 1; i < col_end; i++)
            {
                if (this.LinkBoard[a.X, i] != 0)//0是空的状态(即没有按钮)
                {
                    return false;
                }
            }
            return true;
        }

(2)水平事件
获取按钮1和按钮2的x值,并利用循环来判断按钮1和按钮2之间是否为通路

private bool horizon(Point a, Point b)
        {
            int row_start = a.X < b.X ? a.X : b.X;//获取a,b中较小的x值
            int row_end = a.X < b.X ? b.X : a.X;//获取a,b中较大的x值
            for (int i = row_start + 1; i < row_end; i++)
            {
                if (this.LinkBoard[i, a.Y] != 0)//0是空的状态(即没有按钮)
                {
                    return false;//return是函数结束
                }
            }
            return true;
        }

(3)一个拐点事件
首先判断拐点位置是否有按钮,若没有,将剩下部分看做由一个垂直事件和一个水平事件组成

private bool oneCorner(Point a, Point b)
        {
            // 拐角点的x,y分别等于两点各个x,y
            Point c = new Point(b.X, a.Y);
            Point d = new Point(a.X, b.Y);
            // 判断C点是否有元素,有就继续执行下面判断条件,没有就看a与c,b与c之间是否能水平或者垂直连线                
            if (this.LinkBoard[c.X, c.Y] == 0)
            {
                bool path1 = vertical(b, c) && horizon(a, c);
                return path1;   //path1本身就是bool值
            }

            //判断D点是否有元素
            if (this.LinkBoard[d.X, d.Y] == 0)
            {
                bool path2 = vertical(a, d) && horizon(b, d);
                return path2;//path2本身就是bool值
            }
            
            // 能执行到这就说明前面判断不成功,不存在一个拐点的情况
            return false;
           
        }

(4)两个拐点事件
首先判断两个拐点是都有按钮,并且判断两个拐点间是否为通路,此过程用到新建的scan函数,剩下即有两个水平事件或者两个垂直事件构成

private List<Line> scan(Point a, Point b)
        {
            List<Line> linkList = new List<Line>();// 新建一个直线对象List容器,用来存成立的拐点之间的直线

            //检测a点上方是否能够水平直连
            for (int i = a.Y; i >= 0; i--)
            {
                if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i)))
                {
                    linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));
                }
            }

            //检测a点下方是否能够水平直连
            for (int i = a.Y; i < Col; i++)
            {
                if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i)))
                {
                    linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));
                }
            }

            //检测a点左侧是否能够垂直直连
            for (int j = a.X; j >= 0; j--)
            {
                if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y)))
                {
                    linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));
                }
            }

            //检测a点右侧是否能够垂直直连
            for (int j = a.X; j < Row; j++)
            {
                if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y)))
                {
                    linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));

                }
            }

            return linkList;
        }
}
 private bool twoCorner(Point a, Point b)
        {
            // list容器:相当于一个Line类型的数组,存扫到的连通路径
            List<Line> ll = scan(a, b);//此时的直线已经判断成功并且拐点位置没有数字
            if (ll.Count == 0)  // 没扫到返回false,List容器里面的计数方法
            {
                return false;
            }

            for (int i = 0; i < ll.Count; i++)
            {
                Line tempLine = ll[i];  // 临时连线变量用于判断拐点与a,b的通路是否成立
                if (tempLine.direct == 1)   // 两个拐点之间是水平直连
                {// 判断分别两个拐点与a,b能否相连
                    if (horizon(a, tempLine.a) && horizon(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路
                    {
                        return true;
                    }
                }
                else if (tempLine.direct == 0)  // 两个拐点之间是垂直直连
                {// 判断分别两个拐点与a,b能否相连
                    if (vertical(a, tempLine.a) && vertical(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路
                    {
                        return true;
                    }
                }
            }
            return false;
        }

7.重新排列
将原本储存的二维数组重新打乱顺序,在按照打乱后的数组动态生成按钮

  // 打乱二维数组顺序
      for (int row = 1; row < hang + 1; row++)
      {
          for (int col = 1; col < hang + 1; col++)
          {
              // 产生随机数
              rnd = r.Next(1, 1000) % hang + 1;
              rnd2 = r.Next(1, 1000) % hang + 1;

              int temp = linkBoard[rnd, rnd2];
              linkBoard[rnd, rnd2] = linkBoard[row, col];
              linkBoard[row, col] = temp;

          }
      }

8.连接成功判断
用两个循环挨个遍历数组元素并计数,最后判断计数是否够元素的个数

        // 判断成功
        private void Judge()
        {
            int count=0;
            // 将内置元素进行随机数分配等操作
            for (int row = 1; row < hang + 1; row++)
            {
                for (int col = 1; col < hang + 1; col++)
                {
                    if (this.LinkObj.LinkBoard[row,col]==0)
                    {
                        count++;
                    }
                }
            }
            if (count==hang*hang)
            {
                MessageBox.Show("拼完了,你可真厉害!","提示",MessageBoxButtons.OK,MessageBoxIcon.Asterisk);
                this.TableLp.Enabled = false;
                btnStart.Enabled = true;

                timetemp = this.curDelay;
                this.curDelay = 0;
                lblTimeDelay.Text = timetemp.ToString();
            }
        }

全部代码

窗体控件命名

namespace Linkup
{
    partial class MainWindow
    {
        /// <summary>
        /// 必需的设计器变量。
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
        /// 清理所有正在使用的资源。
        /// </summary>
        /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows 窗体设计器生成的代码

        /// <summary>
        /// 设计器支持所需的方法 - 不要修改
        /// 使用代码编辑器修改此方法的内容。
        /// </summary>
        private void InitializeComponent()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
            this.TableLp = new System.Windows.Forms.TableLayoutPanel();
            this.btnStart = new System.Windows.Forms.Button();
            this.pbTimeDelay = new System.Windows.Forms.ProgressBar();
            this.lblTimeDelay = new System.Windows.Forms.Label();
            this.lableScore = new System.Windows.Forms.Label();
            this.btnStop = new System.Windows.Forms.Button();
            this.comboBox1 = new System.Windows.Forms.ComboBox();
            this.btnPause = new System.Windows.Forms.Button();
            this.btnReset = new System.Windows.Forms.Button();
            this.comboBox2 = new System.Windows.Forms.ComboBox();
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // TableLp
            // 
            this.TableLp.ColumnCount = 1;
            this.TableLp.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.TableLp.Location = new System.Drawing.Point(61, 12);
            this.TableLp.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.TableLp.Name = "TableLp";
            this.TableLp.RowCount = 1;
            this.TableLp.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.TableLp.Size = new System.Drawing.Size(534, 534);
            this.TableLp.TabIndex = 0;
            // 
            // btnStart
            // 
            this.btnStart.Location = new System.Drawing.Point(697, 44);
            this.btnStart.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(243, 36);
            this.btnStart.TabIndex = 1;
            this.btnStart.Text = "开始游戏";
            this.btnStart.UseVisualStyleBackColor = true;
            this.btnStart.Click += new System.EventHandler(this.btnStart_Click);
            // 
            // pbTimeDelay
            // 
            this.pbTimeDelay.Location = new System.Drawing.Point(61, 573);
            this.pbTimeDelay.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.pbTimeDelay.Name = "pbTimeDelay";
            this.pbTimeDelay.Size = new System.Drawing.Size(451, 19);
            this.pbTimeDelay.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
            this.pbTimeDelay.TabIndex = 2;
            // 
            // lblTimeDelay
            // 
            this.lblTimeDelay.AutoSize = true;
            this.lblTimeDelay.Location = new System.Drawing.Point(542, 573);
            this.lblTimeDelay.Name = "lblTimeDelay";
            this.lblTimeDelay.Size = new System.Drawing.Size(15, 15);
            this.lblTimeDelay.TabIndex = 3;
            this.lblTimeDelay.Text = "0";
            // 
            // lableScore
            // 
            this.lableScore.AutoSize = true;
            this.lableScore.Location = new System.Drawing.Point(724, 573);
            this.lableScore.Name = "lableScore";
            this.lableScore.Size = new System.Drawing.Size(60, 15);
            this.lableScore.TabIndex = 4;
            this.lableScore.Text = "分数:0";
            // 
            // btnStop
            // 
            this.btnStop.Location = new System.Drawing.Point(697, 160);
            this.btnStop.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(243, 36);
            this.btnStop.TabIndex = 1;
            this.btnStop.Text = "结束游戏";
            this.btnStop.UseVisualStyleBackColor = true;
            this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
            // 
            // comboBox1
            // 
            this.comboBox1.FormattingEnabled = true;
            this.comboBox1.Location = new System.Drawing.Point(780, 318);
            this.comboBox1.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Size = new System.Drawing.Size(160, 23);
            this.comboBox1.TabIndex = 5;
            // 
            // btnPause
            // 
            this.btnPause.Location = new System.Drawing.Point(697, 102);
            this.btnPause.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.btnPause.Name = "btnPause";
            this.btnPause.Size = new System.Drawing.Size(243, 36);
            this.btnPause.TabIndex = 1;
            this.btnPause.Text = "暂停游戏";
            this.btnPause.UseVisualStyleBackColor = true;
            this.btnPause.Click += new System.EventHandler(this.btnPause_Click);
            // 
            // btnReset
            // 
            this.btnReset.Location = new System.Drawing.Point(697, 218);
            this.btnReset.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.btnReset.Name = "btnReset";
            this.btnReset.Size = new System.Drawing.Size(243, 36);
            this.btnReset.TabIndex = 1;
            this.btnReset.Text = "重新排列";
            this.btnReset.UseVisualStyleBackColor = true;
            this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
            // 
            // comboBox2
            // 
            this.comboBox2.FormattingEnabled = true;
            this.comboBox2.Location = new System.Drawing.Point(780, 367);
            this.comboBox2.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.comboBox2.Name = "comboBox2";
            this.comboBox2.Size = new System.Drawing.Size(160, 23);
            this.comboBox2.TabIndex = 5;
            // 
            // label1
            // 
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(694, 321);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(52, 15);
            this.label1.TabIndex = 6;
            this.label1.Text = "难度:";
            // 
            // label2
            // 
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(694, 372);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(52, 15);
            this.label2.TabIndex = 6;
            this.label2.Text = "模式:";
            // 
            // MainWindow
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
            this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
            this.ClientSize = new System.Drawing.Size(1008, 652);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.comboBox2);
            this.Controls.Add(this.comboBox1);
            this.Controls.Add(this.lableScore);
            this.Controls.Add(this.lblTimeDelay);
            this.Controls.Add(this.pbTimeDelay);
            this.Controls.Add(this.btnReset);
            this.Controls.Add(this.btnPause);
            this.Controls.Add(this.btnStop);
            this.Controls.Add(this.btnStart);
            this.Controls.Add(this.TableLp);
            this.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);
            this.Name = "MainWindow";
            this.Text = "Form1";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.TableLayoutPanel TableLp;
        private System.Windows.Forms.Button btnStart;
        private System.Windows.Forms.ProgressBar pbTimeDelay;
        private System.Windows.Forms.Label lblTimeDelay;
        private System.Windows.Forms.Label lableScore;
        private System.Windows.Forms.Button btnStop;
        private System.Windows.Forms.ComboBox comboBox1;
        private System.Windows.Forms.Button btnPause;
        private System.Windows.Forms.Button btnReset;
        private System.Windows.Forms.ComboBox comboBox2;
        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
    }
}


连连看功能类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;

namespace Linkup
{
    // 连连看功能类
    public class LinkClass
    {
        // 连连看 看板
        public int[,] LinkBoard { get; set; }//命名为LinkBoard的连连看看板

        // 连线成功事件
        public event EventHandler SucClick;
        // 连接失败事件
        public event EventHandler FailClick;

        //Col是行
        private int col = 10;
        public int Col 
        {
            get
            {
                return col;
            }
            set
            {
                col = value;
            }
        }

        // Row是列
        private int row = 10;
        public int Row
        {
            get
            {
                return row;
            }
            set
            {
                row = value;
            }
        }

        // 连接测试函数
        public void LinkLine(Point first,Point second)//点击的第一个点为first;点击的第二个点为second
        {
            // EventArgs:事件参数
            EventArgs e = new EventArgs();
            if (checkLink(first, second))   // 判断是否连接成功
            {
                // 连线成功
                this.LinkBoard[first.X, first.Y] = 0;//清除记忆中的first,以便下一次消除
                this.LinkBoard[second.X, second.Y] = 0;//清除记忆中的second,以便下一次消除
                if (this.SucClick != null)  // 给事件添加事件参数
                {
                    SucClick(this, e); 
                }
            }
            else
            {
                // 连线失败
                if (this.FailClick != null)//给事件添加事件参数
                {
                    FailClick(this, e);
                }
            }


        }

        // 是否赋
        public bool IsChecked(Point p)
        {
            bool flag = false;
            if (p.X != -1 && p.Y != -1)
            {
                flag = true;
            }
            return flag;
        }


        // 判断是否连接成功——核心算法
        /*
            a,第一个点   b,第二个点
         */
        private bool checkLink(Point a,Point b)
        {
            if (!Point.Equals(a,b)) // 如果点击的不是同一位置
            {
                // 如果点击的不是同一位置
                if (this.LinkBoard[a.X, a.Y] == this.LinkBoard[b.X, b.Y])//如果两者为同一图案
                {
                    // 判断连线能否成功
                    if (a.X == b.X && vertical(a, b))//垂直
                    {
                        return true;
                    }
                    if (a.Y == b.Y && horizon(a, b))//水平
                    {
                        return true;
                    }
                    if (oneCorner(a, b))//一个拐点
                    {
                        return true;
                    }
                    else//两个拐点
                    {
                        return twoCorner(a, b);
                    }
                }
                else
                {
                    // 如果点击的不是同一个图案,直接返回false
                    return false;
                }
            }
            else
            {
                // 如果点击的是同一个位置的图案,直接返回false;
                return false;
            }

        }


        // 垂直的连线情况
        private bool vertical(Point a, Point b)
        {
            int col_start = a.Y < b.Y ? a.Y : b.Y;    //获取a,b中较小的y值
            int col_end = a.Y < b.Y ? b.Y : a.Y;      //获取a,b中较大的y值

            //遍历a,b之间是否通路,如果一个不是就返回false;
            for (int i = col_start + 1; i < col_end; i++)
            {
                if (this.LinkBoard[a.X, i] != 0)//0是空的状态(即没有按钮)
                {
                    return false;
                }
            }
            return true;
        }

        // 水平的连线情况
        private bool horizon(Point a, Point b)
        {
            int row_start = a.X < b.X ? a.X : b.X;//获取a,b中较小的x值
            int row_end = a.X < b.X ? b.X : a.X;//获取a,b中较大的x值
            for (int i = row_start + 1; i < row_end; i++)
            {
                if (this.LinkBoard[i, a.Y] != 0)//0是空的状态(即没有按钮)
                {
                    return false;//return是函数结束
                }
            }
            return true;
        }

        // 一个拐角连线情况
        private bool oneCorner(Point a, Point b)
        {
            // 拐角点的x,y分别等于两点各个x,y
            Point c = new Point(b.X, a.Y);
            Point d = new Point(a.X, b.Y);
            // 判断C点是否有元素,有就继续执行下面判断条件,没有就看a与c,b与c之间是否能水平或者垂直连线                
            if (this.LinkBoard[c.X, c.Y] == 0)
            {
                bool path1 = vertical(b, c) && horizon(a, c);
                return path1;   //path1本身就是bool值
            }

            //判断D点是否有元素
            if (this.LinkBoard[d.X, d.Y] == 0)
            {
                bool path2 = vertical(a, d) && horizon(b, d);
                return path2;//path2本身就是bool值
            }
            
            // 能执行到这就说明前面判断不成功,不存在一个拐点的情况
            return false;
           
        }

        // 两个拐点
        private bool twoCorner(Point a, Point b)
        {
            // list容器:相当于一个Line类型的数组,存扫到的连通路径
            List<Line> ll = scan(a, b);//此时的直线已经判断成功并且拐点位置没有数字
            if (ll.Count == 0)  // 没扫到返回false,List容器里面的计数方法
            {
                return false;
            }

            for (int i = 0; i < ll.Count; i++)
            {
                Line tempLine = ll[i];  // 临时连线变量用于判断拐点与a,b的通路是否成立
                if (tempLine.direct == 1)   // 两个拐点之间是水平直连
                {// 判断分别两个拐点与a,b能否相连
                    if (horizon(a, tempLine.a) && horizon(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路
                    {
                        return true;
                    }
                }
                else if (tempLine.direct == 0)  // 两个拐点之间是垂直直连
                {// 判断分别两个拐点与a,b能否相连
                    if (vertical(a, tempLine.a) && vertical(b, tempLine.b))//判断a与a"之间是否有通路;判断b与b"之间是否有通路
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        // 扫描A与B之间的连接点组成的线(scan)
        //List为容器
        private List<Line> scan(Point a, Point b)
        {
            List<Line> linkList = new List<Line>();// 新建一个直线对象List容器,用来存成立的拐点之间的直线

            //检测a点上方是否能够水平直连
            for (int i = a.Y; i >= 0; i--)
            {
                if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i)))
                {
                    linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));
                }
            }

            //检测a点下方是否能够水平直连
            for (int i = a.Y; i < Col; i++)
            {
                if (this.LinkBoard[a.X, i] == 0 && this.LinkBoard[b.X, i] == 0 && horizon(new Point(a.X, i), new Point(b.X, i)))
                {
                    linkList.Add(new Line(new Point(a.X, i), new Point(b.X, i), 0));
                }
            }

            //检测a点左侧是否能够垂直直连
            for (int j = a.X; j >= 0; j--)
            {
                if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y)))
                {
                    linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));
                }
            }

            //检测a点右侧是否能够垂直直连
            for (int j = a.X; j < Row; j++)
            {
                if (this.LinkBoard[j, a.Y] == 0 && this.LinkBoard[j, b.Y] == 0 && vertical(new Point(j, a.Y), new Point(j, b.Y)))
                {
                    linkList.Add(new Line(new Point(j, a.Y), new Point(j, b.Y), 1));

                }
            }

            return linkList;
        }
    }

    // 两个拐点之间构成连线类
    public class Line
    {
        public Point a;
        public Point b;
        public int direct; //连线方向———  1:水平直连   0:垂直直连
        public Line(Point a, Point b, int direct)//Line是个构造函数
        {
            this.a = a;
            this.b = b;
            this.direct = direct;
        }

    }
}


窗体功能实现

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Linkup
{
    public partial class MainWindow : Form
    {
        private int curDelay = 0;                           // 当前剩余时间
        private LinkClass LinkObj = new LinkClass();        // 连连看功能类对象(变量),以后在这对象层面上操作
        private Point first = new Point(-1, -1);            // 第一次点击的位置
        private Point second = new Point(-1, -1);           // 第二次点击的位置
        private int score = 0;                              // 分数
        private bool isClosing = false;                     // 界面是否关闭

        public MainWindow()
        {
            InitializeComponent();

            //以下采用双缓冲方式,减少闪烁
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            this.SetStyle(ControlStyles.UserPaint, true);

            // 给该对象添加点击事件
            this.LinkObj.SucClick += Link_SucClick;
            this.LinkObj.FailClick += Link_FailClick;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //在combox加载三种关卡
            comboBox1.Items.Add("8*8");
            comboBox1.Items.Add("10*10");
            comboBox1.Items.Add("12*12");

            comboBox2.Items.Add("图片");
            comboBox2.Items.Add("数字");
        }

        // 初始化连连看数组
        int hang = 0;   // 行列数
        private void InitLinkGameOne()
        {
            // 难度设置
            if (comboBox1.Text == ""|| comboBox2.Text=="")
            {
                MessageBox.Show("请选择难度或者模式", "提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                // 选择的关卡难度文本
                string Diffcult = comboBox1.Text;
                //根据对应难度初始化数组并给n赋值
                switch (Diffcult)
                {
                    case "8*8":
                        hang = 8;
                        break;
                    case "10*10":
                        hang = 10;
                        break;
                    case "12*12":
                        hang = 12;
                        break; 
                }

                // 用于产生不重复随机数
                long tick = DateTime.Now.Ticks;
                Random r = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));
                // 棋盘周围空出一圈0
                int[,] linkBoard = new int[hang + 2, hang + 2];
                this.LinkObj.Row = hang + 2;
                this.LinkObj.Col = hang + 2;

                // 将内置元素进行随机数分配等操作
                for (int row = 1; row < hang + 1; row++)
                {
                    for (int col = 1; col < (hang + 1)/2+1; col++)
                    {
                        int rnd;
                        switch (hang)
                        {
                            case 8:
                                rnd = r.Next(1, 1000) % 13 + 1;//13种图
                                break;
                            case 10:
                                rnd = r.Next(1, 1000) % 15 + 1;//15种图
                                break;
                            case 12:
                                rnd = r.Next(1, 1000) % 17 + 1;//17种图
                                break;
                            default:
                                rnd = 0;
                                break;
                        }
                        // n种图

                        string img = string.Format("_1_{0}", rnd.ToString("00"));//给图片命名

                        linkBoard[row, col] = rnd;                      // 赋给每个位置随机数字
                        linkBoard[row, (hang + 1) / 2 + col] = rnd;     // 赋给相对位置随机数字
                    }
                }
                this.LinkObj.LinkBoard = linkBoard;//初始化棋盘
                this.TableLp.Enabled = false;

                // 控件变量等初始化
                this.btnStart.Enabled = false;
                //设置时间以及按钮
                this.curDelay = pbTimeDelay.Maximum;//让剩余时间为计时条的最大值
                this.pbTimeDelay.Value = this.curDelay;//给进度条赋值
                this.lblTimeDelay.Text = this.curDelay.ToString();//显示剩余时间
                this.TableLp.Enabled = true;
                score = 400;//计分

                // 启动用多线程计时器函数
                timeDelay = new Thread(timeDelay_Tick);
                //ma.Set();
                timeDelay.Start();  //启动定时器线程
            }

        }

        // 获取图片名
        internal static Bitmap GetImageByName(string name)
        {
            // 全局资源搜索
            object obj = global::Linkup.Properties.Resources.ResourceManager.GetObject(name, global::Linkup.Properties.Resources.Culture);
            return ((Bitmap)(obj));
        }

        // 打乱顺序函数
        private void Reset()
        {
            //设置tableLayoutPanel的列数和行数
            this.TableLp.ColumnCount = hang + 2;
            this.TableLp.RowCount = hang + 2;
            // 清除残余属性
            this.TableLp.ColumnStyles.Clear();
            this.TableLp.RowStyles.Clear();
            this.TableLp.Controls.Clear();

            for (int i = 0; i < hang + 2; i++)
            {
                //设置等宽等高  边缘2,里面内容10
                if (i == 0 || i == hang + 1)
                {
                    this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 2));
                    this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 2));
                }
                else
                {
                    this.TableLp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 10));
                    this.TableLp.RowStyles.Add(new RowStyle(SizeType.Percent, 10));
                }
            }
            this.TableLp.Padding = new Padding(1);//约束控件内边距
            this.TableLp.CellBorderStyle = TableLayoutPanelCellBorderStyle.None;    // 不显示边框线
            long tick = DateTime.Now.Ticks;
            // 用于产生不重复随机数
            Random r = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32));

            int[,] linkBoard = new int[hang + 2, hang + 2];

            linkBoard = this.LinkObj.LinkBoard;//将这个对象的数组值赋给这个变量,便于操作
                                               // 将内置元素进行随机贴图等操作
            int rnd, rnd2;
            // 打乱二维数组顺序
            for (int row = 1; row < hang + 1; row++)
            {
                for (int col = 1; col < hang + 1; col++)
                {
                    // 产生随机数
                    rnd = r.Next(1, 1000) % hang + 1;
                    rnd2 = r.Next(1, 1000) % hang + 1;

                    int temp = linkBoard[rnd, rnd2];
                    linkBoard[rnd, rnd2] = linkBoard[row, col];
                    linkBoard[row, col] = temp;

                }
            }
            this.LinkObj.LinkBoard = linkBoard;//初始化棋盘

            for (int row = 1; row < hang + 1; row++)
            {
                for (int col = 1; col < hang + 1; col++)
                {
                    if (linkBoard[row, col] != 0)
                    {
                        if (comboBox2.Text=="图片")
                        {
                            string img = string.Format("_1_{0}", linkBoard[row, col].ToString("00"));//给图片命名
                            Button btnLinker = new Button() // 设置每个小按钮的相关属性
                            {
                                BackgroundImage = GetImageByName(img),//给按钮填充背景图片
                                                                      //Text = rnd.ToString(),
                                BackgroundImageLayout = ImageLayout.Stretch,//背景图片拉伸
                                Tag = img,//标签
                                Name = string.Format("{0},{1}", row, col),//将按钮的名字分成为x,y
                                Dock = DockStyle.Fill,//调整控件的层次,填充
                                Margin = new Padding(1),//调整外边距
                            };
                            btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
                            this.TableLp.Controls.Add(btnLinker, col, row);
                        }
                        else
                        {
                            string img = string.Format("_1_{0}", linkBoard[row, col].ToString("00"));//给图片命名
                            Button btnLinker = new Button() // 设置每个小按钮的相关属性
                            {
                                Text = linkBoard[row, col].ToString(),
                                Tag = img,//标签
                                Name = string.Format("{0},{1}", row, col),//将按钮的名字分成为x,y
                                Dock = DockStyle.Fill,//调整控件的层次,填充
                                Margin = new Padding(1),//调整外边距
                            };
                            btnLinker.Click += BtnLinker_Click; // 让每个按钮都拥有点击事件
                            this.TableLp.Controls.Add(btnLinker, col, row);
                        }
                        
                    }
                }
            }

            score -= 400;
            lableScore.Text = "分数:" + score.ToString();
        }

        // 判断成功
        private void Judge()
        {
            int count=0;
            // 将内置元素进行随机数分配等操作
            for (int row = 1; row < hang + 1; row++)
            {
                for (int col = 1; col < hang + 1; col++)
                {
                    if (this.LinkObj.LinkBoard[row,col]==0)
                    {
                        count++;
                    }
                }
            }
            if (count==hang*hang)
            {
                MessageBox.Show("拼完了,你可真厉害!","提示",MessageBoxButtons.OK,MessageBoxIcon.Asterisk);
                this.TableLp.Enabled = false;
                btnStart.Enabled = true;

                timetemp = this.curDelay;
                this.curDelay = 0;
                lblTimeDelay.Text = timetemp.ToString();
            }
        }

        // 事件
        // 每个小图片按钮单击事件
        private void BtnLinker_Click(object sender, EventArgs e)
        {
            Button btnLinker = (Button)sender;                  // 事件参数
            btnLinker.FlatStyle = FlatStyle.Flat;               // 决定按钮的外观
            btnLinker.FlatAppearance.BorderColor = Color.Red;   // 按钮外边框颜色
            btnLinker.FlatAppearance.BorderSize = 2;            // 按钮外边框的大小
            string[] loc = btnLinker.Name.Split(',');           // 将按钮的名字分成x,y
            if (!LinkObj.IsChecked(first))                      // 第一次点给第一个点赋值
            {
                first = new Point(int.Parse(loc[0]), int.Parse(loc[1]));
            }
            else// 第二次点给第二个参数赋值
            {
                second = new Point(int.Parse(loc[0]), int.Parse(loc[1]));
                // 如果就第二次点击,则进行判断
                LinkObj.LinkLine(first, second);
            }
        }

        // 计时器线程
        Thread timeDelay;       // 线程变量
        int timetemp = 0;       // 计时器中间变量

        // 开始按钮事件
        private void btnStart_Click(object sender, EventArgs e)
        {
            if (btnStart.Text=="开始游戏")
            {
                // 先初始化连连看板
                InitLinkGameOne();
                // 打乱顺序
                Reset();
            }
            else if (btnStart.Text == "继续游戏")
            {
                //ma.Set();//信号打开,不阻塞当前线程
                btnStart.Text = "开始游戏";
                btnStart.Enabled = false;
                this.TableLp.Enabled = true;
                this.curDelay= timetemp;// 将中间变量赋值给新的当前时间
                lblTimeDelay.Text = this.curDelay.ToString();
                timeDelay = new Thread(new ThreadStart(this.timeDelay_Tick));//新的计时器
                timeDelay.Start();
            }
            
        }

        // 结束按钮
        private void btnStop_Click(object sender, EventArgs e)
        {
            this.curDelay = 0;
            Thread.Sleep(100);
            btnStart.Text = "开始游戏";
        }

        // 重新排列
        private void btnReset_Click(object sender, EventArgs e)
        {
            // 扣分
            if (score<= -400)
            {
                MessageBox.Show("你已经负债累累,不能再扣分了!","提示",MessageBoxButtons.OK,MessageBoxIcon.Stop);
            }
            else
            {
                Reset();
            }
        }

        // 暂停按钮
        private void btnPause_Click(object sender, EventArgs e)
        {
            this.TableLp.Enabled = false;
  
            btnStart.Enabled = true;
            btnStart.Text = "继续游戏";

            timetemp = this.curDelay;
            this.curDelay = 0;
            lblTimeDelay.Text = timetemp.ToString();
        }

        // 定时器事件:时间剩余
        void timeDelay_Tick()
        {
            //ma.WaitOne(); // 根据是否收到信号判断是否阻塞当前线程
            while (this.curDelay > 0 )   // 当前时间大于零
            {
                if (!isClosing)
                {
                    this.curDelay--;
                    this.pbTimeDelay.Value = this.curDelay;
                    this.lblTimeDelay.Text = this.curDelay.ToString();
                    
                }
                Thread.Sleep(1000);
            }
            if (!isClosing)
            {
              
                this.btnStart.Enabled = true;
                this.TableLp.Enabled = false;
              
            }
        }

        // 连接成功事件
        private void Link_SucClick(object sender, EventArgs e)
        {
            //连接成功,则删除,重新初始化
            this.TableLp.GetControlFromPosition(this.first.Y, this.first.X).Visible = false;//第一块消失
            this.TableLp.GetControlFromPosition(this.second.Y, this.second.X).Visible = false;//第二块消失
            this.first = new Point(-1, -1);//初始化第一块的位置
            this.second = new Point(-1, -1);//初始化第一块的位置
            this.score += 100;  
            this.lableScore.Text = string.Format("分数:{0}", score);

            // 消除成功加时,保证进度条不会消失
            if (curDelay <= 98)
            {
                curDelay += 2;
            }
            else if (curDelay == 99)
            {
                curDelay += 1;
            }
            // 判断是否连完
            Judge();
        }

        // 连接失败事件
        private void Link_FailClick(object sender, EventArgs e)
        {
            //连接失败,则第一个按钮取消选择
            Control c1 = this.TableLp.GetControlFromPosition(this.first.Y, this.first.X);
            ((Button)c1).FlatAppearance.BorderSize = 0;//第一块的外边框消失
            this.first = new Point(this.second.X, this.second.Y);
            this.second = new Point(-1, -1);
        }

        // 界面关闭事件
        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.isClosing = true;
            this.btnStop_Click(null, null);
            // 界面关闭,终止线程
            if (this.timeDelay != null && this.timeDelay.ThreadState == ThreadState.Running)
            {
                this.timeDelay.Abort();
            }
        }
    }
}

相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页