Практична робота 2

 


Гра «Сапер»

         Метою даної практичної роботи буде вивчення загального методу роботи з візуальними компонентами, а саме: програмне створення, програмний обробіток, та використання універсальних обробників подій. В даному випадку «програмне» мається на увазі – створення візуальної компоненти в коді, а не переміщення мишею на форму. В лабораторній буде наведено обробка лише кнопок та текстових міток, але загальні методи одні і ті ж, і підходять до всіх візуальних компонент.

         Спершу нам потрібно створити форму яка буде містити головне меню і відображати наші динамічно створені кнопки для гри.

         Якщо Ви ніколи не грали в сапера, то почитайте основні правила на вікіпедії. В нас буде дещо легша версія гри, перше, чого в нас не буде – це обробка натиску двох кнопок миші одночасно (це коли відкриваються послідовно прилеглі клітинки до порожньої). Також в нашій грі можна буде лише програти, процедури та функції обробки виграшу ми упустили, так як нам основне це процес гри, обробка натиску кнопок, обробка поля. Сама процедура виграшу –  нецікава в плані візуального програмування. Ще в нашій грі не буде «прапорця», який помічає міну, і взагалі подія натиску правої кнопки миші буде відсутня.

         Розпишемо форму детальніше:

         На форму потрібно буде додати лише один компонент – головне меню.

         В свою чергу, головне меню буде містити такі елементи:

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML83d6697.PNG

         І елемент «Кількість мін»:

         Назви компонентів подані для того, щоб Ви знали, який із наведених нижче подій в коді, відноситься до якої компоненти. І, у Вас може виникнути ситуація, коли на формі назви компонент будуть відрізнятися від наведених в прикладі. Наприклад компоненти, які були створені в іншій послідовності, будуть мати номери в назвах компоненти інші, бо вони нумеруються по порядку, а порядок створення вже інший.

         По замовчуванню, при доданні створюється стандартний елемент меню. Створити ж, текстовий елемент, роздільник чи список вибору можна так:

Описание: C:\Users\Sergiy\AppData\Local\Temp\SNAGHTML83f99e2.PNG

         Натискаємо правою кнопкою миші там, де на малюнку показує стрілка, та обираємо потрібний вид елемента меню.

         Тепер потрібно описати події і методи нашої форми, нижче приведено код з коментарями. Блок підключення простору імен, ми упустили.

 

namespace WindowsFormsApplication1 //простір імен нашого додатку

{

    public partial class Form1 : Form

    {

        int[,] f = new int [51,51]; //масив значень поля (зберігає числа під кнопками) 255 - міна

        Button[,] b = new Button [51,51]; //масив кнопок (кнопки, які відображаються над числами)

        Label[,] l = new Label[51, 51]; //масив міток під кнопками (візуальні мітки, які відображають числа, які знаходяться під кнопками))

        int fsize = 20; //розмір поля по-замовчуванню

        int status = 0; //(0 - гра не розпочата, 1 - гра триває, 2 - програна)

 

        public void f_to_zero() { // прописує 0-лі в масив f

            int x, y; //змінні ля почергового перебору елементів

            for (x = 0; x <= 50; x++) //в нас максимальна ширина поля 50

                for (y = 0; y <= 50;y++) // в нас максимальна висота поля 50

                {

                    f[x,y]=0; //присвоюємо 0 елементу

                }

        }

 

        private void but_clck(object sender, EventArgs e) //процедура обробки натискання кнопки мінного поля

        {

            Button o = ((Button)sender);//зводимо викликаючий об’єкт до кнопки

            Label ol = ((Label)o.Tag);//перетворюємо тег кнопки до мітки

            o.Visible = false;//приховуємо кнопку

            ol.Visible = true;//показуємо мітку з числом

            if (ol.Text == "B")//перевіряємо, чи ми не знаходимося на міні

            {//якщо все-таки знаходимося

                MessageBox.Show("Looooser!");//виводимо повідомлення по те, що користувач програв

                b_hide();//приховуємо всі кнопки

                l_show(fsize);//показуємо всі мітки

                ol.Font = new Font(ol.Font, FontStyle.Bold);//виділяємо ту клітинку, на якій "підірвався" гравець

                status = 2;//міняємо статус на гра програна

            }

        }

 

