首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 網路程式設計實戰系列(二):從零打造單機版踩地雷遊戲

     編輯:關於.NET

前言:演算法先行,複製貼上無益

踩地雷是經典遊戲,若從未玩過,建議先體驗再動手實作演算法。透過思考推敲才能真正掌握核心邏輯,直接下載原始碼追蹤只會讓你停留在「知道怎麼寫」而非「理解為什麼這樣寫」。

一、雙層架構設計

遊戲畫面採用兩層重疊:

  • 第一層(底層):Label 陣列,負責顯示地雷圖片或周圍地雷數字。
  • 第二層(頂層):Button 陣列,蓋在 Label 上方,點擊時移除按鈕露出底層內容。

雷區大小固定為 20×20:

private const int Xcount = 20;
private const int Ycount = 20;

二、建立 Label 陣列並隨機佈雷

private Label[] labels2 = new Label[Xcount * Ycount];
private Label[,] labels = new Label[Xcount, Ycount];
int indexOfLable = 0;

private void CreateLable()
{
    int[] ints = new int[400];
    Random rd = new Random();
    for (int i = 0; i < 400; i++)
    {
        ints[i] = rd.Next(1, 400);
    }

    for (int i = 0; i < Xcount; i++)
    {
        for (int j = 0; j < Ycount; j++)
        {
            Label lb = new Label();
            lb.Location = new Point(i * 30, 30 * j);
            lb.Size = new Size(30, 30);
            lb.BorderStyle = BorderStyle.Fixed3D;
            this.Controls.Add(lb);
            labels[i, j] = lb;
            labels2[indexOfLable] = lb;
            indexOfLable++;
        }
    }

    // 隨機決定是否為地雷(約 10% 機率)
    for (int i = 0; i < ints.Length; i++)
    {
        if (ints[i] < 40)
        {
            labels2[i].Image = Properties.Resources.標準地雷;
        }
    }
}

三、建立 Button 陣列並綁定座標

利用 Tag 屬性將按鈕與其邏輯座標 (x, y) 關聯:

private Button[,] buttons = new Button[Xcount, Ycount];

private void AddButton()
{
    for (int i = 0; i < Xcount; i++)
    {
        for (int j = 0; j < Ycount; j++)
        {
            Button btn = new Button();
            btn.Click += new EventHandler(btn_Click);
            btn.MouseUp += new MouseEventHandler(btn_MouseUp);
            btn.Location = new Point(i * 30, 30 * j);
            btn.Size = new Size(30, 30);
            this.Controls.Add(btn);

            Point pt = new Point(i, j);
            btn.Tag = pt; // 關鍵:儲存座標
            buttons[i, j] = btn;
        }
    }
}

四、滑鼠右鍵:標記疑似地雷

void btn_MouseUp(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Right)
    {
        Button btn = (Button)sender;
        btn.BackgroundImage = (btn.BackgroundImage == null) 
            ? Properties.Resources.挖雷工兵 
            : null;
    }
}

五、滑鼠左鍵:核心遞迴展開邏輯

點擊按鈕後:

  1. 移除按鈕,取得座標
  2. 若底層 Label 有地雷圖片 → 遊戲結束
  3. 否則呼叫 CheckLable(x, y) 遞迴展開
void btn_Click(object sender, EventArgs e)
{
    Button btn = (Button)sender;
    this.Controls.Remove(btn);
    Point pt = (Point)btn.Tag;
    x = pt.X;
    y = pt.Y;

    if (labels[x, y].Image != null)
    {
        MessageBox.Show("遊戲結束,果然是踩雷高手");
        ReStart();
        return;
    }
    CheckLable(x, y);
}

六、檢查周圍 8 格並遞迴展開

CheckLable 會計算周圍地雷數:

  • 若周圍 8 格全無地雷(num == 8 表示 8 格皆安全)→ 移除周圍 8 個按鈕並遞迴檢查其周圍
  • 若有地雷 → 在 Label 上顯示數字並停止遞迴
private void CheckLable(int x, int y)
{
    if (x < Xcount && x >= 0 && y >= 0 && y < Ycount)
    {
        num = 0;
        num = GetCount(x, y);
        if (num == 8) // 周圍無地雷
        {
            num = 0;
            string tag = buttons[x, y].Tag.ToString();
            buttons[x, y].Tag = 0; // 標記已展開

            if (labels[x, y].Image == null && tag != "0")
            {
                // 依序移除周圍 8 個按鈕
                x++; RemoveButton(x, y);
                y++; RemoveButton(x, y);
                x--; RemoveButton(x, y);
                x--; RemoveButton(x, y);
                y--; RemoveButton(x, y);
                y--; RemoveButton(x, y);
                x++; RemoveButton(x, y);
                x++; RemoveButton(x, y);

                if (tag != "0")
                {
                    num = 0;
                    Recursion(x, y); // 遞迴展開
                }
            }
        }
        else // 周圍有地雷
        {
            if (x < Xcount && x >= 0 && y >= 0 && y < Ycount)
            {
                labels[x, y].Text = (8 - num).ToString(); // 顯示地雷數
                num = 0;
                return;
            }
        }
    }
}

七、計算周圍地雷數量

private int GetCount(int x, int y)
{
    temp2 = 0;
    // 依序檢查 8 個方向,超出邊界視為有地雷(temp2++)
    x++; CheckBounds(x, y);
    y++; CheckBounds(x, y);
    x--; CheckBounds(x, y);
    x--; CheckBounds(x, y);
    y--; CheckBounds(x, y);
    y--; CheckBounds(x, y);
    x++; CheckBounds(x, y);
    x++; CheckBounds(x, y);
    return temp2;
}

// 輔助方法簡化邊界檢查
private void CheckBounds(int x, int y)
{
    if (x < Xcount && x >= 0 && y >= 0 && y < Ycount)
    {
        if (labels[x, y].Image == null) temp2++;
    }
    else { temp2++; }
}

八、遞迴展開函式

private void Recursion(int x, int y)
{
    CheckLable(x, y);
    y++; CheckLable(x, y);
    y++; CheckLable(x, y);
    x--; CheckLable(x, y);
    x--; CheckLable(x, y);
    y--; CheckLable(x, y);
    y--; CheckLable(x, y);
    x++; CheckLable(x, y);
}

九、重新開始遊戲

public void ReStart()
{
    this.Controls.Clear();
    InitializeComponent();
    temp2 = 0; x = 0; y = 0; indexOfLable = 0; num = 0;
    AddButton();
    CreateLable();
}

原始碼下載

單機版掃雷完整專案

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved