Полная версия
Справочник Жаркова по проектированию и программированию искусственного интеллекта. Том 6: Программирование на Visual Basic искусственного интеллекта. Продолжение 2
graphics.Clear(backColor)
Dim row As Integer
Dim column As Integer
Dim theBlock As Block
For row = 0 To matrix.GetLength(0) – 1
For column = 0 To matrix.GetLength(1) – 1
theBlock = matrix(row, column)
If Not theBlock Is Nothing Then
Dim pointA As New Point( _
column * Block.BlockSize, _
row * Block.BlockSize)
matrix(row, column).Draw(graphics, pointA)
End If
Next
Next
End Sub
'''
''' This method responds to a click event in the UI.
'''
'''
'''
'''
Public Function Click(ByVal point As Point) As Integer
' Figure out row and column.
Dim total As Integer
Dim transPt As Point = PointTranslator.TranslateToTL(point)
Dim selectedRow As Integer = transPt.Y \ Block.BlockSize
Dim selectedColumn As Integer = transPt.X \ Block.BlockSize
Dim selectedBlock As Block = matrix(selectedRow, _
selectedColumn)
If Not selectedBlock Is Nothing Then
selectedBlock.MarkedForDeletion = True
' Determine if any of the neighboring blocks are
' the same color.
FindSameColorNeighbors(selectedRow, selectedColumn)
' Determine how many blocks would be eliminated.
total = Me.CalculateScore()
If total > 1 Then
Me.CollapseBlocks()
Else
Me.ClearMarkedForDeletion()
End If
End If
Return total
End Function
Private Sub ClearMarkedForDeletion()
Dim row As Integer
Dim column As Integer
For column = matrix.GetLength(1) – 1 To 0 Step -1
' If column is completely empty, then move everthing
' down one.
For row = 0 To matrix.GetLength(0) – 1
If Not matrix(row, column) Is Nothing Then
matrix(row, column).MarkedForDeletion = False
End If
Next
Next
End Sub
'''
''' Find out how many blocks will be eliminated.
'''
'''
'''
Private Function CalculateScore() As Integer
Dim row As Integer
Dim column As Integer
Dim total As Integer = 0
For column = matrix.GetLength(1) – 1 To 0 Step -1
' If column is completely empty, then move everthing
' down one.
For row = 0 To matrix.GetLength(0) – 1
If Not matrix(row, column) Is Nothing Then
If matrix(row, column).MarkedForDeletion Then
total += 1
End If
End If
Next
Next
Return total
End Function
'''
''' After the blocks are removed from the columns, there may be
''' columns that are empty. Move columns from right to left to
''' fill in the empty columns.
'''
'''
Public Sub CollapseColumns()
Dim row As Integer
Dim column As Integer
For column = matrix.GetLength(1) – 1 To 0 Step -1
' If column is completely empty, then all the columns
' over one.
Dim noBlocks As Boolean = True
For row = 0 To matrix.GetLength(0) – 1
If Not matrix(row, column) Is Nothing Then
noBlocks = False
End If
Next
If noBlocks Then
Dim newcol As Integer
For newcol = column To matrix.GetLength(1) – 2
For row = 0 To matrix.GetLength(0) – 1
matrix(row, newcol) = matrix(row, newcol + 1)
Next
Next
newcol = matrix.GetLength(1) – 1
For row = 0 To matrix.GetLength(0) – 1
matrix(row, newcol) = Nothing
Next
End If
Next
End Sub
'''
''' Remove all the blocks from the grid.
'''
'''
Public Sub CollapseBlocks()
Dim theBlock As Block
Dim column As Integer
Dim row As Integer
Dim aRow As Integer
' First remove the blocks from each column.
For column = 0 To matrix.GetLength(1) – 1
For row = matrix.GetLength(0) – 1 To 0 Step -1
theBlock = matrix(row, column)
If (Not theBlock Is Nothing) Then
If theBlock.MarkedForDeletion Then
For aRow = row To matrix.GetLength(0) – 2
matrix(aRow, column) = _
matrix(aRow + 1, column)
Next
matrix(matrix.GetLength(0) – 1, _
column) = Nothing
End If
End If
Next
Next
' Reset the MarkedForDeletion flags.
For row = 0 To matrix.GetLength(0) – 1
For column = 0 To matrix.GetLength(1) – 1
theBlock = matrix(row, column)
If Not theBlock Is Nothing Then
theBlock.MarkedForDeletion = False
End If
Next
Next
' Remove any columns that are now empty.
CollapseColumns()
End Sub
'''
''' Provides access into the grid.
'''
'''
'''
'''
'''
Default Public Property Item(ByVal row As Integer, _
ByVal column As Integer) As Block
Get
Return matrix(row, column)
End Get
Set(ByVal Value As Block)
matrix(row, column) = Value
End Set
End Property
Private blocksToExamine As ArrayList
'''
''' Set MarkedForDeletion to True for each neighboring block
''' of the same color.
'''
'''
'''
'''
Private Sub FindSameColorNeighbors(ByVal row As Integer, _
ByVal column As Integer)
Dim color As Color = matrix(row, column).Color
blocksToExamine = New ArrayList
blocksToExamine.Add(New Point(row, column))
matrix(row, column).MarkedForDeletion = True
' Each time you find a neighbor, mark it for deletion, and
' add it to the list of blocks to look for neighbors.
' After you
' examine it, remove it from the list. Keep doing this
' until there are no more blocks to look at.
While blocksToExamine.Count > 0
FindNeighbors()
End While
End Sub
'''
''' Look to the blocks on each side.
'''
'''
Private Sub FindNeighbors()
' Take the first block out of the arraylist and examine it.
Dim location As Point = CType(blocksToExamine(0), Point)
Dim currentBlock As Block = matrix(location.X, location.Y)
Dim row As Integer = location.X
Dim column As Integer = location.Y
blocksToExamine.RemoveAt(0)
Dim nextRow As Integer
Dim nextCol As Integer
Dim selected As Block
' look up
If row < matrix.GetLength(0) – 1 Then
nextRow = row + 1
selected = matrix(nextRow, column)
ExamineNeighbor(selected, nextRow, column, _
currentBlock.Color)
End If
' look down
If row > 0 Then
nextRow = row – 1
selected = matrix(nextRow, column)
ExamineNeighbor(selected, nextRow, column, _
currentBlock.Color)
End If
' look left
If column > 0 Then
nextCol = column – 1
selected = matrix(row, nextCol)
ExamineNeighbor(selected, row, nextCol, _
currentBlock.Color)
End If
' look right
If column < matrix.GetLength(1) – 1 Then
nextCol = column + 1
selected = matrix(row, nextCol)
ExamineNeighbor(selected, row, nextCol, _
currentBlock.Color)
End If
End Sub
'''
''' If the neighbor is the same color, add it to the blocks
''' to examine.
'''
'''
'''
'''
'''
'''
Private Sub ExamineNeighbor(ByVal selected As Block, _
ByVal row As Integer, ByVal column As Integer, _
ByVal color As Color)
If Not selected Is Nothing Then
If selected.Color.Equals(color) Then
If Not selected.MarkedForDeletion Then
selected.MarkedForDeletion = True
blocksToExamine.Add(New Point(row, column))
End If
End If
End If
End Sub
End Class
По второму варианту, в панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item, в панели Add New Item выделяем шаблон Code File, в окне Name записываем имя HighScore.vb и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем код со следующего листинга.
Листинг 20.18. Новый файл.
'''
''' Represents one high score.
'''
'''
Public Class HighScore
Implements IComparable
Public nameValue As String
Public scoreValue As Integer
Public Property Name() As String
Get
Return nameValue
End Get
Set(ByVal Value As String)
nameValue = Value
End Set
End Property
Public Property Score() As Integer
Get
Return scoreValue
End Get
Set(ByVal Value As Integer)
scoreValue = Value
End Set
End Property
Public Overrides Function ToString() As String
Return Name & ":" & Score
End Function
Public Sub New(ByVal saved As String)
Name = saved.Split(":".ToCharArray)(0)
Score = CInt(saved.Split(":".ToCharArray)(1))
End Sub
Public Function CompareTo(ByVal obj As Object) As Integer Implements System.IComparable.CompareTo
Dim other As HighScore
other = CType(obj, HighScore)
Return Me.Score – other.Score
End Function
End Class
По второму варианту, в панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item, в панели Add New Item выделяем шаблон Code File, в окне Name записываем имя HighScores.vb и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем код со следующего листинга.
Листинг 20.19. Новый файл.
Imports Microsoft.Win32
'''
''' Reads and writes the top three high scores to the registry.
'''
'''
Public Class HighScores
'''
''' Read scores from the registry.
'''
'''
'''
Public Shared Function GetHighScores() As HighScore()
Dim tops(2) As HighScore
Dim scoreKey As RegistryKey = Registry.CurrentUser. _
CreateSubKey("Software\VBSamples\Collapse\HighScores")
For index As Integer = 0 To 2
Dim key As String = "place" & index.ToString
Dim score As New HighScore(CStr(scoreKey.GetValue(key)))
tops(index) = score
Next
scoreKey.Close()
Return tops
End Function
'''
''' Update and write the high scores.
'''
'''
'''
Public Shared Sub UpdateScores(ByVal score As Integer)
Dim tops(3) As HighScore
Dim scoreKey As RegistryKey = Registry.CurrentUser. _
CreateSubKey("Software\VBSamples\Collapse\HighScores")
tops(0) = New HighScore(scoreKey.GetValue("Place0").ToString)
tops(1) = New HighScore(scoreKey.GetValue("Place1").ToString)
tops(2) = New HighScore(scoreKey.GetValue("Place2").ToString)
If score > tops(2).Score Then
Dim name As String = InputBox("New high score of " & _
score & " for:")
tops(3) = New HighScore(" :0")
tops(3).Name = name
tops(3).Score = score
Array.Sort(tops)
Array.Reverse(tops)
scoreKey.SetValue("Place0", tops(0).ToString)
scoreKey.SetValue("Place1", tops(1).ToString)
scoreKey.SetValue("Place2", tops(2).ToString)
End If
scoreKey.Close()
End Sub
'''
''' Set up the entries for new scores.
'''
'''
Shared Sub SetUpHighScores()
Dim scoreKey As RegistryKey = Registry.CurrentUser. _
CreateSubKey("Software\VBSamples\Collapse\HighScores")
If scoreKey.GetValue("Place1") Is Nothing Then
scoreKey.SetValue("Place0", " :0")
scoreKey.SetValue("Place1", " :0")
scoreKey.SetValue("Place2", " :0")
End If
scoreKey.Close()
End Sub
'''
''' Reset scores.
'''
'''
Shared Sub ResetScores()
Dim scoreKey As RegistryKey = Registry.CurrentUser. _
CreateSubKey("Software\VBSamples\Collapse\HighScores")
scoreKey.SetValue("Place0", " :0")
scoreKey.SetValue("Place1", " :0")
scoreKey.SetValue("Place2", " :0")
scoreKey.Close()
End Sub
End Class
По второму варианту, в панели Solution Explorer выполняем правый щелчок по имени проекта и в контекстном меню выбираем Add, New Item, в панели Add New Item выделяем шаблон Code File, в окне Name записываем имя PointTranslator.vb и щёлкаем кнопку Add. В проект (и в панель Solution Explorer) добавляется этот файл, открывается пустое окно редактирования кода, в которое записываем код со следующего листинга.
Листинг 20.20. Новый файл.
'''
''' Form coordinates have the top, left as (0,0). For the game grid,
''' it is easier to have the bottom left of the grid as (0,0). This
''' translates the points.
'''
'''
Public Class PointTranslator
Private Shared graphicsValue As Graphics
Private Shared height As Integer
Public Shared Property Graphics() As Graphics
Get
Return graphicsValue
End Get
Set(ByVal Value As Graphics)
graphicsValue = Value
height = CInt(graphicsValue.VisibleClipBounds.Height())
End Set
End Property
' Translates an (X,Y) point from the top left to
' an (X, Y) point from the bottom left.
Public Shared Function TranslateToBL(ByVal topleft As Point) _
As Point
Dim newPoint As Point
newPoint.X = topleft.X
newPoint.Y = height – topleft.Y
Return newPoint
End Function
Public Shared Function TranslateToTL(ByVal bottomleft As Point) _
As Point
Dim newPoint As Point
newPoint.X = bottomleft.X
newPoint.Y = height – bottomleft.Y
Return newPoint
End Function
End Class
После этих добавлений ( Block.vb, Grid.vb, HighScore.vb, HighScores.vb, PointTranslator.vb) в панели Solution Explorer должны быть файлы, показанные выше. Дважды щёлкая по имени файла, любой файл можно открыть, изучить и редактировать.
Теперь в наш проект добавляем переменные и методы, связанные с формой Form2 для вывода результатов игры.
Открываем файл Form2.vb (например, по схеме: File, Open, File) и в классе Form2 нашего проекта записываем следующее свойство.
Листинг 20.21. Свойство.
Public Property SoundOn() As Boolean
Get
Return Me.isSoundOn.Checked
End Get
Set(ByVal Value As Boolean)
Me.isSoundOn.Checked = Value
End Set
End Property
В панели Properties (для Form2) на вкладке Events дважды щёлкаем по имени события Load (Загрузка). Появившийся шаблон метода Form2_Load после записи нашего кода принимает следующий вид.
Листинг 20.22. Метод для загрузки результатов игры.
Private Sub Form2_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Me.DataGridView1.DataSource = HighScores.GetHighScores()
End Sub
На форме Form2 дважды щёлкаем по кнопке Button. Появляется шаблон метода (для очистки таблицы результатов), который после записи нашего кода принимает следующий вид.
Листинг 20.23. Метод-обработчик щелчка кнопки.
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
HighScores.ResetScores()
Me.DataGridView1.DataSource = HighScores.GetHighScores()
End Sub
В случае необходимости, методика добавления в проект звукового сигнала Beep (по-русски: Бип) описана ранее.
20.5. Запуск игры
Строим и запускаем программу на выполнение обычным образом:
Build, Build Selection; Debug, Start Without Debugging.
В ответ Visual Studio выводит показанную выше форму, на которой искусственный интеллект произвольным образом (при помощи генератора случайных чисел – г.с.ч. класса Random) строит разноцветную палитру строк и столбцов из плоских геометрических фигур, в данном примере, из разноцветных кругов.
Затем игрок при помощи указателя мыши быстро выбирает тот цвет, который охватывает как можно большее количество кругов (площадь палитры) и нажимает кнопку мыши (чтобы выбить эти круги из палитры).
Круги одинакового цвета, соединённые между собой по горизонтали (по строке) и вертикали (по столбцу) искусственный интеллект удаляет, а игроку начисляются по 10 очков за каждый выбитый круг.
По такой схеме игрок быстро щёлкает мышью по кругам, стараясь за отведённое время выбить как можно больше кругов и соответственно очков (согласно приведённым выше правилам).
По методике данной главы можно разрабатывать самые разнообразные игры по выбиванию фигур одного цвета из разноцветной палитры разнообразных фигур с использованием искусственного интеллекта.
Глава 21. Методика программирования искусственного интеллекта в игре по сборке прямых из 5 и более объектов одинакового цвета
21.1. Общие сведения
Опишем методику проектирования и программирования типичной и широко распространённой игры игрока с искусственным интеллектом в виде компьютера, когда на форме в отдельных квадратах сетки, например, 9 x 9 сначала произвольным образом (при помощи генератора случайных чисел – г.с.ч. класса Random) появляется определённое количество, в данной игре 3 разноцветных объекта, например, 3 больших мяча, которые игрок может перемещать при помощи мыши, и 3 маленьких разноцветных мяча, которые размещает искусственный интеллект, чтобы помешать игроку построить прямую линию из мячей (так как в клетку с маленьким мячом большой мяч уже нельзя разместить).
Затем игрок при помощи указателя мыши быстро выбирает (щёлкает) тот большой мяч, который он желает переместить к другому мячу (или мячам) того же цвета. Мяч, по которому игрок щёлкнул, начинает пульсировать. Игрок второй раз щёлкает на той пустой клетке, в которую должен переместиться мяч, чтобы образовать прямую из мячей одинакового цвета. Мяч перемещается в эту клетку. После этого искусственный интеллект размещает в пустующие клетки следующие 3 больших мяча произвольных цветов.
Аналогично игрок снова щёлкает по выбранному им мячу и по той клетке, в которую мяч перемещается. После этого искусственный интеллект снова размещает в пустующие клетки следующие 3 больших мяча произвольных цветов.
Как только игрок соберёт горизонтальную, вертикальную или диагональную прямую линию из 5 и более мячей одинакового цвета, игроку начисляются очки (по 100 очков за каждый собранный в линию мяч), а линия из собранных мячей исчезает, освобождая клетки для новых мячей.
По такой схеме игрок быстро щёлкает мышью по мячам, стараясь за отведённое время собрать как можно больше линий из мячей одинакового цвета. Искусственный интеллект же периодически (после каждого второго щелчка игрока и следующего за ним перемещения пульсирующего мяча в новую клетку) дополняет клетки 3 новыми разноцветными мячами (произвольным образом).
Данную игру мы будем разрабатывать, следуя игре Line (проект lines_vbnet.zip в заархивированном виде) из Интернета, которая разработана на устаревшей версии Visual Studio. Поэтому автор данной книги разработал эту игру на современной версии Visual Studio, исправил ошибки и дополнил её недостающими для типичной игры элементами, например, счётчиком секунд и мелодиями начала и окончания времени игры.
В панели Solution Explorer (нашего будущего проекта) дважды щёлкаем по имени графического файла BlackBall.png для большого мяча чёрного цвета. Появляется собственный графический редактор Visual Studio с изображением этого мяча, причём инструментами этого редактора можно редактировать это изображение (рис. 21.1) применительно к нашим задачам. Видно, что за счёт раскраски круга различными оттенками соответствующего цвета (в данном случае, чёрного цвета) создаётся впечатление световых бликов, и плоский чёрный круг нами воспринимается как объёмный резиновый мяч. Аналогично можно увидеть графические файлы больших и маленьких мячей всех цветов в данном проекте.
В начале игры звучит мелодия:
My.Computer.Audio.Play("..\..\Sounds\drumpad-crash.wav")
Чтобы можно было ограничить игру по времени при помощи определённой мелодии, используем такой код:
'Счётчик секунд, который обнуляем в начале каждой игры
'в методе NewGame:
Dim secondCounter As Integer
'Время, через которое звучит мелодия
'возможного окончания игры:
Dim EndGameTime As Integer = 60
Private Sub tmr2_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles tmr2.Tick
DDTime.number += 1
lblTime.Refresh()
'Счётчик секунд:
secondCounter = secondCounter + 1
'Мелодия окончания игры:
If secondCounter = EndGameTime Then
My.Computer.Audio.Play("..\..\Sounds\win.wav", _
AudioPlayMode.Background)
End If
End Sub
В этом коде мелодия звучит через 60 секунд. Можно задать любое время, по окончании которого сеанс игры можно прекратить, записать набранные очки и начать новый сеанс игры или допустить к игре следующего игрока.
Рис. 21.1. Графический редактор Visual Studio с изображением мяча.
21.2. Правила игры
1. После запуска данной игры (игрока с компьютером) на мониторе появляется основная форма (рис. 21.2).
2. Для начала игры в меню Игра выбираем команду Новая.
Появляется стандартная (нам не нужно её проектировать) форма InputBox для записи имени игрока (рис. 21.3). Записываем (русскими или английскими буквами) имя и щёлкаем кнопку OK (или нажимаем клавишу Enter, поскольку, как мы видим, эта клавиша выделена по умолчанию).
Рис. 21.2. Исходная форма.
Рис. 21.3. Форма для записи имени игрока.
3. На форме в отдельных квадратах сетки 9 x 9 произвольным образом (при помощи г.с.ч. класса Random) появляются 3 разноцветных больших мяча, которые игрок может перемещать при помощи мыши, и 3 маленьких разноцветных мяча, которые размещает искусственный интеллект, чтобы помешать игроку построить прямую линию из мячей (так как в клетку с маленьким мячом большой мяч уже нельзя разместить).
Звучит мелодия начала игры, и пошёл отсчёт времени игры в секундах (рис. 21.4).
4. Если из трёх больших мячей есть мячи одинакового цвета, то игрок при помощи указателя мыши быстро выбирает (щёлкает) тот мяч, который он желает переместить к другому мячу (или мячам) того же цвета.
Если из трёх больших мячей нет мячей одинакового цвета, то игрок при помощи указателя мыши быстро выбирает (щёлкает) тот мяч, который он желает переместить в другой (более удобный, по его мнению) квадрат сетки.
Рис. 21.4. 3 больших и 3 маленьких мяча, отсчёт времени игры в секундах.
Мяч, по которому игрок щёлкнул, начинает пульсировать (и игрок видит, какой же мяч он щёлкнул).
Игрок может передумать и щёлкнуть другой мяч. После этого предыдущий мяч перестаёт пульсировать, и начинает пульсировать тот мяч, который игрок щёлкнул последним.
Ниже надписи “Следующие мячи:” игрок видит 3 мяча, которые появятся на поле после его щелчка, и опытный игрок учитывает эти мячи в своих прогнозах.
Теперь игрок щёлкает на тому пустому квадрату, в который должен переместиться мяч, чтобы образовать прямую линию из мячей одинакового цвета.
Мяч перемещается в эту клетку.
Отметим, что игрок может разместить мяч и не вплотную к мячу того же цвета, а через один или несколько пустых квадратов, чтобы прогнозировать сборку как можно более длинной линии из мячей одинакового цвета (так как на место уже имеющихся в сетке мячей компьютер с искусственным интеллектом не может размещать другие мячи). Однако игрок должен помнить, что компьютер со своим искусственным интеллектом (для данной игры) может “разгадать” замысел игрока и разместить свой маленький мяч в пустой квадрат между большими мячами игрока (тем самым, разорвав сплошную линию из мячей игрока).