Полная версия
Справочник Жаркова по проектированию и программированию искусственного интеллекта. Том 8: Программирование на Visual C# искусственного интеллекта. Издание 2. Продолжение 1
К недостатку XML-комментария, относятся две дополнительные строки начального и конечного тэгов, увеличивающие (и без них) большое количество строк в программе.
1.20. Методика рисования текстов на основе класса
Для рисования текстов на экране при помощи универсального (для многих других игр) класса Utilities,
В панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item. В панели Add New Item выделяем шаблон Code File, в окне Name записываем имя нового файла с расширением *.cs и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем следующий код.
Листинг 1.13. Файл Utilities.cs.
using System.Drawing;
namespace PocketJack
{
public class Utilities
{
static private SolidBrush messageBrush =
new SolidBrush(Color.Black);
public static void BigText(string message, int x, int y,
Color back, Color fore, Font messageFont, Graphics g)
{
int i;
messageBrush.Color = back;
for (i = 1; i < 3; i++)
{
g.DrawString(message, messageFont, messageBrush,
x – i, y – i);
g.DrawString(message, messageFont, messageBrush,
x – i, y + i);
g.DrawString(message, messageFont, messageBrush,
x + i, y – i);
g.DrawString(message, messageFont, messageBrush,
x + i, y + i);
}
messageBrush.Color = fore;
g.DrawString(message, messageFont, messageBrush, x, y);
}
public Utilities()
{
// TODO: Add constructor logic here
}
}
}
Этот файл Utilities.cs мы будем использовать в нескольких приведённых далее играх для рисования текстов на экране , но там мы не будем приводить этот файл, для экономии места в книге, а будем давать только ссылку на этот параграф.
Сразу же здесь отметим, что для использования данного файла Utilities.cs в проекте с другим именем (отличным от имени данного проекта PocketJack), необходимо:
или в данном файле Utilities.cs вместо имени пространства имён (с именем проекта PocketJack) в строке:
namespace PocketJack
записать имя нового проекта,
или в новом проекте в файле, где используется ссылка на данный файл Utilities.cs, в верхней части импортировать (записать директиву) пространства имён PocketJack:
using PocketJack;
1.21. Методика добавления информации в справочные формы
Для ввода в проект новой (справочной) формы, по которой игрок будет изучать, например, правила игры, в меню Project выбираем Add Windows Form, в панели Add New Item оставляем заданные по умолчанию параметры и щёлкаем кнопку Add. В ответ Visual Studio выводит новую форму Form2 (рис. 1.31) и добавляет в панель Solution Explorer новый пункт Form2.cs.
Рис. 1.31. Проектируем справочную форму. Рис. 1.32 В свойстве Multiline выбираем True.
Аналогично, как первую, проектируем вторую форму и вводим элемент управления в виде окна TextBox. Чтобы в это окно можно было записать многострочный текст, в панели Properties (для этого элемента) в свойстве Multiline выбираем значение True (рис. 1.32).
По этой схеме можно добавлять и большее количество форм, сколько необходимо для каждого конкретного приложения. Для закрытия второй формы можно воспользоваться каким-либо элементом управления или компонентом. А можно использовать уже автоматически размещенный на форме крестик Close, которым мы и будем пользоваться.
Теперь мы должны написать программу для второй формы Form2. Открываем (например, по схеме: File, Open, File) файл Form2.cs и в методе-конструкторе класса Form2 ниже следующих строк:
public Form2()
{
InitializeComponent();
записываем следующий код для выдачи на экран на элемент управления TextBox справочной информации.
Листинг 1.14. Код для выдачи на экран справочной информации.
StringBuilder sbl;
sbl = new StringBuilder();
sbl.Append("Правила игры в очко:\r\n\r\n\r\n");
sbl.Append("Rules of the game in “point”:\r\n\r\n\r\n");
sbl.Append("1) Вы являетесь игроком (player) и играете " +
sbl.Append("1) You are a player and play" +
"один на один с банкомётом (dealer).\r\n\r\n");
"in private with a dealer.\r\n\r\n");
sbl.Append("2) Ваша цель состоит в том, чтобы иметь в руке " +
sbl.Append("2) Your purpose consists in having in a hand" +
"карты с очками, как можно ближе к 21, но не превышая 21, " +
"a card with points as it is possible closer to 21, " +
"but without exceeding 21," +
"и больше, чем у банкомёта.\r\n\r\n");
"and more, than at dealer.\r\n\r\n");
sbl.Append("3) Первоначально у вас имеются две карты, " +
sbl.Append("3) Originally are available for you two cards," +
"вы видите сумму очков этих двух карт, и вы можете взять " +
"you see the sum of points of these two cards, " +
"and you can take" +
"дополнительные карты, нажимая кнопку Enter " +
"the additional cards, pressing the Enter key" +
"или выбирая команду “Карту мне” в Меню " +
"or choosing the command "HitMe" in the Menu" +
"для элемента управления mainMenu1.\r\n\r\n");
"for the control mainMenu1.\r\n\r\n control");
sbl.Append("4) Если общее количество очков " +
sbl.Append("4) If total quantity of points" +
"ваших карт превышает 21, " +
"of your cards exceeds 21," +
"вы взяли лишние карты и теряете вашу ставку.\r\n\r\n");
"you took excess cards and lose yours bet.\r\n\r\n");
sbl.Append("5) Если банкомёт набрал такое же количество очков, " +
sbl.Append("5) If a dealer scored the same quantity of points," +
"как и вы, побеждаете вы, " +
"as well as you, win you," +
"и счёт увеличивается в вашу пользу.\r\n\r\n");
"and the account increases in yours advantage.\r\n\r\n");
sbl.Append("6) Значения очков каждой карты следующие:\r\n");
sbl.Append("6) Values of points of each card following:\r\n");
sbl.Append("Ace – A = 1 or 11; " +
"как 1-я, 2-я или 3-я карта – Туз даёт 11 очков; " +
"as the 1st, 2nd or 3rd card – Ace gives 11 points;" +
"например, с Валетом, Дамой и Королём Туз даёт 11 очков " +
"for example, with Jack, Gueen and King, Ace gives 11 points" +
"и в сумме 10+11=21 эти две карты называются PocketJack, " +
"and in the sum 10+11=21 these two cards are called " +
"PocketJack," +
"который бьёт карты соперника, даже набравшие 21; " +
"who covers the rival's cards, even gathered 21;" +
"как 4-я и последующая карта – Туз даёт 1 очко;\r\n");
"as the 4th and subsequent card – Ace gives 1 point; \r\n");
sbl.Append("цифры на картах от 2 до 9 " +
sbl.Append("Digits on cards from 2 to 9" +
"означают очки этой карты;\r\n");
"mean the points of this card; \r\n");
sbl.Append("карта с числом 10, " +
sbl.Append ("a card with number 10," +
"Jack – J, " +
"Queen – Q, " +
"King – K = on 10 points." +
"\r\n\r\n");
sbl.Append("7) Если первые две карты у игрока или банкомёта " +
sbl.Append("7) If the first two cards at player or dealer" +
"набрали 21 очко, то они также " +
"gathered 21 points, they also" +
"бьют карты соперника, даже набравшие 21.\r\n\r\n");
"cover the rival's cards, even gathered 21.\r\n\r\n");
sbl.Append("8) Банкомёт сдаёт карты " +
sbl.Append("8) Dealer hands over cards" +
"с единственной колоды карт.\r\n\r\n");
"from the only shoe of cards.\r\n\r\n");
sbl.Append("9) Банкомёт будет сдавать себе карты, " +
sbl.Append("9) Dealer will hand over itself cards," +
"пока не достигнет 17 или больше.\r\n\r\n");
"will not reach 17 or it is more.\r\n\r\n");
sbl.Append("10) Первая карта банкомёта может сдаваться " +
sbl.Append("10) The first card of a dealer can be given" +
"лицевой стороной вниз и быть невидимой.\r\n\r\n");
"the face down and to be nevidimoy.\r\n\r\n");
sbl.Append("11) Вы должны или оставить ставку по умолчанию, " +
sbl.Append("11) You should or to leave a bet by default," +
"или установить новую вашу ставку до раздачи карт " +
"or to set your new bet before distribution of cards" +
"(в последнем случае используйте команды " +
" (in the latter case use the commands" +
"
"в Меню для элемента управления mainMenu1).\r\n\r\n");
"in the Menu for the mainMenu1 control).\r\n\r\n");
sbl.Append("12) Ваше значение банка " +
sbl.Append("12) Your value of bank" +
"все время показывают на экране. " +
"all the time show on the screen." +
"Если значение банка станет ниже вашей ставки, " +
"If value of bank becomes below your bet," +
"вам предложат начать новую игру.\r\n\r\n");
"to you will suggest to begin the new game.\r\n\r\n");
sbl.Append("13) Когда вы набрали карты, вы можете " +
sbl.Append ("13) When you gathered cards, you can" +
"приостановить игру, выбрав в Меню команду Останов. " +
"suspend a game, having chosen in Menu the command Stop." +
"Банкомёт покажет свою карту.\r\n\r\n");
"Dealer will show the card.\r\n\r\n");
sbl.Append("14) Схема оплаты:\r\n");
sbl.Append ("14) Scheme of payment:\r\n");
sbl.Append("проигравший платит победителю по договорённости, " +
sbl.Append ("the loser pays the winner by agreement," +
"например, 1:1;\r\n");
"for example, 1:1; \r\n");
sbl.Append("игра в очко желает вам всего наилучшего .\r\n");
sbl.Append ("The game in a point wishes you all the best.\r\n");
textBox1.Text = sbl.ToString();
Естественно, текст в этом листинге мы можем редактировать, как пожелаем.
В режиме выполнения, после выбора команды Помощь на форме Form1, поверх этой формы Form1 появляется справочная форма Form2 (рис. 1.33). Внутри элемента управления TextBox мигает курсор, который мы можем перемещать клавишами, одновременно перемещая текст, чтобы он стал видимым. На рис. 1.33 видны первые три правила игры, а следующие правила 4, 5 и 6 на форме уже были показаны ранее.
Рис. 1.33. Справочная форма Form2.
Напомним, что выше мы записали код для вывода справочной формы Form2 методом ShowDialog как модальной формы (рис. 1.34), а именно, мы не сможем продолжить игру на первой форме Form1 (и на любой другой форме), пока не закроем форму Form2.
Рис. 1.34. Код для вывода Form2 методом ShowDialog как модальной формы.
Для вывода справочной формы Form2 методом Show как немодальной формы надо записать:
helpForm.Show();
Теперь, активируя (щёлкая) форму Form1, мы сможем продолжить игру на первой форме Form1 (и на любой другой форме любого приложения), видя на экране справочную форму Form2 (которую можно передвинуть в любое удобное место экрана).
1.22. Запуск игры
Строим и запускаем программу на выполнение обычным образом:
Build, Build Selection; Debug, Start Without Debugging.
В ответ, Visual C# выводит форму Form1 с показанным выше фоном данной игры.
Отметим, что все графические файлы этой игры применимы как для смартфонов и планшетов (описанных в наших предыдущих книгах), так и для настольных компьютеров. Для настольного компьютера с большим (чем у смартфона и планшета) экраном размеры рисунков фона и карт можно увеличить (в случае необходимости) по описанной ранее схеме.
Для начала игры нажимаем клавишу Enter и играем согласно приведённым выше правилам.
Таким образом, эта глава описывает механизм раздачи карт (из колоды) случайным образом (на основе генератора случайных чисел класса Random), показ их на экране, показ их в "руках" для каждого игрока и управления карточной игрой.
Мы разработали методику программирования полностью функциональной игры, которая в США и других странах называется как “Black Jack”, “21” или понтон (pontoon) при использовании 52 карт, а в России и других странах обычно называется как “очко” или “21” при использовании 36 карт.
В разработанной в данной главе игре можно вместо 52 карт добавить в проект новые 36 карт (в случае необходимости) или из 52 карт удалить лишние карты, чтобы оставить только 36 карт.
По методике данной главы можно разрабатывать самые разнообразные карточные игры.
Часть
I
I. Методология программирования искусственного интеллекта в спортивных играх с ракетками и мячами
Глава 2. Методика программирования искусственного интеллекта в игре в теннис для игрока с компьютером или двух игроков
2.1. Общие сведения
Разработаем методику проектирования и программирования такой типичной и широко распространённой игры, как игра в теннис с мячом и двумя ракетками.
Кратко, сюжет игры заключается в следующем. После старта игры появляется форма, справа на форме (точнее, в клиенткой области формы) находится ракетка Игрока 1, слева – ракетка Игрока 2 (по первому режиму игры) или Компьютера в роли Игрока 2 (по второму режиму игры). Обе ракетки могут перемещаться только вертикально. Мяч произвольным образом прыгает в пределах 2-х границ формы, отскакивая от верхней и нижней границ формы и двух ракеток. Правая граница формы – это ворота Игрока 1, а левая граница формы – это ворота Компьютера в роли Игрока 2 или самого Игрока 2. Если мяч коснётся левой или правой границ формы, считается, что один игрок забил мяч в ворота другого игрока, у пропустившего мяч игрока количество жизней (lives) с трёх уменьшается на единицу, а мяч снова появляется в произвольной точке формы и летит в произвольном направлении.
По первому режиму игры Singleplayer (Игрока 1 с компьютером), заданному по умолчанию, Игрок 1 при помощи клавиш со стрелками перемещает правую ракетку, старается отбить ею мяч и не дать мячу коснуться правой границы формы, так как после каждого такого касания (пропущенного в свои ворота мяча) Игрок 1 теряет одну жизнь. Более того, Игрок 1 должен стараться (по возможности) ракеткой отбить мяч таким образом, чтобы забить его в противоположные ворота. Аналогично поступает Компьютер с левой ракеткой.
По второму режиму игры Multiplayer (Игрока 1 с Игроком 2), Игрок 1 действует так же, как по первому режиму, а вот Игрок 2 при помощи клавиш W и S перемещает левую ракетку, старается отбить ею мяч и не дать мячу коснуться левой границы формы, так как после каждого такого касания (пропущенного в свои ворота мяча) Игрок 2 теряет одну жизнь.
В программе в методе GetInputStates в строках:
if (gamePadUp ||
keyboard.IsKeyDown(Keys.Up))
rightPaddlePosition -= moveFactorPerSecond;
if (gamePadDown ||
keyboard.IsKeyDown(Keys.Down))
мы видим, что Игрок 1 управляет правой ракеткой при помощи клавиш со стрелками Up и Down.
В методе GetInputStates в строках:
if (gamePad2Up ||
keyboard.IsKeyDown(Keys.W))
leftPaddlePosition -= moveFactorPerSecond;
if (gamePad2Down ||
keyboard.IsKeyDown(Keys.S) ||
keyboard.IsKeyDown(Keys.O))
leftPaddlePosition += moveFactorPerSecond;
мы видим, что Игрок 2 управляет левой ракеткой при помощи клавиш W, S и O.
По мере игры скорость перемещения мяча увеличивается, а угол отскока мяча от границ формы и ракеток изменяется, что приводит к достаточно быстрому окончанию игры.
Игра прекращается, когда один из игроков потеряет все жизни. Победителем считается игрок, у которого сохранилась хотя бы одна жизнь.
2.2. Правила игры
1. После запуска игры, на экране появляется форма с меню Singleplayer, Multiplayer и Exit (рис. 2.1). Клавишами со стрелками выбираем нужную команду и нажимаем клавишу Enter.
2. Появляется поле игры, на котором справа находится синяя (Blue) ракетка Игрока 1, а слева – красная (Red) ракетка Компьютера в роли Игрока 2 (по первому режиму игры Singleplayer) или Игрока 2 (по второму режиму игры Multiplayer).
Вверху слева и справа находятся два табло с тремя жизнями Lives в виде начальных трёх шаров для двух игроков (рис. 2.2).
Мяч произвольным образом прыгает в пределах 4-х границ формы, отскакивая от границ формы и двух ракеток. После каждого такого отскока мы слышим звук добавленного в проект звукового файла. Правая граница формы – это ворота Игрока 1, а левая граница формы – это ворота Игрока 2 (или Компьютера в роли Игрока 2).
Рис. 2.1. Выбираем режим игры Singleplayer или Multiplayer.
Рис. 2.2. Справа – ракетка Игрока 1, слева – ракетка Игрока 2, мяч – в нижнем правом углу.
3. Предположим, что мы выбрали заданный по умолчанию первый режим игры Singleplayer (Игрока 1 с компьютером) и нажали клавишу Enter. Игрок 1 при помощи клавиш со стрелками Up и Down перемещает правую ракетку, старается отбить ею мяч и не дать мячу коснуться правой границы формы, так как после каждого такого касания (пропущенного в свои ворота мяча) Игрок 1 теряет одну жизнь. Аналогично поступает Компьютер с левой ракеткой.
4. Предположим, что клавишами со стрелками мы выбрали второй режим игры Multiplayer (Игрока 1 с Игроком 2) и нажили клавишу Enter. Игрок 1 действует так же, как по первому режиму, а вот Игрок 2 при помощи клавиш W и S перемещает левую ракетку, старается отбить ею мяч и не дать мячу коснуться левой границы формы, так как после каждого такого касания (пропущенного в свои ворота мяча) Игрок 2 теряет одну жизнь.
5. Игра прекращается, когда один из игроков потеряет все жизни.
6. Победителем считается игрок, у которого сохранилась хотя бы одна жизнь (на рис. 2.3 – это Игрок 2 с красной ракеткой Red). Мы видим сообщение Red Won. Чаще всего, Компьютер (в роли Игрока 2) побеждает.
4. Если в игре участвуют несколько человек, то победителем считается тот, у кого после окончания игры сохранилось больше жизней.
5. Форму с игрой закрываем стандартно, щёлкая на ней значок Close.
На основании этих правил можно сформулировать другие правила.
В игре можно использовать различные графические изображения и звуковые файлы различных форматов (с внесением соответствующих изменений в приведённую далее программу).
Рис. 2.3. Сообщение о победителе.
2.3. Создание проекта Visual Studio
В VS щёлкаем значок New Project (или File, New, Project), в панели New Project в окне Project types выбираем тип проекта Visual C#, XNA Game Studio в окне Templates выделяем шаблон Windows Game, в окне Name записываем имя Pong2 и щёлкаем OK. VS создаёт проект и выводит файл Game1.cs с показанным выше шаблоном кода.
В панели Solution Explorer выполняем правый щелчок по имени проекта Content, в контекстном меню выбираем Add, Existing Item, в панели Add Existing Item в окне “Files of type” выбираем “All Files”, в центральном окне находим (например, в Интернете) и с нажатой клавишей Shift или Ctrl выделяем три не звуковых файла PongGame.png, PongMenu.png и SpaceBackground.dds, и щёлкаем кнопку Add. Эти файлы добавляются в панель Solution Explorer, как показано на рис. 2.4 (файла PongSound.xap здесь пока не должно быть, он появится после создания проекта XACT). Если в панели Solution Explorer выделить файл SpaceBackground.dds, то ниже в панели Properties мы увидим тип этого файла (рис. 2.5).
Чтобы добавить в проект звуковые файлы, щёлкаем значок Open File (или в меню выбираем File, Open, File), в появившейся панели Open File в окне "Look in:" находим (например, в Интернете) папку со звуковыми файлами (в данном примере, PongBallHit.wav и PongBallLost.wav), с нажатой клавишей Shift или Ctrl выделяем все эти звуковые файлы, выполняем по ним правый щелчок и в контекстном меню выбираем команду Копировать (Copy) для копирования этих файлов в буфер обмена.
Рис. 2.4. Панель Solution Explorer. Рис. 2.5. Панель Properties.
Теперь в этой же панели Open File в окне "Look in:" находим наш проект Visual Studio и в нём папку Content, на белом поле центрального окна выполняем правый щелчок и в контекстном меню выбираем команду Вставить (Paste) для вставки звуковых файлов из буфера обмена. Эти звуковые файлы вставятся в папку Content только в панели Open File, а в панели проводника решения Solution Explorer в этой же папке Content этих звуковых файлов не будет видно.
Кратко поясним, что в данной игре файл PongBallHit.wav будет воспроизводится, когда будет происходить столкновение мяча с ракеткой. А файл PongBallLost.wav будет воспроизводится, когда игрок пропускает мяч, мяч пролетает мимо ракетки и исчезает с формы, а игрок теряет одну жизнь.
На данном этапе программирования приложения построение (Build, Build Solution) и выполнение (Debug, Start Without Debugging) программы должно происходить без ошибок, но и без звука. А чтобы получить звуковое сопровождение приложения, нам нужно создать звуковой проект типа XACT в виде файла формата (.xap), как описано в следующем параграфе. Сворачиваем или закрываем Visual Studio.
2.4. Создание звукового проекта XACT
Приведём пошаговую инструкцию создания звукового проекта типа XACT в виде файла формата Audio Project или (.xap).
1. Чтобы запустить XACT, выбираем команды Start (Пуск), All Programs (Все программы), | Microsoft XNA Game Studio Tools, Microsoft Cross-Platform Audio Creation Tool (XACT). Появляется показанная ранее панель Microsoft Cross-Platform Audio Creation Tool (XACT) v.2.0 (Windows).
2. В меню File выбираем New Project. Появляется панель New Project Path. В этой панели в окне "Папка:" находим в проекте Visual Studio папку Content, в окне "Имя файла:" записываем любое имя, например, PongSound и щёлкаем кнопку Сохранить (Save), рис. 2.6. Отметим, что если в этой панели в окне "Тип файла:" мы выберём строку All Files, то увидим также и добавленные нами в эту папку звуковые файлы.
Рис. 2.6. В проекте VS в папке Content записываем имя. Рис. 2.7. Выделяем файл.
В панели Microsoft Cross-Platform Audio Creation Tool (XACT) v.2.0 (Windows) вместо слов New Project появляется записанное нами имя файла.
3. В меню Wave Banks выбираем команду New Wave Bank. В панели XACT в центральном окне появляется панель Wave Bank.
В меню Sound Banks выбираем команду New Sound Bank. В панели XACT в центральном окне появляется панель Sound Bank.
Размещаем эти две панели Wave Bank и Sound Bank в центральном панели XACT так, как нам удобно. При щелчке по любой из этих панелей, в нижнем левом углу панели XACT появляются параметры соответствующей панели Wave Bank или Sound Bank.
4. Выполняем правый щелчок на белом поле панели Wave Bank и в контекстном меню выбираем команду Insert Wave File(s).
В появившейся панели Открыть (Open) проверяем, чтобы в окне "Папка:" была папка Content (если этой папки в окне нет, то находим ее), выделяем первый добавленный нами звуковой файл (рис. 2.7) и щёлкаем кнопку Открыть. Панель Открыть закрывается, а этот файл появляется в панели Wave Bank (со всеми заполненными свойствами).
Аналогично выполняем правый щелчок на белом поле панели Wave Bank, в контекстном меню выбираем команду Insert Wave File(s), в панели Открыть выделяем второй файл Lost.wav и щёлкаем кнопку Открыть. Этот файл появляется в панели Wave Bank.
На рисунке в окне "Тип файлов:" видно, какого формата звуковые файлы поддерживает XNA, а именно: Sound Files (*.wav, *.aif, *.aiff).
5. В панели Wave Bank сначала выделяем указателем мыши все добавленные нами звуковые файлы, затем захватываем их указателем мыши и переносим в панель Sound Bank, точнее, в нижнюю левую часть со свойством Cue Name. Эти файлы появляются в двух левых окнах панели Sound Bank. Щёлкаем по файлам, в двух правых окнах появляется информация о файлах.