Листинг 11.2. Метод.
private void frmTicTacToe_Load (object sender, EventArgs e){//’establish arrayboxArray [0] = lblBox1;boxArray [1] = lblBox2;boxArray [2] = lblBox3;boxArray [3] = lblBox4;boxArray [4] = lblBox5;boxArray [5] = lblBox6;boxArray [6] = lblBox7;boxArray [7] = lblBox8;boxArray [8] = lblBox9;// possible winspossibleWins [0] = «012»;possibleWins [1] = «345»;possibleWins [2] = «678»;possibleWins [3] = «036»;possibleWins [4] = «147»;possibleWins [5] = «258»;possibleWins [6] = «048»;possibleWins [7] = «246»;// clear boxesfor (int i = 0; i <9; i++)boxArray [i].Text = «»;lblMessage. Text = «Game Stopped»;grpFirst. Enabled = false;grpComputer. Enabled = false;//drawSound = new System.Media.SoundPlayer (//Application.StartupPath + "\\beep. wav»);//winSound = new System.Media.SoundPlayer (//Application.StartupPath + "\\tada. wav»);}
Дважды щёлкаем по первой кнопке в режиме проектирования или в панели Properties (для этой кнопки) на вкладке Events дважды щёлкаем по имени события Click. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 11.3. Метод.
private void btnStartStop_Click (object sender, EventArgs e){if (btnStartStop. Text == «Start Game»){btnStartStop. Text = «Stop Game»;grpPlayers. Enabled = false;grpFirst. Enabled = false;grpComputer. Enabled = false;btnExit. Enabled = false;xTurn = true;lblMessage. Text = «X’s Turn»;// reset boxesfor (int i = 0; i <9; i++){boxArray [i].Text = «»;boxArray[i].BackColor = Color. White;}canClick = true;numberClicks = 0;gameOver = false;if (rdoComputerFirst.Checked &&rdoOnePlayer.Checked)ComputerTurn ();}else{btnStartStop. Text = «Start Game»;if (!gameOver)lblMessage. Text = «Game Stopped»;grpPlayers. Enabled = true;if (rdoOnePlayer.Checked){grpFirst. Enabled = true;grpComputer. Enabled = true;}btnExit. Enabled = true;canClick = false;}
}
Дважды щёлкаем по второй кнопке в режиме проектирования или в панели Properties (для этой кнопки) на вкладке Events дважды щёлкаем по имени события Click. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 11.4. Метод.
private void btnExit_Click (object sender, EventArgs e)
{
this.Close ();
}
В панели Properties для второго переключателя на вкладке Events дважды щёлкаем по имени события Click. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 11.5. Метод.
private void rdoOnePlayer_Click (object sender, EventArgs e){grpFirst. Enabled = true;grpComputer. Enabled = true;}
В панели Properties для первого переключателя на вкладке Events дважды щёлкаем по имени события Click. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 11.6. Метод.
private void rdoTwoPlayers_Click (object sender, EventArgs e){grpFirst. Enabled = false;grpComputer. Enabled = false;}
Если мы желаем, чтобы при завершении игры с результатом «Ничья» или «Победа» звучала соответствующая музыка, необходимо раскомментировать строки кода, в которых есть слово «Sound», и отладить проект.
Строим и запускаем программу на выполнение обычным образом: Build, Build Selection; Debug, Start Without Debugging.
В ответ Visual Studio выводит форму Form1 с показанным выше полем игры из 9 клеток.
Далее Игрок играет с Компьютером или Игрок 1 играет с Игроком 2 в крестики-нолики согласно приведённым выше правилам.
По методике данной главы можно разрабатывать самые разнообразные игры в крестики-нолики как для игры Игрока с Компьютером, так и для игры Игрока 1 с Игроком 2.
Глава 12. Методика программирования искусственного интеллекта в игре в «Крестики-нолики» на сетке 6x7
12.1. Общие сведения
Разработаем методику проектирования и программирования типичной и широко распространённой игры типа игры в крестики-нолики. Известно много вариантов этой игры как в ручном, так и в компьютерном варианте.
В ручном варианте смысл игры состоит в том, что, например, в тетради в клеточку на поле из квадратов, например, 6 x 7 квадратов (цифры соответствуют количеству квадратов по оси абсцисс «x» и оси ординат «y») два игрока по очереди выделяют квадраты крестиками и ноликами, стараясь первым выделить горизонтальную, вертикальную или диагональную линию из определённого количества крестиков или ноликов.
Данную компьютерную игру мы будем разрабатывать, следуя статье [см. Список литературы: Article from the website: www.codeproject.com/netcf/Connect4AB.asp: Labib B. Connect4 using Alpha-Beta Search algorithm], но с нашими усовершенствованиями для современной версии Visual Studio.
В данной игре (игрока с компьютером) не будут использоваться графические файлы. Поэтому сначала методом FillRectangle класса Graphics на форме рисуется прямоугольник (поле игры), заполненный синим цветом RoyalBlue:
e.Graphics.FillRectangle (new SolidBrush(Color.RoyalBlue),
15, 70, 210, 180);
где 15, 70 – координаты верхнего левого угла прямоугольника (от верхнего левого угла формы, когда ось «y» направлена вниз);
210, 180 – размеры прямоугольника соответственно по осям «x» и «y».
Далее методом FillEllipse класса Graphics в этом прямоугольнике рисуются 6 x 7 кружочков (ноликов) диаметром 27 пикселей белого цвета White:
g.FillEllipse (new SolidBrush (Color. White),
x +3, y +3, 27, 27);
Когда игрок щёлкает мышью по белому кружочку, этот белый кружочек заменяется таким же кружочком, но жёлтого цвета Yellow:
g.FillEllipse (new SolidBrush (Color. Yellow),
x +3, y +3, 27, 27);
После расчёта вариантов (по определённому математическому алгоритму, реализованному в показанной далее программе) компьютер заменяет один белый кружочек таким же кружочком красного цвета Red:
g.FillEllipse (new SolidBrush(Color.Red),
x +3, y +3, 27, 27);
Так по очереди игрок и компьютер выделяют жёлтые и красные кружочки, стараясь первым выделить горизонтальную, вертикальную или диагональную линию из 4-х кружочков одинакового цвета. После каждого щелчка игроком поля игры (мышью) он слышит сигнал Beep (по-русски: Бип).
12.2. Правила игры
1. После запуска игры на форме появляется поле игры, состоящее из 7 x 6 белых кружочков (рис. 12.1). Напомним, что цифры соответствуют количеству квадратов по осям «x» и «y». В меню «Новая игра» по умолчанию установлен режим «Начинает игрок», но можно выбрать «Начинает компьютер» (рис. 12.1). В меню Уровень по умолчанию установлен режим Обычный, но можно выбрать Нормальный или Сложный (рис. 12.2).
Рис. 12.1. Меню Новая игра. Рис. 12.2. Меню Уровень (Level).
2. Игрок щёлкает мышью по выбранному им белому кружочку, этот белый кружочек заменяется жёлтым кружочком. После каждого щелчка игроком поля игры он слышит сигнал Beep.
После расчёта вариантов (по определённому математическому алгоритму, реализованному в показанной далее программе) компьютер заменяет один белый кружочек таким же кружочком красного цвета (рис. 12.3).
3. Так по очереди игрок и компьютер выделяют жёлтые и красные кружочки, стараясь первым выделить горизонтальную, вертикальную или диагональную линию из 4-х кружочков одинакового цвета. Но если игрок или компьютер «видят», что следующим ходом его соперник соберёт линию из 4-х кружочков одинакового цвета, он должен (чтобы не проиграть) на этот квадрат поместить свой кружок (даже в ущерб своей тактике игры). Середина игры показана на рис. 12.4.
4. Как только игрок или компьютер первым соберёт линию из 4-х кружочков своего цвета, появляется информационная панель Show класса MessageBox с сообщением для игрока, либо «Вы победили» (рис. 12.5), либо «Вы проиграли» (рис. 12.6).
На этой панели нужно щёлкнуть OK.
5. Для начала новой игры следует в меню «Новая игра» выбрать режим «Начинает игрок» (или «Начинает компьютер»).
6. Для закрытия игры следует на форме выбрать значок Close.
Рис. 12.3. Начало игры. Рис. 12.4. Середина игры.
Рис. 12.5. Победил игрок. Рис. 12.6. Победил компьютер.
На основании этих правил можно сформулировать другие правила, и любые правила ввести в справочную форму игры, которую можно разработать по приведённым в других главах методикам.
12.3. Создание проекта
Создаём проект по обычной схеме: в VS в панели New Project (показанной выше) слева на вкладке Templates выбираем шаблон (тип проекта) Visual C#, Windows Classic Desktop и проверяем, чтобы в окне Templates был выделен (как правило, по умолчанию) шаблон Windows Forms App (.NET Framework); в окне Name записываем (или оставляем по умолчанию) имя проекта, например, Connect4 (рис. 12.7, 12.8, 12.9) и щёлкаем OK.
Рис. 12.7. Форма Form1. Рис. 12.8. Меню Level. Рис. 12.9. SE и Properties.
Создаётся проект, появляется форма Form1 на экране в режиме проектирования (рис. 12.7).
Оставляем по умолчанию или проектируем форму, как подробно описано выше и в наших книгах с сайта ZharkovPress.ru в параграфе «Методика проектирования формы».
Для задания режимов игры, как и выше, с панели инструментов Toolbox переносим на форму элемент управления MenuStrip (рис. 12.7).
На форме Form1 появляются окна с надписью Type Here (Печатайте здесь), в которые записываем команды, слева: New game (Новая игра), Begins the player (Начинает игрок), Begins the computer (Начинает компьютер), справа: Level (Уровень), Usual (Обычный), Normal (Нормальный), Difficult (Сложный), рис. 12.8. Если мы записали команды в виде русских слов, то теперь в панели Properties (для всех команд элемента управления MenuStrip) в свойстве Name вместо этих русских слов записываем английские слова, а именно, для команд слева: newGame, playerFirst, computerFirst, для команд справа: level, easy, normal, hard.
Если мы хотим подавать звуковой сигнал Beep в различные моменты выполнения каких-либо действий, например, после каждого щелчка экрана мышью, то поступаем следующим образом. По 1-му варианту, согласно разработанной выше методике использования в нашем приложении метода (функции) из любого другого языка, на первом этапе необходимо создать ссылку на тот язык, например, на Visual Basic. Для этого в меню Project выбираем команду Add Reference, в панели Reference Manager выбираем ссылку на соответствующую динамически подключаемую библиотеку (dynamic link library) формата (.dll), в данном случае, на Microsoft.VisualBasic и щёлкаем кнопку OK. А в тех местах кода, где нам нужен этот сигнал, например, в метод-обработчик Form1_MouseDown нажатия левой кнопки мыши, иначе, щелчка мышью по экрану, запишем далее строку:
Microsoft.VisualBasic.Interaction.Beep ();
По 2-му варианту, в тех местах кода, где нам нужен этот сигнал Beep, например, в обработчик события в виде нажатия клавиши Enter, записываем следующую строку:
System.Media.SystemSounds.Beep.Play ();
12.4. Код программы
Открываем файл Form1.cs (например, по схеме: File, Open, File) и в классе Form1 записываем следующие переменные и метод.
Листинг 12.1. Переменные и метод.
public int cx = 15;public int cy = 70;public int Level = 3;public Connect4Board b = new Connect4Board ();public void Draw (Graphics g){Pen p = new Pen (Color. Black);g. DrawRectangle (p, 15, 70, 210, 180);int q = 0;int w = 5;for (int y = cy; y}
В панели Properties (для Form1) на вкладке Events дважды щёлкаем по имени события Paint. Появившийся шаблон метода Form1_Paint после записи нашего кода принимает следующий вид. Напомним, что другие варианты вывода изображения, например, на элемент управления PictureBox и после щелчка по какому-либо элементу управления уже приводились ранее.
Листинг 12.2. Метод для рисования изображений.
private void Form1_Paint (object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle (new SolidBrush(Color.RoyalBlue),
15, 70, 210, 180);
Draw(e.Graphics);
}
В панели Properties (для Form1) на вкладке Events дважды щёлкаем по имени события MouseDown. Появившийся шаблон метода Form1_MouseDown после записи нашего кода принимает следующий вид.
Листинг 12.3. Метод для обработки щелчка экрана мышью.
private void Form1_MouseDown (object sender, MouseEventArgs e){Microsoft.VisualBasic.Interaction.Beep ();if (b. endt == 0){if ((e.X> cx) && (e.X}
}
}
}
}
}
Дважды щёлкаем по команде Begins the player (Начинает игрок) для элемента управления MenuStrip (или в панели Properties на вкладке Events выбираем событие Click). Появляется шаблон метода, который после записи нашего кода принимает следующий вид.
Листинг 12.4. Метод-обработчик выбора команды.
private void playerFirstToolStripMenuItem_Click (object sender, EventArgs e){//Player Firstb = new Connect4Board ();b.rec = Level;Invalidate ();}
Дважды щёлкаем по команде Begins the computer (Начинает компьютер) для элемента управления MenuStrip. Появляется шаблон метода, который после записи нашего кода принимает следующий вид.
Листинг 12.5. Метод-обработчик выбора команды.
private void computerFirstToolStripMenuItem_Click (object sender, EventArgs e){//CPU Firstb = new Connect4Board ();b.rec = Level;Draw(this.CreateGraphics ());b. turn = b. cpu;if (b. endt == 0){int ck;b.col = b.Think ();ck = b.add(b.col, b. cpu);if (ck == 0){b.lin = 6 – b.tops[b.col];b. turn = b.plr;}}Draw(this.CreateGraphics ());b. turncheck ();}
Дважды щёлкаем по команде Usual (Обычный). Появляется шаблон метода, который после записи нашего кода принимает следующий вид.
Листинг 12.6. Метод-обработчик выбора команды.
private void easyToolStripMenuItem_Click (object sender, EventArgs e){easyToolStripMenuItem.Checked = true;hardToolStripMenuItem.Checked = false;normalToolStripMenuItem.Checked = false;//EasyLevel = 3;b = new Connect4Board ();b.rec = Level;Invalidate ();}
Дважды щёлкаем по команде Normal (Нормальный). Появляется шаблон метода, который после записи нашего кода принимает следующий вид.
Листинг 12.7. Метод-обработчик выбора команды.
private void normalToolStripMenuItem_Click (object sender, EventArgs e){hardToolStripMenuItem.Checked = false;easyToolStripMenuItem.Checked = false;normalToolStripMenuItem.Checked = true;//NormalLevel = 5;b = new Connect4Board ();b.rec = Level;Invalidate ();}
Дважды щёлкаем по команде Difficult (Сложный). Появляется шаблон метода, который после записи нашего кода принимает следующий вид.
Листинг 12.8. Метод-обработчик выбора команды.
private void hardToolStripMenuItem_Click (object sender, EventArgs e){hardToolStripMenuItem.Checked = true;easyToolStripMenuItem.Checked = false;normalToolStripMenuItem.Checked = false;//HardLevel = 7;b = new Connect4Board ();b.rec = Level;Invalidate ();}
Схема записи и вывода справочной информации, например, с правилами игры после выбора дополнительной команды Справка (для элемента управления MenuStrip) дана ранее и в наших книгах на сайте ZharkovPress.ru в параграфе «Методика добавления информации в справочную форму».
Мы закончили написание программы в главный класс Form1 (для формы Form1 с пользовательским интерфейсом игры).
Теперь в наш проект добавляем новый файл (для программирования соответствующих игровых действий).
Добавить в проект файл можно по двум вариантам.
По первому варианту, добавляем в проект нужный файл по обычной схеме: в панели Solution Explorer выполняем правый щелчок по имени проекта, в контекстном меню выбираем Add, Existing Item, в панели Add Existing Item в окне «Files of type» выбираем «All Files», в центральном окне находим (например, в папке компьютера файл, скопированный из Интернета), выделяем имя этого файла и щёлкаем кнопку Add (или дважды щёлкаем по имени этого файла).
По второму варианту, в панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item, в панели Add New Item выделяем шаблон Code File, в окне Name записываем имя Connect4Board. cs и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем код со следующего листинга.
Листинг 12.9. Новый файл.
using System;using System. Drawing;using System.Collections;using System.Windows.Forms;namespace Connect4{public class Connect4Board{public Random random = new Random ();public int [,] arr = new int [7, 6];public int [,] thn = new int [7, 6];public int [] tops = new int [7];public int player, computer, endt = 0;public int plr = 1, cpu = 2, rec = 3, turn = 1;public int m, n, r, temp, so, ch, col, t, y;public int plrcoin, cpucoin;public int lin;public Connect4Board (){}public int Think (){int i;i = rec;return check (i, -9999999, 9999999);}public void turncheck (){int temp;char [] toto = new char [20];temp = checkwin ();if (temp == plr){MessageBox.Show («You won!»);endt = 1; return;}if (temp == cpu){MessageBox.Show («You lost.»);endt = 1; return;}if (temp == 0){for (t = 0; t <= 6; t++)if (tops [t] <6) temp = 1;if (temp == 0){//drawn ();endt = 1;return;}}}public int check (int i, int alpha, int beta){int co, score, t, g, j = 0, p;i – ;if (i == -1) {score = position (); return score;}if (i % 2 == 0){int max = 0, k;j = 0; co = 0;for (t = 0; t <7; t++){g = add (t, cpu);if (g == 0){if (checkwin () == cpu){sub (t);if (i == rec – 1)return t;else return 9000;}k = check (i, alpha, 999999);if (k> alpha) alpha = k;sub (t);if (k> beta) return k;if (co == 0) {max = k; co = 1; j = t;}if (k == max){p = (random.Next (6)) +1;if (p> 4) j = t;}if (k> max) {max = k; j = t;}}}score = max;}else{int min = 0, k = 0;co = 0;for (t = 0; t <7; t++){g = add (t, plr);if (g == 0){if (checkwin () == plr){sub (t);/*if (i==rec-1) return t; else*/return -10000;}k = check (i, -99999, beta);if (k}