Читать книгу Справочник Жаркова по проектированию и программированию искусственного интеллекта. Том 3: Программирование на Visual C# искусственного интеллекта (продолжение 2) - Валерий Алексеевич Карданов, Дарья Андреевна Самсонова, Наталья Сергеевна Кузьмина - Страница 21
Введение
1.19. Код программы
ОглавлениеОткрываем файл Form1.cs (например, по схеме: File, Open, File) и в самом верху импортируем пространства имён для управления соответствующими классами:
using System.Reflection; //Namespace for class Assembly.
using System.Media; //Namespace for class SoundPlayer.
using System.IO; //Namespace for class Stream.
В классе Form1 нашего проекта записываем следующие переменные и методы.
Листинг 1.1. Переменные и методы.
//Shoe of cards:
CardShoe shoe;
CardHand playerHand = new CardHand ();
Card dealerHoleCard;
CardHand dealerHand = new CardHand ();
//Bank of a game:
Pot pot;
//We declare an object for a help form:
Form2 helpForm;
Image loadingImage = null;
Image bankImage = null;
public enum GameMode
{
LoadingDisplay,
PlacingBets,
PlayerActive,
PlayerWon,
PlayerBust,
PocketJack,
DealerActive,
DealerWon,
DealerBust,
Push
}
GameMode modeValue;
GameMode mode
{
get
{
return modeValue;
}
set
{
switch (value)
{
case GameMode. LoadingDisplay:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Point»;
StayToolStripMenuItem. Enabled = false;
MenuToolStripMenuItem. Text = «21»;
MenuToolStripMenuItem. Enabled = false;
break;
case GameMode.PlacingBets:
BetMinusToolStripMenuItem1.Enabled = true;
BetPlusToolStripMenuItem. Enabled = true;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Себе»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
break;
case GameMode.PlayerActive:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = true;
StayToolStripMenuItem. Text = «Take a card to yourself»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//Disturbing noise of the support groups
//surrounding the player:
//not single, but continuous Looping;
Assembly a = Assembly.GetExecutingAssembly ();
Stream s =
a.GetManifestResourceStream (
«PocketJack. pj_bg_noise. wav»);
SoundPlayer player = new SoundPlayer (s);
player.PlayLooping ();
break;
case GameMode.PlayerWon:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//An applause in our address for a prize in cards:
Assembly a1 = Assembly.GetExecutingAssembly ();
Stream s1 =
a1.GetManifestResourceStream (
«PocketJack. pj_claps. wav»);
SoundPlayer player1 = new SoundPlayer (s1);
player1.Play ();
break;
case GameMode.PlayerBust:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//Discontent of support group with our exceeding of
//cards:
Assembly a2 = Assembly.GetExecutingAssembly ();
Stream s2 =
a2.GetManifestResourceStream (
«PocketJack. pj_busted. wav»);
SoundPlayer player2 = new SoundPlayer (s2);
player2.Play ();
break;
case GameMode. PocketJack:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//A victorious tune after Pocket Jack with 21 points:
Assembly a3 = Assembly.GetExecutingAssembly ();
Stream s3 =
a3.GetManifestResourceStream (
«PocketJack. pj_pj. wav»);
SoundPlayer player3 = new SoundPlayer (s3);
player3.Play ();
break;
case GameMode.DealerActive:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = false;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
break;
case GameMode.DealerWon:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//Discontent of support group with our exceeding of
//cards:
Assembly a4 = Assembly.GetExecutingAssembly ();
Stream s4 =
a4.GetManifestResourceStream (
«PocketJack. pj_busted. wav»);
SoundPlayer player4 = new SoundPlayer (s4);
player4.Play ();
break;
case GameMode.DealerBust:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
//An applause in our address for a prize in cards:
Assembly a5 = Assembly.GetExecutingAssembly ();
Stream s5 =
a5.GetManifestResourceStream (
«PocketJack. pj_claps. wav»);
SoundPlayer player5 = new SoundPlayer (s5);
player5.Play ();
break;
case GameMode. Push:
BetMinusToolStripMenuItem1.Enabled = false;
BetPlusToolStripMenuItem. Enabled = false;
HitMeToolStripMenuItem. Enabled = false;
StayToolStripMenuItem. Text = «Distribution of cards»;
StayToolStripMenuItem. Enabled = true;
MenuToolStripMenuItem. Text = «Menu»;
MenuToolStripMenuItem. Enabled = true;
break;
}
modeValue = value;
this.Invalidate ();
}
}
//We load the game objects:
public void init ()
{
System.Reflection.Assembly asm =
System.Reflection.Assembly.GetExecutingAssembly ();
loadingImage = new Bitmap(asm.GetManifestResourceStream (
"PocketJack.images.loading.gif»));
bankImage = new Bitmap(asm.GetManifestResourceStream (
"PocketJack.images.bank.jpg»));
mode = GameMode. LoadingDisplay;
this.Visible = true;
this.Refresh ();
pot = new Pot ();
//We create also initsializuy the help Form2 form:
helpForm = new Form2 ();
}
void startGame ()
{
shoe = new CardShoe ();
//We comment in the original,
//since this line – only for testing of a game:
//shoe =
//new CardShoe (new byte [] {2, 14, 11, 25, 10, 7, 6, 5});
pot.ResetPot ();
mode = GameMode.PlacingBets;
}
void startHand ()
{
mode = GameMode.PlacingBets;
}
void showPot ()
{
this. Text =
«Bank: " + pot.PotValue.ToString () + " Bet: " +
pot.BetValue.ToString ();
}
void startPlay ()
{
//We commented out in the original:
//pot.DoPlaceBet ();
//We write in the original:
if (mode == GameMode.PlayerBust && mode == GameMode.DealerWon)
pot.DoPlaceBet ();
showPot ();
// clear the hands
playerHand.Clear ();
dealerHand.Clear ();
// deal the face down hole card
dealerHoleCard = shoe.DealCard ();
dealerHoleCard. FaceUp = false;
dealerHand.Add (dealerHoleCard);
// deal the first player card
playerHand.Add(shoe.DealCard ());
// deal the second dealer card (face up)
dealerHand.Add(shoe.DealCard ());
// deal the second player card
playerHand.Add(shoe.DealCard ());
if ((dealerHand. BlackJackScoreHand () == 21) &&
(playerHand. BlackJackScoreHand ()!= 21))
{
//We write in the original:
pot.DoPlaceBet ();
pot. HouseWins ();
showPot ();
//Discontent of support group with our exceeding of
//cards:
Assembly a2 = Assembly.GetExecutingAssembly ();
Stream s2 =
a2.GetManifestResourceStream («PocketJack. pj_busted. wav»);
SoundPlayer player2 = new SoundPlayer (s2);
player2.Play ();
dealerHoleCard. FaceUp = true;
mode = GameMode.DealerWon;
return;
}
if ((playerHand. BlackJackScoreHand () == 21) &&
(dealerHand. BlackJackScoreHand ()!= 21))
{
pot.PlayerWins ();
showPot ();
dealerHoleCard. FaceUp = true;
mode = GameMode. PocketJack;
return;
}
if ((playerHand. BlackJackScoreHand () == 21) &&
(dealerHand. BlackJackScoreHand () == 21))
{
pot.DoPushBet ();
showPot ();
dealerHoleCard. FaceUp = true;
mode = GameMode. Push;
return;
}
mode = GameMode.PlayerActive;
}
Font messageFont = new Font(FontFamily.GenericSansSerif, 20,
FontStyle.Regular);
void paintForm (Graphics g)
{
switch (mode)
{
case GameMode. LoadingDisplay:
//We draw all images below the menu:
g. DrawImage (
bankImage, 0, StayToolStripMenuItem. Height);
g. DrawImage (
loadingImage, 0, StayToolStripMenuItem. Height +60);
break;
case GameMode.PlacingBets:
g. DrawImage (bankImage, 0, StayToolStripMenuItem. Height);
Utilities.BigText («Bank: " + pot.PotValue.ToString (),
10, 40, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Bet: " +
pot.BetValue.ToString (), 10, 80, Color. Black,
Color. Yellow, messageFont, g);
break;
case GameMode.PlayerActive:
dealerHand. DrawHand (g, 10, 30, 80, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
break;
case GameMode.PlayerWon:
case GameMode. PocketJack:
dealerHand. DrawHand (g, 10, 30, 20, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (dealerHand. BlackJackScoreHand ().
ToString (), 140, 45, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Вы выиграли!»,
20, 80, Color. Black, Color. Yellow, messageFont, g);
break;
case GameMode.PlayerBust:
dealerHand. DrawHand (g, 10, 30, 80, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Вы перебрали.»,
20, 80, Color. Black, Color.Red, messageFont, g);
break;
case GameMode.DealerActive:
dealerHand. DrawHand (g, 10, 30, 20, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (dealerHand. BlackJackScoreHand ().
ToString (), 140, 45, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
break;
case GameMode.DealerWon:
dealerHand. DrawHand (g, 10, 30, 20, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (dealerHand. BlackJackScoreHand ().
ToString (), 140, 45, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Вы потеряли.»,
20, 80, Color. Black, Color.Red, messageFont, g);
break;
case GameMode.DealerBust:
dealerHand. DrawHand (g, 10, 30, 20, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (dealerHand. BlackJackScoreHand ().
ToString (), 140, 45, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Dealer Bust»,
20, 80, Color. Black, Color. Yellow, messageFont, g);
break;
case GameMode. Push:
dealerHand. DrawHand (g, 10, 30, 20, 25);
playerHand. DrawHand (g, 10, 135, 20, 25);
Utilities.BigText (dealerHand. BlackJackScoreHand ().
ToString (), 140, 45, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText (playerHand. BlackJackScoreHand ().
ToString (), 140, 150, Color. Black,
Color. Yellow, messageFont, g);
Utilities.BigText («Вы выиграли.»,
20, 80, Color. Black, Color. Yellow, messageFont, g);
break;
}
}
void playerHits ()
{
if (playerHand. BlackJackScoreHand () <21)
{
playerHand.Add(shoe.DealCard ());
if (playerHand. BlackJackScoreHand ()> 21)
{
//We write in the original:
pot.DoPlaceBet ();
pot. HouseWins ();
showPot ();
mode = GameMode.PlayerBust;
}
this.Invalidate ();
}
}
void playerStays ()
{
dealerHoleCard. FaceUp = true;
mode = GameMode.DealerActive;
this.Refresh ();
System.Threading.Thread.Sleep (750);
while (dealerHand. BlackJackScoreHand () <17)
{
dealerHand.Add(shoe.DealCard ());
this.Refresh ();
System.Threading.Thread.Sleep (750);
}
if (dealerHand. BlackJackScoreHand ()> 21)
{
mode = GameMode.DealerBust;
pot.PlayerWins ();
showPot ();
return;
}
if (playerHand. BlackJackScoreHand ()>
dealerHand. BlackJackScoreHand ())
{
mode = GameMode.PlayerWon;
pot.PlayerWins ();
showPot ();
return;
}
if (playerHand. BlackJackScoreHand () <
dealerHand. BlackJackScoreHand ())
{
mode = GameMode.DealerWon;
//We write in the original:
pot.DoPlaceBet ();
pot. HouseWins ();
showPot ();
return;
}
if (playerHand. BlackJackScoreHand () ==
dealerHand. BlackJackScoreHand ())
{
mode = GameMode. Push;
pot.DoPushBet ();
showPot ();
return;
}
}
void doLeftMenuKey ()
{
switch (mode)
{
case GameMode. LoadingDisplay:
break;
case GameMode.PlacingBets:
startPlay ();
break;
case GameMode.PlayerActive:
playerStays ();
break;
case GameMode. PocketJack:
case GameMode.PlayerWon:
case GameMode.PlayerBust:
case GameMode.DealerActive:
case GameMode.DealerWon:
case GameMode.DealerBust:
case GameMode. Push:
startHand ();
break;
}
}
void doEnter ()
{
switch (mode)
{
case GameMode. LoadingDisplay:
break;
case GameMode.PlacingBets:
startPlay ();
break;
case GameMode.PlayerActive:
playerHits ();
break;
case GameMode. PocketJack:
case GameMode.PlayerWon:
case GameMode.PlayerBust:
case GameMode.DealerActive:
case GameMode.DealerWon:
case GameMode.DealerBust:
case GameMode. Push:
startHand ();
break;
}
}
void doUp ()
{
switch (mode)
{
case GameMode.PlacingBets:
pot.DoIncreaseBet ();
showPot ();
this.Invalidate ();
break;
}
}
void doDown ()
{
switch (mode)
{
case GameMode.PlacingBets:
pot.DoDecreaseBet ();
showPot ();
this.Invalidate ();
break;
}
}
void showHelp ()
{
helpForm.ShowDialog ();
}
В панели Properties (для Form1) на вкладке Events дважды щёлкаем по имени события Load. Появившийся шаблон метода Form1_Load после записи нашего кода принимает следующий вид.
Листинг 1.2. Метод для загрузки файлов объектов.
private void Form1_Load (object sender, EventArgs e)
{
//We load the game objects:
init ();
startGame ();
}
В панели Properties (для Form1) на вкладке Events дважды щёлкаем по имени события Paint. Появившийся шаблон метода Form1_Paint после записи нашего кода принимает следующий вид. Напомним, что другие варианты вывода изображения, например, на элемент управления PictureBox и после щелчка по какому-либо элементу управления уже приводились ранее.
Листинг 1.3. Метод для рисования изображений.
private void Form1_Paint (object sender, PaintEventArgs e)
{
paintForm(e.Graphics);
}
Для задания режимов и управления игрой воспользуемся каким-либо элементом управления или компонентом. Как и выше, с панели инструментов Toolbox переносим на форму компонент MenuStrip и щёлкаем по нему (ниже формы в режиме проектирования). На форме Form1 появляются окна с надписью Type Here, в которые записываем команды (по второму варианту, можно записывать в панели Properties в свойстве Text): Stay, Menu, Exit, HitMe, BetPlus, BetMinus, Sound, Help, рис. 1.30. Для команды, слева от которой поставлен флажок, в панели Properties для этой команды значение Checked следует задать как True.
Рис. 1.30. Команды элемента управления MenuStrip.
В режиме редактирования дважды щёлкаем по левой команде Stay (для приостановки игры). Появившийся шаблон метода после записи строки принимает следующий вид.
Листинг 1.4. Метод для приостановки игры.
private void StayToolStripMenuItem_Click (object sender, EventArgs e)
{
doLeftMenuKey ();
}
Аналогично дважды щёлкаем по команде Exit. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 1.5. Метод для окончания игры.
private void ExitToolStripMenuItem_Click (object sender, EventArgs e)
{
Application. Exit ();
}
Аналогично дважды щёлкаем по команде «Hit Me». Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 1.6. Метод для выдачи карты игроку.
private void HitMeToolStripMenuItem_Click (object sender, EventArgs e)
{
playerHits ();
}
Аналогично дважды щёлкаем по команде «BetPlus». Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 1.7. Метод для увеличения Ставки.
private void BetPlusToolStripMenuItem_Click (object sender, EventArgs e)
{
doUp ();
}
Аналогично дважды щёлкаем по команде «Ставка -” (Bet -). Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 1.8. Метод для уменьшения Ставки.
private void BetMinusToolStripMenuItem1_Click (object sender, EventArgs e)
{
doDown ();
}
Методика приостановки и возобновления звука при помощи установки и удаления флажка в команде Звук (Sound) была описана выше. В данной игре, звук включён постоянно.
Аналогично дважды щёлкаем по команде Help. Появившийся шаблон метода после записи нашего кода принимает следующий вид.
Листинг 1.9. Метод для вывода справочной формы.
private void HelpToolStripMenuItem_Click (object sender, EventArgs e)
{
showHelp ();
}
Теперь программа должна управлять карточной игрой, используя любые клавиши, например, верхнюю (Up) и нижнюю (Down), левую (Left) и правую (Right) клавиши со стрелками, а также клавишу Enter (для начальной сдачи карт игроку и Банкомёту и последующей сдачи по одной карте игроку). В панели Properties (для формы Form1) на вкладке Events дважды щёлкаем по имени события KeyDown. Появившийся шаблон обработки нажатий всех клавиш после записи нашего кода для нажатий только трех клавиш (относящимся к тематике данной главы) принимает следующий вид.
Листинг 1.10. Метод для обработки нажатий клавиш.
private void Form1_KeyDown (object sender, KeyEventArgs e)
{
if ((e.KeyCode == System.Windows.Forms.Keys. Up))
{
doUp ();
e. Handled = true;
}
if ((e.KeyCode == System.Windows.Forms.Keys.Down))
{
doDown ();
e. Handled = true;
}
if ((e.KeyCode == System.Windows.Forms.Keys. Enter))
{
//Набираем себе карты:
doEnter ();
}
}
Мы закончили написание программы в главный класс Form1 (для формы Form1 с пользовательским интерфейсом игры). В этом проекте движок игры (Engine Game) находится не в файле Form1.cs (как обычно бывает), а в следующем файле CardEngine. cs.
Теперь в наш проект добавляем новые файлы (для программирования соответствующих игровых действий) по следующей схеме.
В панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item. В панели Add New Item выделяем шаблон Code File, в окне Name записываем имя нового файла с расширением *.cs и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем следующий код.
Листинг 1.11. Новый файл CardEngine. cs.
using System;
using System.Collections;
using System. Drawing;
namespace PocketJack
{
/// <summary>
/// Provides the behaviours required to manage and draw cards
/// </summary>
public class Card
{
/// <summary>
/// The number of the card, in the range 1 to 52
/// </summary>
public byte CardNo;
/// <summary>
/// Indicates if the card is to be drawn face up.
/// True by default.
/// </summary>
public bool FaceUp = true;
/// <summary>
/// The images of the cards. Stored for all the cards.
/// The image with number 0 is the
/// back pattern of the card
/// </summary>
static private Image [] cardImages = new Bitmap [53];
/// <summary>
/// The attribute to be used when drawing the card
/// to implement transpancy
/// </summary>
static public System.Drawing.Imaging.ImageAttributes
cardAttributes;
/// <summary>
/// Used when loading card images prior to drawing
/// </summary>
static private System.Reflection.Assembly execAssem;
/// <summary>
/// Sets up the color and attribute values.
/// </summary>
static Card ()
{
cardAttributes =
new System.Drawing.Imaging.ImageAttributes ();
cardAttributes.SetColorKey(Color.Green, Color.Green);
execAssem =
System.Reflection.Assembly.GetExecutingAssembly ();
}
/// <summary>
/// Scores for each of the cards in a suit
/// </summary>
static private byte [] scores =
new byte [] {11, //ace
2,3,4,5,6,7,8,9,10, //spot cards
10,10,10}; //jack, queen, king
/// <summary>
/// Picture information for each card in a suit
/// </summary>
static private bool [] isPicture =
new bool [] {false, //ace
false, false, false, false, false, false,
false, false, false, //spot cards
true, true, true}; //jack, queen, king
/// <summary>
/// Names of the suits, in the order that of the suits
/// in the number sequence
/// </summary>
static private string [] suitNames =
new string [] {«club», «diamond», «heart», «spade»};
/// <summary>
/// Names of individual cards, in the order of the cards
/// in a suit
/// </summary>
static private string [] valueNames =
new string [] {«Ace», «Deuce», «Three», «Four», «Five», «Six»,
«Seven», «Eight», «Nine», «Ten», «Jack», «Queen», «King»};
/// <summary>
/// Returns the value in points of a given card,
/// according to BlackJack rules
/// </summary>
public int BlackJackScore
{
get
{
return scores [(CardNo – 1) % 13];
}
}
/// <summary>
/// Returns true if the card is a picture
/// (i.e. jack, queen or king)
/// </summary>
public bool IsPicture
{
get
{
return isPicture [(CardNo – 1) % 13];
}
}
/// <summary>
/// Returns text of the suit of this card
/// </summary>
public string Suit
{
get
{
return suitNames [(CardNo – 1) / 13];
}
}
/// <summary>
/// Returns the text of the value of this card
/// </summary>
public string ValueName
{
get
{
return valueNames [(CardNo – 1) % 13];
}
}
/// <summary>
/// Returns true if this is a red card
/// </summary>
public bool Red
{
get
{
int suit = (CardNo – 1) / 13;
return ((suit == 1) || (suit == 2));
}
}
/// <summary>
/// Returns true if this is a black card
/// </summary>
public bool Black
{
get
{
return! Red;
}
}
/// <summary>
/// Returns an image which can be used to draw this card
/// </summary>
public Image CardImage
{
get
{
int dispNo = CardNo;
if (!FaceUp)
{
dispNo = 0;
}
if (cardImages [dispNo] == null)
{
cardImages [dispNo] = new Bitmap (
execAssem.GetManifestResourceStream (
@"PocketJack.images.» + dispNo + @".gif»));
}
return cardImages [dispNo];
}
}
/// <summary>
/// Constructs a card with a partiuclar number
/// </summary>
/// <param name=«cardNo»> number of the card
/// in the range 1 to 52 </param>
/// <param name=«faceUp»> true if the card
/// is to be drawn face up </param>
public Card (byte cardNo, bool faceUp)
{
CardNo = cardNo;
FaceUp = faceUp;
}
/// <summary>
/// Constructs a face up card with that number
/// </summary>
/// <param name=«cardNo»> </param>
public Card (byte cardNo)
: this (cardNo, true)
{
}
/// <summary>
/// String description of the card
/// </summary>
/// <returns> the name and suit of the card </returns>
public override string ToString ()
{
return ValueName + " of " + Suit;
}
}
/// <summary>
/// Provides a container for a number of cards.
/// May be used to draw the cards and compute their score.
/// </summary>
public class CardHand: ArrayList
{
/// <summary>
/// Used as a destination of teh draw action
/// </summary>
private static Rectangle drawRect;
/// <summary>
/// Draws the hand on the graphics.
/// </summary>
/// <param name=«g»> graphics to draw with </param>
/// <param name=«startx»> left edge of first card </param>
/// <param name=«starty»> top of first card </param>
/// <param name=«gapx»> x gap between each card </param>
/// <param name=«gapy»> y gap between each card </param>
public void DrawHand (Graphics g, int startx, int starty,
int gapx, int gapy)
{
drawRect. X = startx;
drawRect. Y = starty;
foreach (Card card in this)
{
drawRect. Width = card.CardImage. Width;
drawRect. Height = card.CardImage. Height;
g. DrawImage (
card.CardImage, // Image
drawRect, // destination rectange
0, // srcX
0, // srcY
card.CardImage. Width, // srcWidth
card.CardImage. Height, // srcHeight
GraphicsUnit. Pixel, // srcUnit
Card.cardAttributes); // ImageAttributes
drawRect. X += gapx;
drawRect. Y += gapy;
}
}
/// <summary>
/// Computes the score of the hand
/// </summary>
/// <returns> the value of the score </returns>
public int BlackJackScoreHand ()
{
int score = 0;
int aces = 0;
foreach (Card card in this)
{
score += card. BlackJackScore;
if (card. BlackJackScore == 11)
{
aces++;
}
}
while ((score> 21) && (aces> 0))
{
score -= 10;
aces – ;
}
return score;
}
}
/// <summary>
/// Contains a number of card decks
/// which can be dealt one at a time.
/// </summary>
public class CardShoe
{
private int noOfDecks = 1;
private byte [] decks;
private int nextCard;
private bool testShoe = false;
/// <summary>
/// True if the deck is «stacked»,
/// i.e. was created from a byte array
/// </summary>
public bool TestShoe
{
get
{
return testShoe;
}
}
private void makeShoe ()
{
decks = new byte [noOfDecks * 52];
int cardPos = 0;
for (int i = 0; i <noOfDecks; i++)
{
for (byte j = 1; j <53; j++)
{
decks [cardPos] = j;
cardPos++;
}
}
nextCard = 0;
}
private void shuffleShoe ()
{
if (!testShoe)
{
System. Random rand = new Random ();
byte swap;
int p1, p2;
for (int i = 0; i <decks. Length; i++)
{
p1 = rand.Next (decks. Length);
p2 = rand.Next (decks. Length);
swap = decks [p1];
decks [p1] = decks [p2];
decks [p2] = swap;
}
}
nextCard = 0;
}
/// <summary>
/// Gets the next card number from the deck
/// </summary>
/// <returns> The number of the next card </returns>
public byte NextCardNo ()
{
if (nextCard == decks. Length)
{
shuffleShoe ();
}
return decks [nextCard++];
}
/// <summary>
/// Gets the next card from the deck.
/// </summary>
/// <returns> A new instance of the card </returns>
public Card DealCard ()
{
return new Card (NextCardNo ());
}
/// <summary>
/// Constructs a shoe containing a number of decks
/// </summary>
/// <param name=«noOfDecks»> </param>
public CardShoe (int noOfDecks)
{
this.noOfDecks = noOfDecks;
makeShoe ();
shuffleShoe ();
testShoe = false;
}
/// <summary>
/// Constructs a shoe containing a single deck
/// </summary>
public CardShoe ()
: this (1)
{
}
/// <summary>
/// Creates a stacked deck for test purposes.
/// </summary>
/// <param name=«stackedDeck»> array of bytes </param>
public CardShoe (byte [] stackedDeck)
{
decks = stackedDeck;
testShoe = true;
}
}
}
В панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item. В панели Add New Item выделяем шаблон Code File, в окне Name записываем имя нового файла с расширением *.cs и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем следующий код.
Листинг 1.12. Новый файл Pot. cs.
using System;
namespace PocketJack
{
/// <summary>
/// Summary description for Betting.
/// </summary>
public class Pot
{
private int betValueChangeValue;
private int betValue;
private int potValue;
private const int INITIAL_POT_VALUE = 500;
private const int INITIAL_BET_CHANGE_VALUE = 5;
public int BetValue
{
get
{
return betValue;
}
}
public int PotValue
{
get
{
return potValue;
}
}
public void ResetPot ()
{
betValueChangeValue = INITIAL_BET_CHANGE_VALUE;
betValue = INITIAL_BET_CHANGE_VALUE;
potValue = INITIAL_POT_VALUE;
}
public void CheckPot ()
{
if (betValue> potValue)
{
if (System.Windows.Forms.MessageBox.Show (
«Insufficient funds for the bet.» +
«Do you want to reload the pot?»,
«Bank»,
System.Windows.Forms.MessageBoxButtons. YesNo,
System.Windows.Forms.MessageBoxIcon. Question,
System.Windows.Forms.
MessageBoxDefaultButton. Button1) ==
System.Windows.Forms. DialogResult. Yes)
{
ResetPot ();
}
else
{
betValue = potValue;
}
}
}
public void DoIncreaseBet ()
{
betValue = betValue + betValueChangeValue;
CheckPot ();
}
public void DoDecreaseBet ()
{
if (betValue> = betValueChangeValue)
{
betValue = betValue – betValueChangeValue;
}
}
public void PlayerWins ()
{
// win back 2 * our stake
potValue = potValue + betValue;
//potValue = potValue + betValue; //We commented out.
}
public void HouseWins ()
{
CheckPot ();
}
public void DoPushBet ()
{
// put the betValue back in the potValue
potValue = potValue + betValue;
}
public void DoPlaceBet ()
{
potValue = potValue – betValue;
}
public Pot ()
{
ResetPot ();
}
}
}
После этого добавления в панели Solution Explorer должны быть файлы, показанные на рисунке выше. Дважды щёлкая по имени файла, любой файл можно открыть, изучить и редактировать.
В этих файлах использованы XML-комментарии (XML comment), где XML – Extensible Markup Language – расширяемый язык разметки, типа:
/// <summary>
/// Description of a variable:
/// </summary>
который состоит из начального тэга (start tag):
/// <summary>
и конечного тэга (end tag):
/// </summary>
между которыми записывается сам комментарий:
/// Описание переменной:
/// Description of a variable:
В отличие от обычных комментариев после двойного слеша // или между двух символов /*…*/, в любом месте программы при наведении указателя мыши на переменную с XML-комментарием появляется подсказка, в которой имеется не только тип и класс переменной (как для переменной с обычным комментарием или вообще без комментария), но также имеется и её описание на любом языке, в том числе на русском языке, которое мы записали между начальным и конечным тэгами. XML-комментарии имеют и другие преимущества, описанные в специальной литературе.
К недостатку 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» +
«<BetPlus -> и <BetMinus -> " +
«в Меню для элемента управления 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 (которую можно передвинуть в любое удобное место экрана).