        public void b_l_create() { //створює мітки та кнопки

            int x, y,h;//допоміжні змінні

            //x, y - для обходу двовимірного масиву, h - для вираховування позиції слідуючої кнопки

            h=menuStrip1.Height;//дізнаємося висоту рядка меню

            for (x = 0; x <= 50; x++)//перебираємо наші масиви з ліва направо

                for (y = 0; y <= 50;y++ )//зверху в низ, через змінні x, y

                {

                    b[x,y]=new Button();//створюємо наступну кнопку

                    l[x, y] = new Label();//створюємо наступну мітку

                    l[x, y].Parent = this;//встановлюємо батька мітки нашу форму

                    b[x, y].Parent = this;//встановлюємо батька кнопки нашу форму

                    b[x, y].Width = 20;//ширина кнопки

                    b[x, y].Height = 20;//висота кнопки

                    l[x, y].Width = 20;//ширина мітки

                    l[x, y].Height = 20;//висота мітки

                    b[x, y].Top = y * 20 + h;//вираховуємо позицію кнопки від верхнього краю форми з поправкою на ширину меню

                    l[x, y].Top = y * 20 + h;//вираховуємо позицію мітки від верхнього краю форми з поправкою на ширину меню

                    b[x, y].Left = x * 20;//вираховуємо позицію кнопки від лівого краю форми

                    l[x, y].Left = x * 20;//вираховуємо позицію мітки від лівого краю форми

                    b[x, y].Visible = false;//приховуємо кнопку

                    l[x, y].Visible = false;//приховуємо мітку

                    b[x, y].Tag = l[x, y];//закріплюємо за кнопкою мітку, яка міститься під нею

                    b[x, y].Click += new System.EventHandler(this.but_clck);//реєструємо універсальну функцію обробітку події

                    //натиску для кожної кнопки

                }

        }

 

        public void b_show(int s) { //показує s кнопок поля (s - ширина поля)

            int x, y;//змінні для обходу двовимірного масиву

            for (x=0;x<s;x++)//проходимо масив зліва направо, зверху вниз

                for (y = 0; y <s; y++) {//через змінні x, y

                    b[x, y].Visible=true;//показуємо кнопку

                }

            //встановлюємо розміри вікна, щоб усі міни помістилися

            this.Height = menuStrip1.Height + s * 20;//змінюємо висоту вікна з врахуванням висоти меню

            this.Width = s * 20+s;//змінюємо ширину вікна

        }

 

        public void b_hide() { // ховає всі кнопки

            int x, y;//змінні для обходу масиву

            for (x=0;x<=fsize;x++)//перебираємо масив зліва направо, зверху вниз

                for (y = 0; y <= fsize; y++) {//через x, y

                    b[x, y].Visible = false;//ховаємо кнопку

                }       

        }

 

        public void l_show(int s) //показує текстові мітки поля (s - розмір поля)

        {

            int x, y;//змінні для обходу масиву

            for (x=0;x<s;x++)//перебираємо масив зліва направо, зверху вниз

                for (y = 0; y < s; y++)//через x, y

                {

                    l[x, y].Visible = true;//показуємо мітку

                }

        }

 

        public void l_hide() //ховає всі текстові мітки

        {

            int x, y;//змінні для обходу масиву

            for (x=0;x<=fsize;x++)//перебираємо масив зліва направо, зверху вниз

                for (y = 0; y <=fsize; y++)//через x, y

                {

                    l[x, y].Visible = false;//приховуємо мітку

                    l[x, y].Font = new Font(l[x,y].Font, FontStyle.Regular);//змінюємо параметри шрифту на звичайний

                    //так, як в нас, наприклад клітинка де гравець програв відображається жирним

                }

        }

 

