5. После этого искусственный интеллект размещает в пустующие клетки следующие 3 больших мяча произвольных цветов, а 3 маленьких блокирующих мяча переносит в другие квадраты (чтобы помешать игроку построить прямую линию из 5 и более мячей), рис. 21.5.
Ниже надписи “Следующие мячи:” компьютер с искусственным интеллектом размещает 3 мяча, которые появятся на поле после перемещения игроком мяча из одного квадрата в другой.
6. Аналогично игрок снова щёлкает по выбранному им мячу и по той клетке, в которую мяч перемещается. После этого искусственный интеллект снова размещает в пустующие клетки следующие 3 больших мяча произвольных цветов, а 3 маленьких блокирующих мяча переносит в другие квадраты (чтобы помешать игроку построить прямую линию из 5 и более мячей).
Рис. 21.5. Добавляются ещё 3 больших мяча, а 3 маленьких мяча – в новых квадратах.
Рис. 21.6. Игрок подготовил вертикаль из 5 чёрных мячей с пустым 6-м квадратом.
7. На рис. 21.6 показано, как игрок подготовил вертикаль из 5 чёрных мячей с пустым 6-м квадратом.
8. На следующем ходе игрок переносит в этом 6-й пустой квадрат чёрный мяч, тем самым собрав вертикальную линию из 6 чёрных мячей.
Как только игрок соберёт горизонтальную, вертикальную или диагональную прямую линию из 5 и более мячей одинакового цвета, игроку начисляются очки (по 100 очков за каждый собранный в линию мяч), а линия из собранных мячей исчезает, освобождая квадраты, в которые искусственный интеллект далее будет размещать новые мячи.
На рис. 21.7 игроку начислено 600 очков.
9. По такой схеме игрок быстро щёлкает мышью по мячам, стараясь за отведённое время собрать как можно больше линий из мячей одинакового цвета.
Искусственный интеллект же периодически (после каждого второго щелчка игрока и следующего за ним перемещения пульсирующего мяча в новую клетку) уничтожает линию из 5 и более мячей (если такая линия собрана игроком), начисляет очки, дополняет квадраты 3 новыми разноцветными мячами (произвольным образом), а 3 маленьких блокирующих мяча переносит в другие квадраты (чтобы помешать игроку построить прямую линию из 5 и более мячей)..
Рис. 21.7. Игроку начислено 600 очков.
10. По окончании игры (например, по окончании заданного времени) игрок выбирает в меню Очки команду “Показать средние” очки.
Появляется стандартная панель MessageBox.Show с выходной информацией (рис. 21.8):
Очки:
Время:
Среднее значение: (очков/сек).
11. По среднему значению очков в секунду определяется победитель в игре.
Рис. 21.8. Панель MessageBox.Show с выходной информацией.
12. Таким образом, после начала игры идёт отсчёт времени при помощи таймера.
Для каждого сеанса (попытки) игры одного или нескольких игроков задано определённое время, в данном примере, 60 секунд, по истечении которого звучит мелодия файла win.wav.
Игрок прекращает щёлкать мышью и смотрит на заработанные им очки.
13. Для начала новой попытки игрок снова щёлкает команду Новая в меню Игра.
14. Для закрытия игры следует в меню Игра выбрать команду Выход (или на форме щёлкнуть значок Close).
На основании этих правил можно сформулировать другие правила игры с использованием искусственного интеллекта, и любые правила ввести в справочную форму игры, которая появится после выбора команды Справка в меню Помощь по разработанной нами ранее (или в книгах с сайта ZharkovPress.ru) методике.
21.3. Создание проекта
Создаём проект по обычной схеме: в VS в панели New Project в окне Project types выбираем тип проекта Visual Basic, Windows, в окне Templates выделяем шаблон Windows Forms Application, в окне Name записываем имя проекта Line и щёлкаем OK. Создаётся проект, появляется форма Form1 в режиме проектирования (рис. 21.9). Оставляем по умолчанию или проектируем форму, как подробно описано в параграфе “Методика проектирования формы”. За маркер увеличиваем размеры формы таким образом, чтобы в панели Properties (для Form1) в свойстве Size были значения, например, 880; 630. Устанавливаем белый цвет Window для фона формы в свойстве BackColor.
Для задания режимов и управления игрой воспользуемся каким-либо элементом управления или компонентом. Как и выше, с панели инструментов Toolbox переносим на форму элемент управления MenuStrip и щёлкаем по нему (ниже формы в режиме проектирования). На форме Form1 появляются окна с надписью Type Here (Печатайте здесь), в которые записываем команды сначала на английском языке:
File, New Game, Save, Load, Exit;
Score, Show Score, Calculate Avg;
Help, Help, About.
Теперь в панели Properties (для каждой команды) в свойстве Text изменяем английские команды на соответствующие русские:
Игра, Новая, Сохранить, Загрузить, Выход;
Очки, Показать, Рассчитать средние;
Помощь, Справка, О программе (рис. 21.10 – 21.12).
Рис. 21.9. Форма Form1 в режиме проектирования.
Рис. 21.10. Команды меню Игра. Рис. 21.11. Команды меню Очки. Рис. 21.12. Меню Помощь.
С панели инструментов Toolbox переносим на форму первую надпись Label. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 430; 124, в свойстве Name изменяем имя на lblName, в свойстве Font увеличиваем размер шрифта до 28, в свойстве Text записываем “Имя:”.
Правее на этой же горизонтали размещаем вторую надпись Label. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 568; 124, в свойстве Name изменяем имя на lblNameShow, в свойстве Font увеличиваем размер шрифта до 28, в свойстве Text записываем “игрока”.
Ниже размещаем третью надпись Label. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 430; 212, в свойстве Name изменяем имя на lblScore, в свойстве Font увеличиваем размер шрифта до 28, в свойстве Text записываем “Очки: ” (с пробелами для вывода очков в эти пробелы).
Ниже размещаем четвёртую надпись Label. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 430; 302, в свойстве Name изменяем имя на lblTime, в свойстве Font увеличиваем размер шрифта до 28, в свойстве Text записываем “Время: ” (с пробелами для вывода секунд, минут и часов в эти пробелы).
Ниже размещаем пятую надпись Label. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 430; 392, в свойстве Name изменяем имя на lblBallPreview, в свойстве Font увеличиваем размер шрифта до 28, в свойстве Text записываем “Следующие мячи:”.
Ниже размещаем первый графический элемент управления PictureBox. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 453; 477, в свойстве Size устанавливаем размеры 52; 52, в свойстве Name изменяем имя на picBallPre1.
Правее размещаем второй элемент PictureBox. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 510; 477, в свойстве Size устанавливаем размеры 52; 52, в свойстве Name изменяем имя на picBallPre2.
Правее размещаем третий элемент PictureBox. В панели Properties (для этого элемента) в свойстве Location устанавливаем координаты верхнего левого угла элемента 568; 477, в свойстве Size устанавливаем размеры 52; 52, в свойстве Name изменяем имя на picBallPre3.
Чтобы программа периодически через Interval времени дополняла поле игры новыми разноцветными мячами, с панели инструментов Toolbox переносим на форму (точнее, ниже формы) первый таймер Timer. В панели Properties (для этого таймера) в свойстве Name записываем имя tmr1, в свойстве Enabled оставляем заданное по умолчанию значение False, т.к. мы включим этот таймер в программе в нужном месте при помощи строки (Timer1.Enabled = True). А в свойстве Interval вместо заданных по умолчанию 100 миллисекунд задаём, например, значение 150 миллисекунд.
Чтобы после начала игры на форме шел отсчёт времени (Time), на форму переносим второй таймер Timer. В панели Properties (для этого таймера) в свойстве Name записываем имя tmr2, в свойстве Enabled оставляем заданное по умолчанию значение False, а в свойстве Interval задаём 1000 миллисекунд (равные 1 секунде).
Если в игре применяются звуковые файлы, то их целесообразно разместить в одной папке с именем, например, Sounds. Для добавления в проект этой папки, в панели Solution Explorer выполняем правый щелчок по имени проекта, в контекстном меню выбираем Add, New Folder, в поле появившегося значка папки записываем имя папки и нажимаем клавишу Enter.
Добавляем в эту папку звуковые файлы drumpad-crash.wav и win.wav по стандартной схеме: выполняем правый щелчок по имени этой папки, в контекстном меню выбираем Add, Existing Item, в панели Add Existing Item в окне “Files of type” выбираем “All Files”, в центральном окне находим (например, в папке с загруженными из Интернета файлами) и с нажатой клавишей Ctrl выделяем имена файлов и щёлкаем кнопку Add. В панели Solution Explorer мы увидим эти файлы (рис. 21.13). В панели Properties свойства этих файлов оставляем заданными по умолчанию.
Рис. 21.13. Панель Solution Explorer. Рис. 21.14. Панель Properties.
Добавляем в проект графические файлы по стандартной схеме: в панели Solution Explorer выполняем правый щелчок по имени проекта, в контекстном меню выбираем Add, Existing Item (или Project, Add Existing Item), в панели Add Existing Item в окне “Files of type” выбираем “All Files”, в центральном окне находим (например, в папке с загруженными из Интернета файлами) и с нажатой клавишей Ctrl выделяем имена файлов и щёлкаем кнопку Add. В панели Solution Explorer мы увидим эти файлы (рис. 21.13). В панели Properties свойства этих файлов оставляем заданными по умолчанию (рис. 21.14).
Для ввода в проект новой формы (для таблицы с результатами игры) в меню Project выбираем Add Windows Form, в панели Add New Item оставляем заданные по умолчанию параметры и щёлкаем кнопку Add. В ответ VS выводит новую форму Form2 и добавляет в панель Solution Explorer новый пункт Form2.vb. Эта форма Form2, которая должна выводиться после выбора команды Показать в меню Очки, будет проектироваться программно согласно приведённому далее коду.
С панели инструментов Toolbox переносим на форму Form2 (точнее, ниже формы) первый таймер Timer. В панели Properties (для этого таймера) в свойстве Enabled оставляем заданное по умолчанию значение False, т.к. мы включим этот таймер в программе в нужном месте при помощи строки (Timer1.Enabled = True). А в свойстве Interval вместо заданных по умолчанию 100 миллисекунд задаём, например, значение 20 миллисекунд.
Аналогично переносим на форму Form2 второй таймер Timer. В панели Properties (для этого таймера) в свойстве Enabled оставляем заданное по умолчанию значение False, а в свойстве Interval задаём 40 миллисекунд.
Аналогично добавляем в проект третью форму Form3, которая будет выводиться после выбора команды Справка в меню Помощь. На форме Form3 размещаем какой-либо элемент управления, например, TextBox, в который записываем справочную информацию, например, правила игры по описанной ранее методике.
Аналогично добавляется и проектируется следующая форма, которая должна выводиться после выбора команды “О программе” в меню Помощь.
Свойства всех элементов управления можно стандартно изменять, как описано ранее.
21.4. Код программы
Открываем файл Form1.vb (например, так: File, Open, File) и вверху записываем директиву для подключения требуемого пространства имен:
Imports System.IO 'Для класса StreamWriter.
Напомним, что эту строку можно и не записывать, но тогда нам придётся перед каждым классом записывать это пространство имён.
Теперь в классе Form1 нашего проекта записываем следующие переменные и методы.
Листинг 21.1. Переменные и методы.
Const intBaseX As Integer = 10
Const intBaseY As Integer = 120
Dim Rand As New Random
'***
Dim playerName As String
Dim playerScore As Double
Dim playerTime As Integer
Dim DDScore As New DPaint
Dim DDTime As New DPaint
'***
Dim intFlag As Integer = -1
Dim flagMadeNew = 0
Dim posMoveTo As Integer
Dim MPBoxes(80) As MotionPic
Dim ThreeBI(2) As Integer
Dim ThreeBP(2) As Integer
Dim prePic(2) As PictureBox
Private Sub InitBoard(ByVal plName As String, _
ByVal plScore As Double, ByVal plTime As Integer)
playerName = plName
playerScore = plScore
playerTime = plTime
If flagMadeNew = 0 Then
Dim i As Integer
Dim intX = intBaseX + 2
Dim intY = intBaseY + 2
For i = 0 To 80
Dim MP As New MotionPic(New Size(36, 36), _
New Point(intX, intY))
MP.SizeMode = PictureBoxSizeMode.StretchImage
intX += 45
If (i + 1) Mod 9 = 0 Then
intY += 45
intX = intBaseX + 2
End If
AddHandler MP.Click, AddressOf Ball_Click
MPBoxes(i) = MP
Next
Me.Controls.AddRange(MPBoxes)
DDScore.width = lblScore.Height / 2 – 6
DDScore.thick = DDScore.width / 4
DDScore.position = New Point(lblScore.Width – _
(DDScore.width + 2) * 9, lblScore.Height / 2)
DDTime.width = lblTime.Height / 2 – 6
DDTime.thick = DDTime.width / 4
DDTime.position = New Point(lblTime.Width – _
(DDTime.width + 2) * 9, lblTime.Height / 2)
AddHandler lblScore.Paint, AddressOf LabelScore_Paint
lblScore.Refresh()
AddHandler lblTime.Paint, AddressOf LabelTime_Paint
lblTime.Refresh()
For i = 0 To 2
prePic(i) = New PictureBox
prePic(i).SizeMode = PictureBoxSizeMode.StretchImage
prePic(i).Size = New Size(16, 16)
prePic(i).Visible = False
Me.Controls.Add(prePic(i))
AddHandler prePic(i).Click, AddressOf PrePic_Click
prePic(i).BringToFront()
Next
Else
ResetBoard()
End If
lblNameShow.Text = playerName
If playerName.Length > 8 Then
lblNameShow.Text += " "
tmr1.Enabled = True
End If
tmr2.Enabled = True
DDScore.number = plScore
lblScore.Refresh()
DDTime.number = plTime
lblTime.Refresh()
PreShow()
End Sub
Private Sub FindSol(ByVal i As Integer)
If MPBoxes(i).Tag <> "" Or MPBoxes(i).Tag = "Here" Then
Return
Else
MPBoxes(i).Tag = "Here"
End If
Select Case TestABox(i)
Case 1
FindSol(1)
FindSol(9)
Case 2
FindSol(7)
FindSol(17)
Case 3
FindSol(71)
FindSol(79)
Case 4
FindSol(63)
FindSol(73)
Case 5
FindSol(i + 1)
FindSol(i + 9)
FindSol(i – 1)
Case 6
FindSol(i – 9)
FindSol(i – 1)
FindSol(i + 9)
Case 7
FindSol(i – 1)
FindSol(i – 9)
FindSol(i + 1)
Case 8
FindSol(i – 9)
FindSol(i + 1)
FindSol(i + 9)
Case Else
FindSol(i – 9)
FindSol(i + 9)
FindSol(i + 1)
FindSol(i – 1)
End Select
End Sub
Private Sub ResetAllTag()
For Each Pic As MotionPic In MPBoxes
If Pic.Tag = "Here" Then
Pic.Tag = ""
End If
Next
End Sub
Private Function TestABox(ByVal val As Integer)
Select Case val
Case 0 : Return 1
Case 8 : Return 2
Case 80 : Return 3
Case 72 : Return 4
Case 1 To 7 : Return 5
Case 73 To 79 : Return 7
Case 17, 26, 35, 44, 53, 62, 71 : Return 6
Case 9, 18, 27, 36, 45, 54, 63 : Return 8
Case Else : Return 0
End Select
End Function
'Serious trouble happened – think more
Private Function GiveThreeBalls() As Boolean
If ThreeBI(1) = -1 Then 'Review for Game over
Return False
Else
For i As Integer = 0 To 2
If ThreeBI(i) = -1 Then
Exit For
Else
If MPBoxes(ThreeBP(i)).MPState = BallState. _
NO_BALL And ThreeBP(i) <> posMoveTo Then
MPBoxes(ThreeBP(i)).Init(ThreeBI(i))
CalWin(ThreeBP(i))
End If
End If
Next
End If
RandomThreeBalls()
PreShow()
Return True
End Function
Private Function IsFullBoard() As Boolean
Dim i As Integer
For Each Pic As PictureBox In MPBoxes
If MPBoxes(i).MPState <> BallState.NO_BALL Then
i += 1
End If
Next
If i = 81 Then
Return True
Else
Return False
End If
End Function
Private Sub RandomThreeBalls()
Dim ArrL As New ArrayList
Dim i As Integer
Dim pos As Integer
Dim ind As Integer
For i = 0 To 80
If MPBoxes(i).MPState = BallState.NO_BALL Or _
MPBoxes(i).MPState = BallState.DESTROYING_BALL Then
ArrL.Add(i)
End If
Next
For i = 0 To IIf(ArrL.Count > 2, 2, ArrL.Count – 1)
pos = Rand.Next(0, ArrL.Count)
pos = CInt(ArrL(pos))
ArrL.Remove(pos)
ThreeBP(i) = pos
ind = Rand.Next(0, 12)
ind = (ind \ 2) * 2
ThreeBI(i) = ind
Next
For j As Integer = i To 2
ThreeBI(j) = -1
ThreeBP(j) = -1
Next
End Sub
'#Region "Check for Calculate Score"
Private Function CheckHor(ByVal pos As Integer) As Integer
Dim type As Integer = MPBoxes(pos).MPIndex
Dim i As Integer = (pos \ 9) * 9
Dim count As Integer
Dim startpos As Integer = i
Dim endpos As Integer = i
While i < (pos \ 9) * 9 + 9
If MPBoxes(i).MPIndex = type Then
endpos += 1
count = endpos – startpos
Else
If count > 4 Then
While MPBoxes(pos).MPState = _
BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j).Destroy()
Next
Return count
End If
If i >= (pos \ 9) * 9 + 5 Then
Return count
End If
startpos = i + 1
endpos = i + 1
End If
i += 1
End While
If count > 4 Then
While MPBoxes(pos).MPState = BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j).Destroy()
Next
Return count
End If
End Function
Private Function CheckVer(ByVal pos As Integer) As Integer
Dim type As Integer = MPBoxes(pos).MPIndex
Dim i As Integer = pos Mod 9
Dim count As Integer
Dim startpos As Integer = i
Dim endpos As Integer = i
While i < (pos Mod 9) + 73
If MPBoxes(i).MPIndex = type Then
endpos += 9
count = (endpos – startpos) / 9
Else
If count > 4 Then
While MPBoxes(pos).MPState = _
BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 9).Destroy()
Next
Return count
End If
If i >= (pos Mod 9) + 36 Then
Return count
End If
startpos = i + 9
endpos = i + 9
End If
i += 9
End While
If count > 4 Then
While MPBoxes(pos).MPState = BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 9).Destroy()
Next
Return count
End If
End Function
Private Function CheckLR(ByVal pos As Integer) As Integer
If pos = 5 Or pos = 6 Or pos = 7 Or pos = 8 Or pos = 15 _
Or pos = 16 Or pos = 17 _
Or pos = 25 Or pos = 26 Or pos = 35 Or pos = 45 _
Or pos = 54 Or pos = 55 _
Or pos = 63 Or pos = 64 Or pos = 65 Or pos = 72 _
Or pos = 73 Or pos = 74 Or pos = 75 Then
Return 0
End If
Dim type As Integer = MPBoxes(pos).MPIndex
Dim i As Integer = pos Mod 10
i = IIf(i = 8, 18, IIf(i = 7, 27, IIf(i = 6, 36, i)))
Dim count As Integer
Dim startpos As Integer = i
Dim endpos As Integer = i
Dim tempi As Integer = i + 1
Dim temp As Integer
If i < 9 Then
temp = 9 – i
Else
temp = 9 – (i \ 9)
End If
While i < tempi + (temp – 1) * 10
If MPBoxes(i).MPIndex = type Then
endpos += 10
count = (endpos – startpos) \ 10
Else
If count > 4 Then
While MPBoxes(pos).MPState = _
BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 10).Destroy()
Next
Return count
End If
If i >= pos + 40 Then
Return count
End If
startpos = i + 10
endpos = i + 10
End If
i += 10
End While
If count > 4 Then
While MPBoxes(pos).MPState = BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 10).Destroy()
Next
Return count
End If
End Function
Private Function CheckRL(ByVal pos As Integer) As Integer
If pos = 0 Or pos = 1 Or pos = 2 Or pos = 3 Or pos = 9 _
Or pos = 10 Or pos = 11 _
Or pos = 18 Or pos = 19 Or pos = 27 Or pos = 53 _
Or pos = 61 Or pos = 62 _
Or pos = 69 Or pos = 70 Or pos = 71 Or pos = 77 _
Or pos = 78 Or pos = 79 Or pos = 80 Then
Return 0
End If
Dim type As Integer = MPBoxes(pos).MPIndex
Dim i As Integer = pos Mod 8
If i = 0 Then
i = 8
ElseIf i < 4 Then
i = (i + 1) * 8 + i
ElseIf pos \ 8 >= 5 Then
i = 45
End If
Dim count As Integer
Dim startpos As Integer = i
Dim endpos As Integer = i
Dim tempi As Integer = i + 1
Dim temp As Integer
If i < 9 Then
temp = i + 1
Else
temp = 9 – (i \ 8)
End If
While i < tempi + temp * 8
If MPBoxes(i).MPIndex = type Then
endpos += 8
count = (endpos – startpos) \ 8
Else
If count > 4 Then
While MPBoxes(pos).MPState = _
BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 8).Destroy()
Next
Return count
End If
If i >= pos + 32 Then
Return count
End If
startpos = i + 8
endpos = i + 8
End If
i += 8
End While
If count > 4 Then
While MPBoxes(pos).MPState = BallState.ZOOMING_BALL
Application.DoEvents()
End While
For j As Integer = 0 To count – 1
MPBoxes(startpos + j * 8).Destroy()
Next
Return count
End If
End Function
Private Function CalWin(ByVal pos As Integer) As Integer
Dim point As Integer = CheckHor(pos)
If point < 4 Then
point = CheckVer(pos)
End If
If point < 4 Then
point = CheckLR(pos)
End If
If point < 4 Then
point = CheckRL(pos)
End If
If point > 4 Then
Dim dpoint As Double = point * 100 + (dpoint \ 6) * 100
Dim n As Double = DDScore.number + dpoint
For i As Double = DDScore.number To n Step 10
DDScore.number = i
lblScore.Refresh()
Next
DDScore.number = n
Return point
Else
Return 0
End If
End Function
'#Region "Ball Event And Paint Board"
Private Sub Ball_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
CType(sender, MotionPic).Jump()
If CType(sender, MotionPic).MPState <> _
BallState.NO_BALL Then
If intFlag <> -1 Then
MPBoxes(intFlag).Jump()
End If
'intFlag = MPBoxes.IndexOf(MPBoxes, sender)
'Исправляем предупреждение:
intFlag = Array.IndexOf(MPBoxes, sender)