基于C#用WinForm实现的2048小游戏

     2048游戏规则比较简单,玩家通过上、下、左、右四个方向来控制方块的移动,每一次移动,所有的方块都会朝这个方向进行移动,而此时则会在某个随机空的地区产生一个新的数字方块,在移动过程中,若方块上的数字与移动方向后一个的方块上的数字相同,则会碰撞成一个新的方块,数字为两个方块数字之和。若所有地方都被填满方块,且每个方块与相邻方块之间的数字均不相同,则游戏结束。

    运行结果:

    所需控件:一个button用于重置游戏,两个label来显示分数,一个panel容器来装16个textbox用于显示实时数字。

如出现上图情况,将每个textbox的TabStop属性改为false,也可以用label来代替textbox。

  本程序使用W、A、S、D四个键来操作方块的移动。记得将Form1的KeyPreview属性改为true。

首先,定义变量

   public class Variables
    {
        public static int score; //分数

        public static int[] Num = new int[16];                

        public static int[,] TempNum = new int[4, 4];       //移动   

        public static int[] num = new int[16];
    }

    本程序是采用一个长度为16的数组来存放每个方块(即每个textbox)的数字。

 移动方法:

public void MovingW()
  {
    for (int i = 0; i < 4; i++)
     {
       ArrayList arr = new ArrayList();
        //将某一列所有不为0的元素的索引找出来
        for (int j = 0; j < 4; j++)
         {
          if (Num[i+j*4] != 0)
            {
              arr.Add(j);
            }
         }
     if (arr.Count == 1)
      {
        TempNum[0, i] = Num[i+Convert.ToInt32(arr[0])*4];
        TempNum[1, i] = 0;
        TempNum[2, i] = 0;
        TempNum[3, i] = 0;
      }

   if (arr.Count == 2)
   {
    if (Num[Convert.ToInt32(arr[0])*4+i] == Num[Convert.ToInt32(arr[1])*4+i])
    {
     TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+i] + Num[Convert.ToInt32(arr[1])*4+i];
     TempNum[1, i] = 0;
     TempNum[2, i] = 0;
     TempNum[3, i] = 0;
     score += (TempNum[0, i]) / 2;
     }
   else
   {
    TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+i];
    TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+i];
    TempNum[2, i] = 0;
    TempNum[3, i] = 0;
    }
    }

   if (arr.Count == 3)
    {
    if (Num[Convert.ToInt32(arr[0])*4+i] == Num[Convert.ToInt32(arr[1])*4+i])
    {
     TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+i] + Num[Convert.ToInt32(arr[1])*4+i];
      TempNum[1, i] = Num[Convert.ToInt32(arr[2])*4+ i];
      TempNum[2, i] = 0;
     TempNum[3, i] = 0;
     score += (TempNum[0, i]) / 2;
     }
     else
   {
    if (Num[Convert.ToInt32(arr[1])*4+i] == Num[Convert.ToInt32(arr[2])*4+i])
    {
     TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i];
   TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+ i] + Num[Convert.ToInt32(arr[2])*4+ i];
   TempNum[2, i] = 0;
   TempNum[3, i] = 0;
   score += (TempNum[1, i]) / 2;
      }
   else
  {
   TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i];
   TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+ i];
   TempNum[2, i] = Num[Convert.ToInt32(arr[2])*4+ i];
   TempNum[3, i] = 0;
  }
   }
   }

 if (arr.Count == 4)
    { if (Num[Convert.ToInt32(arr[0])*4+ i] == Num[Convert.ToInt32(arr[1])*4+ i])
 {
if (Num[Convert.ToInt32(arr[2])*4+ i] == Num[Convert.ToInt32(arr[3])*4+ i])
  {
   TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i] + Num[Convert.ToInt32(arr[1])*4+ i];
   TempNum[1, i] = Num[Convert.ToInt32(arr[2])*4+ i] + Num[Convert.ToInt32(arr[3])*4+ i];
      TempNum[2, i] = 0;
       TempNum[3, i] = 0;
     score += (TempNum[0, i] + TempNum[1, i]) / 2;
    }
 else
   {
   TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i] + Num[Convert.ToInt32(arr[1])*4+ i];
      TempNum[1, i] = Num[Convert.ToInt32(arr[2])*4+ i];
      TempNum[2, i] = Num[Convert.ToInt32(arr[3])*4+ i];
        TempNum[3, i] = 0;
      score += (TempNum[0, i]) / 2;
        }
      }
  else
 {
   if (Num[Convert.ToInt32(arr[1])*4+ i] == Num[Convert.ToInt32(arr[2])*4+ i])
 {
   TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i];
   TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+ i] + Num[Convert.ToInt32(arr[2])*4+ i];
   TempNum[2, i] = Num[Convert.ToInt32(arr[3])*4+ i];
  TempNum[3, i] = 0;
   score += (TempNum[1, i]) / 2;
  }
 else
  {
    if (Num[Convert.ToInt32(arr[2])*4+ i] == Num[Convert.ToInt32(arr[3])*4+ i])
     {
       TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i];
      TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+ i];
   TempNum[2, i] = Num[Convert.ToInt32(arr[2])*4+ i] + Num[Convert.ToInt32(arr[3])*4+ i];
      TempNum[3, i] = 0;
      score += (TempNum[2, i]) / 2;
      }
      else
     {
        TempNum[0, i] = Num[Convert.ToInt32(arr[0])*4+ i];
         TempNum[1, i] = Num[Convert.ToInt32(arr[1])*4+ i];
         TempNum[2, i] = Num[Convert.ToInt32(arr[2])*4+ i];
          TempNum[3, i] = Num[Convert.ToInt32(arr[3])*4+ i];
                            }
                        }
                    }
                }
            }
        }

 此为向上运动的方法,即W。根据方阵每一列的非0的个数以及相互之间的相等情况来计算移动的结果,只有在有方块进行相加时才有分数产生。其余三个方向与其类似,就不赘述。