        public void generate_bombs(int bm,int fs) { //розкидає міни по полю та переносить надписи на поле

            //де:

            //int bm - кількість мін на полі,

            //int fs - розмір поля

            int i,x,y;//допоміжні змінні

            //i - для розміщення мінок на полі в потрібній кількості,

            //x,y - для обходу масиву поля

            Random rnd = new Random();//ініціалізуємо генератор випадкових чисел

            for (i = 0; i < bm; i++)//генеруємо bm ітерацій (bm - кількість мінок)

            {

                f[rnd.Next(1, fs + 1), rnd.Next(1, fs + 1)] = 255;//присвоюємо випадковим координатам масиву мінок

                // число 255 - яке в нас позначає міну

            }

            //тепер нам наш числовий масив потрібно обробити, а саме:

            //вирахувати числа суміжних з мінами клітинок

            for (x=1;x<=fs;x++)//перебираємо масив зліва направо, зверху вниз

                for (y = 1; y <= fs; y++)//через x, y

                    if (f[x, y] == 255)//якщо клітинка містить мінку

                    {

                        //додаємо +1 до всіх 8-ми суміжних клітинок

                        f[x - 1, y - 1]++;//

                        f[x - 1, y]++;    //

                        f[x - 1, y + 1]++;// [+1][+1][+1]

                        f[x, y - 1]++;    // [+1][ B][+1]

                        f[x, y + 1]++;    // [+1][+1][+1]

                        f[x + 1, y - 1]++;//

                        f[x + 1, y]++;    //

                        f[x + 1, y + 1]++;//

                    }    

            //тепер потрібно всі числа і міни перенести в масив міток, які знаходяться під кнопками

            for (x=0;x<fs;x++)//перебираємо масив зліва направо, зверху вниз

                for (y = 0; y < fs; y++)//через x, y

                {

                    if (f[x+1,y+1]<255)//перевіряємо чи не міститься в клітинці міна

                     {l[x, y].Text = f[x + 1, y + 1].ToString();} //якщо не міститься зводимо число до рядка і присвоюємо мітці

                      else {l[x,y].Text="B";}//якщо міна, то присвоюємо мітці «В»

                }

        }

 

        /*

         *  Завантажуватись додаток буде відносно довго,

         *  так як, на створення 2500 (максимальний розмір поля 50х50)

         *  кнопок та міток і прив’язка їх між собою, займає трохи часу

         */

 

        public Form1()//конструктор форми

        {

            InitializeComponent();

            f_to_zero();//обнуляємо числовий масив поля

            b_l_create();//створюємо мітки та кнопки поля

        }

 

        private void розпочатиГруToolStripMenuItem_Click(object sender, EventArgs e)

            //натискання елементу меню початку гри

        {

            int bmv = Int32.Parse(toolStripTextBox2.Text);//визначаємо з меню кількість мін

            int fsv = Int32.Parse(toolStripTextBox1.Text);//визначаємо з меню розмір поля

            fsize = fsv;//зберігаємо розмір поля в глобальних змінних

            if (this.status == 0) {//статус 0 - гра ще не починалась

                generate_bombs(bmv,fsv);//генеруємо мінки, ініціалізуємо поле

                b_show(fsv);//показуємо кнопки fsv - розмір поля

                status = 1;//змінюємо статус на 1 - гра розпочалась

            } else // якщо статус не 0 додатково перевіряємо:

                //статус 1 - гра триває

            if (status == 1) { MessageBox.Show("You need to loose first!"); }//виводимо повідомлення, що потрібно спочатку програти

            if (status == 2) //статус 2 - гравець програв

            {

                f_to_zero();//обнуляємо (очищуємо) числове представлення поля

                l_hide();//ховаємо поле, яке відобразилося гравцю після програшу

                generate_bombs(bmv,fsv);//заново ініціалізуємо поле по кількості мінок та розміру поля

                b_show(fsv);//показуємо кнопки

                status = 1;//змінюємо на статус 1 - гра триває

            }

 

        }

    }

}

 

         Якщо все зробити правильно, на екрані з’явиться щось схоже на це:

         Акцентую увагу, що при здачі лабораторної роботи будуть задаватися питання по роботі гри, та можуть бути додатково (якщо є сумнів, що студент розібрався в практичному завданні) даватися завдання такі, наприклад, як:

·        Створити елемент меню який дозволить користувачу «підглянути» поле.

·        Створити елемент меню завершення гри.

·        Створити повідомлення «Сапер помиляється тільки один раз!» при кожному відкритті клітинки поля.

·        Створити повідомлення «А ви дійсно певні, що бажаєте відкрити цю клітинку?» з вибором ТАК або НІ.

·        Зробити так, щоб гра вважалася програною при другому «підриві» на міні, а після першого виводилось, «У Вас вже немає однієї руки, будьте пильними!»

         Це приклад додаткових завдань, можуть бути і інші.