判断是否游戏结束

public bool GameOver()
  {
    bool over1 = false;
    //判断格子是否已满
    for (int i = 0; i < Num.Length; i++)
    {
       if (Num[i] == 0)
       over1 = true;
     }
            //判断是否有相邻相同的
    //四个角
   if (Num[0] == Num[1] || Num[0] == Num[4] || Num[3] == Num[2] || Num[3] == Num[7]
  || Num[12] == Num[8] || Num[12] == Num[13] || Num[15] == Num[14] || Num[15] == Num[11])
         over1 = true;

    //四条边
 int[] lines1 = { 1, 2, 4, 8, 7, 11, 13, 14 };
  foreach (int i in lines1)
            {
                switch (i)
                {
                    case 1:
                    case 2:
                        if (Num[i] == Num[i - 1] || Num[i] == Num[i + 1] || Num[i] == Num[i + 4])
                            over1 = true;
                        break;
                    case 4:
                    case 8:
                        if (Num[i] == Num[i - 4] || Num[i] == Num[i + 1] || Num[i] == Num[i + 4])
                            over1 = true;
                        break;
                    case 7:
                    case 11:
                        if (Num[i] == Num[i - 4] || Num[i] == Num[i - 1] || Num[i] == Num[i + 4])
                            over1 = true;
                        break;
                    case 13:
                    case 14:
                        if (Num[i] == Num[i - 1] || Num[i] == Num[i - 4] || Num[i] == Num[i + 1])
                            over1 = true;
                        break;
                    default:
                        break;
                }
            }

        //中间四块
     if (Num[5] == Num[6] || Num[6] == Num[10] || Num[5] == Num[9] || Num[9] == Num[10])
                over1 = true;

            return over1;
        }

 游戏结束的规则就是方阵被填满,且没有两个相邻的数字。

首先判断是否方阵被填满,只有Num中有一个数字为0,则游戏继续。

再来判断相邻情况。由于方阵中每个位置的相邻情况都不一样,所以需要分开来讨论。

首先看四个角的相邻情况,每个角均与两个方块相邻。然后是每条边的中间两个均与三个方块相邻。最后是中间四个都与四个方块相邻。只有这些相邻情况中有一个为真,即返回值为true,游戏没有结束。

程序加载时

    private void Form1_Load(object sender, EventArgs e)
        {
            label2.Text = "0";

            //随机赋初值
            Random r = new Random();
            int k = r.Next(0, Num.Length);
            Num[k] = 2;

            timer1.Start();

            MessageBox.Show("开始!");

        }

程序加载时,初始化分数为0,并随机在方阵中产生一个数字“2”。计时器是用来将Num数组的值赋给textbox并实时刷新显示。此处可以根据textbox不同值来设定不同的背景色,以便于区分。

        private void timer1_Tick(object sender, EventArgs e)
        {
            #region 数字显示
            if (Num[0] != 0)
                textBox1.Text = Num[0].ToString();
            if (Num[1] != 0)
                textBox2.Text = Num[1].ToString();
            if (Num[2] != 0)
                textBox3.Text = Num[2].ToString();
            if (Num[3] != 0)
                textBox4.Text = Num[3].ToString();
            if (Num[4] != 0)
                textBox5.Text = Num[4].ToString();
            if (Num[5] != 0)
                textBox6.Text = Num[5].ToString();
            if (Num[6] != 0)
                textBox7.Text = Num[6].ToString();
            if (Num[7] != 0)
                textBox8.Text = Num[7].ToString();
            if (Num[8] != 0)
                textBox9.Text = Num[8].ToString();
            if (Num[9] != 0)
                textBox10.Text = Num[9].ToString();
            if (Num[10] != 0)
                textBox11.Text = Num[10].ToString();
            if (Num[11] != 0)
                textBox12.Text = Num[11].ToString();
            if (Num[12] != 0)
                textBox13.Text = Num[12].ToString();
            if (Num[13] != 0)
                textBox14.Text = Num[13].ToString();
            if (Num[14] != 0)
                textBox15.Text = Num[14].ToString();
            if (Num[15] != 0)
                textBox16.Text = Num[15].ToString();

            #endregion


            #region 设置字体及颜色
            foreach (Control ctr in panel1.Controls)
            {
                if (ctr is TextBox)
                {
                    ctr.Font = new Font("宋体", 12, ctr.Font.Style);
                    ctr.ForeColor = Color.Black;
                    switch (ctr.Text)
                    {
                        case "":
                            ctr.BackColor = Color.White;
                            break;
                        case "2":
                            ctr.BackColor = Color.Blue;
                            break;
                        case "4":
                            ctr.BackColor = Color.Pink;
                            break;
                        case "8":
                            ctr.BackColor = Color.Orange;
                            break;
                        case "16":
                            ctr.BackColor = Color.Gray;
                            break;
                        case "32":
                            ctr.BackColor = Color.Green;
                            break;
                        case "64":
                            ctr.BackColor = Color.Brown;
                            break;
                        case "128":
                            ctr.BackColor = Color.Goldenrod;
                            break;
                        case "256":
                            ctr.BackColor = Color.LightBlue;
                            break;
                        case "512":
                            ctr.BackColor = Color.Violet;
                            break;
                        case "1024":
                            ctr.BackColor = Color.Yellow;
                            break;
                        case "2048":
                            ctr.BackColor = Color.MediumTurquoise;
                            break;
                    }
                }
            }
            #endregion

            label2.Text = score.ToString();
        }

把方阵移动前后进行比较,即Num在Moving方法调用前后进行比较,如果不同则在方阵移动完以后,随机在此时方阵空的,即值为0的地方生成一个2或者4,如果相同则不会生成新的数。

        public int[] NewNumber()
        {
            Random a = new Random();
            int i;
            ArrayList arr = new ArrayList { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, };  //生成数的范围,2多4少

            int[] Select;

            Select = ArraylistToInt(arr);   //list转int数组

            i = GetOneIndex(Num);  //在Num中随机得到一个索引即生成的位置

            if (i != 99)
                Num[i] = GetOneNumber(Select);

            return Num;
        }

        public int GetOneNumber(int[] arr)   //在数组中随机获得一个数
        {
            Random index = new Random();
            int i;
            if (arr.Length == 1)
                i = 0;
            else
                i = index.Next(0, arr.Length);

            return arr[i];
        }

        public int GetOneIndex(int[] a)       //在数组中随机得到一个为0的索引
        {
            ArrayList arr = new ArrayList();
            for (int i = 0; i < a.Length; i++)
            {
                if (a[i] == 0)
                {
                    arr.Add(i);
                }
            }
            Random r = new Random();
            int k;
            if (arr.Count == 0)
            { k = 99; }
            else
            {
                if (arr.Count == 1)
                    k = 0;
                else
                    k = r.Next(0, arr.Count);
            }

            return (int)arr[k];
        }

     public int[] ArraylistToInt(ArrayList arr)
        {
            int[] s = new int[arr.Count];
            for (int i = 0; i < arr.Count; i++)
            {
                s[i] = Convert.ToInt16(arr[i]);
            }
            return s;
        }
           //判断运动前后是否相同
            for(int i=0;i<Num.Length;i++)
            {
                if(num[i]!=Num[i])
                {
                    method.NewNumber();
                    break;
                }
            }

每次运动完成之后需要再进一个新的计时器,其中跟上面那个计时器内容差不多,只需要在每次进该计时器时需要将文本框内容清零一下,再进行显示。再加上一个判断游戏是否结束就OK了。

   //清空文本框内容
            foreach (Control ctrl in panel1.Controls)
            {
                if (ctrl is TextBox)
                {
                    ctrl.Text = "";
                }
            }
  //
//跟timer1中的显示和颜色字体设置一样
//

//判断游戏是否结束
  if (quiit== false)
                {
                    quiit = true;
                    MessageBox.Show("gameover", "失败");
                }

猜你喜欢

转载自blog.csdn.net/Iawfy_/article/details/111041918